<commit_before>"use babel";

let {$, ScrollView} = require('atom-space-pen-views');
let {Point, TextEditor} = require('atom');
let fs = require('fs-plus');
let path = require('path');
let _ = require('underscore-plus');
let {File, Disposable, CompositeDisposable} = require('atom');
let {Function} = require('loophole');
global.Function = Function;

global.PDFJS = {workerSrc: "temp", cMapUrl:"temp", cMapPacked:true};
require('./../node_modules/pdfjs-dist/build/pdf.js');
PDFJS.workerSrc = "file://" + path.resolve(__dirname, "../node_modules/pdfjs-dist/build/pdf.worker.js");
PDFJS.cMapUrl = "file://" + path.resolve(__dirname, "../node_modules/pdfjs-dist/cmaps")+"/";

let {exec, execFile} = require('child_process');

export default class PdfEditorView extends ScrollView {
  static content() {
    this.div({class: 'pdf-view', tabindex: -1}, () => {
      this.div({outlet: 'container'});
    });
  }

  constructor(filePath) {
    super();

    this.currentScale = 1.5;
    this.defaultScale = 1.5;
    this.scaleFactor = 10.0;
    this.fitToWidth = atom.config.get('pdf-view.fitToWidth')

    this.filePath = filePath;
    this.file = new File(this.filePath);
    this.canvases = [];

    this.updatePdf();

    this.currentPageNumber = 0;
    this.totalPageNumber = 0;
    this.centersBetweenPages = [];
    this.pageHeights = [];
    this.scrollTopBeforeUpdate = 0;
    this.scrollLeftBeforeUpdate = 0;
    this.updating = false;

    let disposables = new CompositeDisposable();

    let onFileChangeCallback = _.debounce(() => {
      if (this.updating) {
        this.fileChanged = true;
      } else {
        this.updatePdf();
      }
    }, 100);

    disposables.add(this.file.onDidChange(onFileChangeCallback));

    let moveLeftCallback = (() => this.scrollLeft(this.scrollLeft() - $(window).width() / 20));
    let moveRightCallback = (() => this.scrollRight(this.scrollRight() + $(window).width() / 20));
    let scrollCallback = (() => this.onScroll());
    let resizeHandler = (() => this.setCurrentPageNumber());

    let elem = this;

    atom.commands.add('.pdf-view', {
      'core:move-left': moveLeftCallback,
      'core:move-right': moveRightCallback
    });

    elem.on('scroll', scrollCallback);
    disposables.add(new Disposable(() => $(window).off('scroll', scrollCallback)));

    $(window).on('resize', resizeHandler);
    disposables.add(new Disposable(() => $(window).off('resize', resizeHandler)));

    atom.commands.add('atom-workspace', {
      'pdf-view:zoom-in': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.zoomIn();
        }
      },
      'pdf-view:zoom-out': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.zoomOut();
        }
      },
      'pdf-view:reset-zoom': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.resetZoom();
        }
      }
    });

    this.dragging = null;

    this.onMouseMove = (e) => {
      if (this.dragging) {
        this.simpleClick = false;

        this.scrollTop(this.dragging.scrollTop - (e.screenY - this.dragging.y));
        this.scrollLeft(this.dragging.scrollLeft - (e.screenX - this.dragging.x));
        e.preventDefault();
      }
    };

    this.onMouseUp = (e) => {
      this.dragging = null;
      $(document).unbind('mousemove', this.onMouseMove);
      $(document).unbind('mouseup', this.onMouseUp);
      e.preventDefault();
    };

    this.on('mousedown', (e) => {
      this.simpleClick = true;
      atom.workspace.paneForItem(this).activate();
      this.dragging = {x: e.screenX, y: e.screenY, scrollTop: this.scrollTop(), scrollLeft: this.scrollLeft()};
      $(document).on('mousemove', this.onMouseMove);
      $(document).on('mouseup', this.onMouseUp);
      e.preventDefault();
    });

    this.on('mousewheel', (e) => {
      if (e.ctrlKey) {
        e.preventDefault();
        if (e.originalEvent.wheelDelta > 0) {
          this.zoomIn();
        } else if (e.originalEvent.wheelDelta < 0) {
          this.zoomOut();
        }
      }
    });
  }

  onCanvasClick(page, e) {
    if (this.simpleClick && atom.config.get('pdf-view.enableSyncTeX')) {
      e.preventDefault();
      this.pdfDocument.getPage(page).then((pdfPage) => {
        let viewport = pdfPage.getViewport(this.currentScale);
        [x,y] = viewport.convertToPdfPoint(e.offsetX, $(this.canvases[page - 1]).height() - e.offsetY);

        let callback = ((error, stdout, stderr) => {
          if (!error) {
            stdout = stdout.replace(/\r\n/g, '\n');
            let attrs = {};
            for (let line of stdout.split('\n')) {
              let m = line.match(/^([a-zA-Z]*):(.*)$/)
              if (m) {
                attrs[m[1]] = m[2];
              }
            }

            let file = attrs.Input;
            let line = attrs.Line;

            if (file && line) {
              let editor = null;
              let pathToOpen = path.normalize(attrs.Input);
              let lineToOpen = +attrs.Line;
              let done = false;
              for (let editor of atom.workspace.getTextEditors()) {
                if (editor.getPath() === pathToOpen) {
                  let position = new Point(lineToOpen-1, -1);
                  editor.scrollToBufferPosition(position, {center: true});
                  editor.setCursorBufferPosition(position);
                  editor.moveToFirstCharacterOfLine();
                  let pane = atom.workspace.paneForItem(editor);
                  pane.activateItem(editor);
                  pane.activate();
                  done = true;
                  break;
                }
              }

              if (!done) {
                atom.workspace.open(pathToOpen, {initialLine: lineToOpen, initialColumn: 0})
              }
            }
          }
        });

        let synctexPath = atom.config.get('pdf-view.syncTeXPath');
        let clickspec = [page, x, y, this.filePath].join(':');

        if (synctexPath) {
          execFile(synctexPath, ["edit", "-o", clickspec], callback);
        } else {
          let cmd = `synctex edit -o "${clickspec}"`;
          exec(cmd, callback);
        }
      });
    }
  }

  onScroll() {
    if (!this.updating) {
      this.scrollTopBeforeUpdate = this.scrollTop();
      this.scrollLeftBeforeUpdate = this.scrollLeft();
    }

    this.setCurrentPageNumber();
  }

  setCurrentPageNumber() {
    if (!this.pdfDocument) {
      return;
    }

    let center = (this.scrollBottom() + this.scrollTop())/2.0
    this.currentPageNumber = 1

    if (this.centersBetweenPages.length === 0 && this.pageHeights.length === this.pdfDocument.numPages)
      for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
        this.centersBetweenPages.push(this.pageHeights.slice(0, pdfPageNumber).reduce(((x,y) => x + y), 0) + pdfPageNumber * 20 - 10);
      }

    for (let pdfPageNumber of _.range(2, this.pdfDocument.numPages+1)) {
      if (center >= this.centersBetweenPages[pdfPageNumber-2] && center < this.centersBetweenPages[pdfPageNumber-1]) {
        this.currentPageNumber = pdfPageNumber;
      }
    }

    atom.views.getView(atom.workspace).dispatchEvent(new Event('pdf-view:current-page-update'));
  }

  finishUpdate() {
    this.updating = false;
    if (this.fileChanged) {
      this.updatePdf();
    }
  }

  updatePdf() {
    this.fileChanged = false;

    if (!fs.existsSync(this.filePath)) {
      return;
    }

    let pdfData = null;

    try {
      pdfData = new Uint8Array(fs.readFileSync(this.filePath));
    } catch (error) {
      if (error.code === 'ENOENT') {
        return;
      }
    }

    this.updating = true;
    this.container.find("canvas").remove();
    this.canvases = [];

    PDFJS.getDocument(pdfData).then((pdfDocument) => {
      this.pdfDocument = pdfDocument;
      this.totalPageNumber = this.pdfDocument.numPages;

      for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
        let canvas = $("<canvas/>", {class: "page-container"}).appendTo(this.container)[0];
        this.canvases.push(canvas);
        $(canvas).on('click', (e) => this.onCanvasClick(pdfPageNumber, e));
      }

      this.renderPdf();
    }, () => this.finishUpdate());
  }

  renderPdf(scrollAfterRender = true) {
    this.centersBetweenPages = [];
    this.pageHeights = [];

    if (this.fitToWidth) {
      this.pdfDocument.getPage(1).then((pdfPage) => {
        this.currentScale = this[0].clientWidth / pdfPage.getViewport(1.0).width
      })
      this.fitToWidth = false;
    }

    for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
      let canvas = this.canvases[pdfPageNumber-1];

      this.pdfDocument.getPage(pdfPageNumber).then((pdfPage) => {
        let viewport = pdfPage.getViewport(this.currentScale);
        let context = canvas.getContext('2d');

        let outputScale = window.devicePixelRatio;
        canvas.height = Math.floor(viewport.height) * outputScale;
        canvas.width = Math.floor(viewport.width) * outputScale;

        if (outputScale != 1) {
          context._scaleX = outputScale;
          context._scaleY = outputScale;
          context.scale(outputScale, outputScale);
          canvas.style.width = Math.floor(viewport.width) + 'px';
          canvas.style.height = Math.floor(viewport.height) + 'px';
        }

        this.pageHeights.push(Math.floor(viewport.height));

        pdfPage.render({canvasContext: context, viewport: viewport});

        if (pdfPage.pageNumber === this.pdfDocument.numPages && scrollAfterRender) {
          this.scrollTop(this.scrollTopBeforeUpdate);
          this.scrollLeft(this.scrollLeftBeforeUpdate);
          this.setCurrentPageNumber();
          this.finishUpdate();
        }
      }, () => this.finishUpdate());
    }
  }

  zoomOut() {
    return this.adjustSize(100 / (100 + this.scaleFactor));
  }

  zoomIn() {
    return this.adjustSize((100 + this.scaleFactor) / 100);
  }

  resetZoom() {
    return this.adjustSize(this.defaultScale / this.currentScale);
  }

  computeZoomedScrollTop(oldScrollTop, oldPageHeights) {
    let pixelsToZoom = 0;
    let spacesToSkip = 0;
    let zoomedPixels = 0;

    for (let pdfPageNumber of _.range(0, this.pdfDocument.numPages)) {
      if (pixelsToZoom + spacesToSkip + oldPageHeights[pdfPageNumber] > oldScrollTop) {
        zoomFactorForPage = this.pageHeights[pdfPageNumber] / oldPageHeights[pdfPageNumber];
        let partOfPageAboveUpperBorder = oldScrollTop - (pixelsToZoom + spacesToSkip);
        zoomedPixels += Math.round(partOfPageAboveUpperBorder * zoomFactorForPage);
        pixelsToZoom += partOfPageAboveUpperBorder;
        break;
      } else {
        pixelsToZoom += oldPageHeights[pdfPageNumber];
        zoomedPixels += this.pageHeights[pdfPageNumber];
      }

      if (pixelsToZoom + spacesToSkip + 20 > oldScrollTop) {
        let partOfPaddingAboveUpperBorder = oldScrollTop - (pixelsToZoom + spacesToSkip);
        spacesToSkip += partOfPaddingAboveUpperBorder;
        break;
      } else {
        spacesToSkip += 20;
      }
    }

    return zoomedPixels + spacesToSkip;
  }

  adjustSize(factor) {
    if (!this.pdfDocument) {
      return;
    }

    let oldScrollTop = this.scrollTop();
    let oldPageHeights = this.pageHeights.slice(0);
    this.currentScale = this.currentScale * factor;
    this.renderPdf(false);

    process.nextTick(() => {
      let newScrollTop = this.computeZoomedScrollTop(oldScrollTop, oldPageHeights);
      this.scrollTop(newScrollTop);
    });

    process.nextTick(() => {
      let newScrollLeft = this.scrollLeft() * factor;
      this.scrollLeft(newScrollLeft);
    });
  }

  getCurrentPageNumber() {
    return this.currentPageNumber;
  }

  getTotalPageNumber() {
    return this.totalPageNumber;
  }

  scrollToPage(pdfPageNumber) {
    if (!this.pdfDocument || isNaN(pdfPageNumber)) {
      return;
    }

    pdfPageNumber = Math.min(pdfPageNumber, this.pdfDocument.numPages);
    pageScrollPosition = (this.pageHeights.slice(0, (pdfPageNumber-1)).reduce(((x,y) => x+y), 0)) + (pdfPageNumber - 1) * 20

    return this.scrollTop(pageScrollPosition);
  }

  serialize() {
    return {filePath: this.filePath, deserializer: 'PdfEditorDeserializer'};
  }

  getTitle() {
    if (this.filePath) {
      return path.basename(this.filePath);
    } else {
      return 'untitled';
    }
  }

  getURI() {
    return this.filePath;
  }

  getPath() {
    return this.filePath;
  }

  destroy() {
    return this.detach();
  }

  onDidChangeTitle() {
    return new Disposable(() => null);
  }

  onDidChangeModified() {
    return new Disposable(() => null);
  }
}
<commit_msg>Rename fitToWidth to fitToWidthOnOpen<commit_after>"use babel";

let {$, ScrollView} = require('atom-space-pen-views');
let {Point, TextEditor} = require('atom');
let fs = require('fs-plus');
let path = require('path');
let _ = require('underscore-plus');
let {File, Disposable, CompositeDisposable} = require('atom');
let {Function} = require('loophole');
global.Function = Function;

global.PDFJS = {workerSrc: "temp", cMapUrl:"temp", cMapPacked:true};
require('./../node_modules/pdfjs-dist/build/pdf.js');
PDFJS.workerSrc = "file://" + path.resolve(__dirname, "../node_modules/pdfjs-dist/build/pdf.worker.js");
PDFJS.cMapUrl = "file://" + path.resolve(__dirname, "../node_modules/pdfjs-dist/cmaps")+"/";

let {exec, execFile} = require('child_process');

export default class PdfEditorView extends ScrollView {
  static content() {
    this.div({class: 'pdf-view', tabindex: -1}, () => {
      this.div({outlet: 'container'});
    });
  }

  constructor(filePath) {
    super();

    this.currentScale = 1.5;
    this.defaultScale = 1.5;
    this.scaleFactor = 10.0;
    this.fitToWidthOnOpen = atom.config.get('pdf-view.fitToWidthOnOpen')

    this.filePath = filePath;
    this.file = new File(this.filePath);
    this.canvases = [];

    this.updatePdf();

    this.currentPageNumber = 0;
    this.totalPageNumber = 0;
    this.centersBetweenPages = [];
    this.pageHeights = [];
    this.scrollTopBeforeUpdate = 0;
    this.scrollLeftBeforeUpdate = 0;
    this.updating = false;

    let disposables = new CompositeDisposable();

    let onFileChangeCallback = _.debounce(() => {
      if (this.updating) {
        this.fileChanged = true;
      } else {
        this.updatePdf();
      }
    }, 100);

    disposables.add(this.file.onDidChange(onFileChangeCallback));

    let moveLeftCallback = (() => this.scrollLeft(this.scrollLeft() - $(window).width() / 20));
    let moveRightCallback = (() => this.scrollRight(this.scrollRight() + $(window).width() / 20));
    let scrollCallback = (() => this.onScroll());
    let resizeHandler = (() => this.setCurrentPageNumber());

    let elem = this;

    atom.commands.add('.pdf-view', {
      'core:move-left': moveLeftCallback,
      'core:move-right': moveRightCallback
    });

    elem.on('scroll', scrollCallback);
    disposables.add(new Disposable(() => $(window).off('scroll', scrollCallback)));

    $(window).on('resize', resizeHandler);
    disposables.add(new Disposable(() => $(window).off('resize', resizeHandler)));

    atom.commands.add('atom-workspace', {
      'pdf-view:zoom-in': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.zoomIn();
        }
      },
      'pdf-view:zoom-out': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.zoomOut();
        }
      },
      'pdf-view:reset-zoom': () => {
        if (atom.workspace.getActivePaneItem() === this) {
          this.resetZoom();
        }
      }
    });

    this.dragging = null;

    this.onMouseMove = (e) => {
      if (this.dragging) {
        this.simpleClick = false;

        this.scrollTop(this.dragging.scrollTop - (e.screenY - this.dragging.y));
        this.scrollLeft(this.dragging.scrollLeft - (e.screenX - this.dragging.x));
        e.preventDefault();
      }
    };

    this.onMouseUp = (e) => {
      this.dragging = null;
      $(document).unbind('mousemove', this.onMouseMove);
      $(document).unbind('mouseup', this.onMouseUp);
      e.preventDefault();
    };

    this.on('mousedown', (e) => {
      this.simpleClick = true;
      atom.workspace.paneForItem(this).activate();
      this.dragging = {x: e.screenX, y: e.screenY, scrollTop: this.scrollTop(), scrollLeft: this.scrollLeft()};
      $(document).on('mousemove', this.onMouseMove);
      $(document).on('mouseup', this.onMouseUp);
      e.preventDefault();
    });

    this.on('mousewheel', (e) => {
      if (e.ctrlKey) {
        e.preventDefault();
        if (e.originalEvent.wheelDelta > 0) {
          this.zoomIn();
        } else if (e.originalEvent.wheelDelta < 0) {
          this.zoomOut();
        }
      }
    });
  }

  onCanvasClick(page, e) {
    if (this.simpleClick && atom.config.get('pdf-view.enableSyncTeX')) {
      e.preventDefault();
      this.pdfDocument.getPage(page).then((pdfPage) => {
        let viewport = pdfPage.getViewport(this.currentScale);
        [x,y] = viewport.convertToPdfPoint(e.offsetX, $(this.canvases[page - 1]).height() - e.offsetY);

        let callback = ((error, stdout, stderr) => {
          if (!error) {
            stdout = stdout.replace(/\r\n/g, '\n');
            let attrs = {};
            for (let line of stdout.split('\n')) {
              let m = line.match(/^([a-zA-Z]*):(.*)$/)
              if (m) {
                attrs[m[1]] = m[2];
              }
            }

            let file = attrs.Input;
            let line = attrs.Line;

            if (file && line) {
              let editor = null;
              let pathToOpen = path.normalize(attrs.Input);
              let lineToOpen = +attrs.Line;
              let done = false;
              for (let editor of atom.workspace.getTextEditors()) {
                if (editor.getPath() === pathToOpen) {
                  let position = new Point(lineToOpen-1, -1);
                  editor.scrollToBufferPosition(position, {center: true});
                  editor.setCursorBufferPosition(position);
                  editor.moveToFirstCharacterOfLine();
                  let pane = atom.workspace.paneForItem(editor);
                  pane.activateItem(editor);
                  pane.activate();
                  done = true;
                  break;
                }
              }

              if (!done) {
                atom.workspace.open(pathToOpen, {initialLine: lineToOpen, initialColumn: 0})
              }
            }
          }
        });

        let synctexPath = atom.config.get('pdf-view.syncTeXPath');
        let clickspec = [page, x, y, this.filePath].join(':');

        if (synctexPath) {
          execFile(synctexPath, ["edit", "-o", clickspec], callback);
        } else {
          let cmd = `synctex edit -o "${clickspec}"`;
          exec(cmd, callback);
        }
      });
    }
  }

  onScroll() {
    if (!this.updating) {
      this.scrollTopBeforeUpdate = this.scrollTop();
      this.scrollLeftBeforeUpdate = this.scrollLeft();
    }

    this.setCurrentPageNumber();
  }

  setCurrentPageNumber() {
    if (!this.pdfDocument) {
      return;
    }

    let center = (this.scrollBottom() + this.scrollTop())/2.0
    this.currentPageNumber = 1

    if (this.centersBetweenPages.length === 0 && this.pageHeights.length === this.pdfDocument.numPages)
      for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
        this.centersBetweenPages.push(this.pageHeights.slice(0, pdfPageNumber).reduce(((x,y) => x + y), 0) + pdfPageNumber * 20 - 10);
      }

    for (let pdfPageNumber of _.range(2, this.pdfDocument.numPages+1)) {
      if (center >= this.centersBetweenPages[pdfPageNumber-2] && center < this.centersBetweenPages[pdfPageNumber-1]) {
        this.currentPageNumber = pdfPageNumber;
      }
    }

    atom.views.getView(atom.workspace).dispatchEvent(new Event('pdf-view:current-page-update'));
  }

  finishUpdate() {
    this.updating = false;
    if (this.fileChanged) {
      this.updatePdf();
    }
  }

  updatePdf() {
    this.fileChanged = false;

    if (!fs.existsSync(this.filePath)) {
      return;
    }

    let pdfData = null;

    try {
      pdfData = new Uint8Array(fs.readFileSync(this.filePath));
    } catch (error) {
      if (error.code === 'ENOENT') {
        return;
      }
    }

    this.updating = true;
    this.container.find("canvas").remove();
    this.canvases = [];

    PDFJS.getDocument(pdfData).then((pdfDocument) => {
      this.pdfDocument = pdfDocument;
      this.totalPageNumber = this.pdfDocument.numPages;

      for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
        let canvas = $("<canvas/>", {class: "page-container"}).appendTo(this.container)[0];
        this.canvases.push(canvas);
        $(canvas).on('click', (e) => this.onCanvasClick(pdfPageNumber, e));
      }

      this.renderPdf();
    }, () => this.finishUpdate());
  }

  renderPdf(scrollAfterRender = true) {
    this.centersBetweenPages = [];
    this.pageHeights = [];

    if (this.fitToWidthOnOpen) {
      this.pdfDocument.getPage(1).then((pdfPage) => {
        this.currentScale = this[0].clientWidth / pdfPage.getViewport(1.0).width
      })
      this.fitToWidthOnOpen = false;
    }

    for (let pdfPageNumber of _.range(1, this.pdfDocument.numPages+1)) {
      let canvas = this.canvases[pdfPageNumber-1];

      this.pdfDocument.getPage(pdfPageNumber).then((pdfPage) => {
        let viewport = pdfPage.getViewport(this.currentScale);
        let context = canvas.getContext('2d');

        let outputScale = window.devicePixelRatio;
        canvas.height = Math.floor(viewport.height) * outputScale;
        canvas.width = Math.floor(viewport.width) * outputScale;

        if (outputScale != 1) {
          context._scaleX = outputScale;
          context._scaleY = outputScale;
          context.scale(outputScale, outputScale);
          canvas.style.width = Math.floor(viewport.width) + 'px';
          canvas.style.height = Math.floor(viewport.height) + 'px';
        }

        this.pageHeights.push(Math.floor(viewport.height));

        pdfPage.render({canvasContext: context, viewport: viewport});

        if (pdfPage.pageNumber === this.pdfDocument.numPages && scrollAfterRender) {
          this.scrollTop(this.scrollTopBeforeUpdate);
          this.scrollLeft(this.scrollLeftBeforeUpdate);
          this.setCurrentPageNumber();
          this.finishUpdate();
        }
      }, () => this.finishUpdate());
    }
  }

  zoomOut() {
    return this.adjustSize(100 / (100 + this.scaleFactor));
  }

  zoomIn() {
    return this.adjustSize((100 + this.scaleFactor) / 100);
  }

  resetZoom() {
    return this.adjustSize(this.defaultScale / this.currentScale);
  }

  computeZoomedScrollTop(oldScrollTop, oldPageHeights) {
    let pixelsToZoom = 0;
    let spacesToSkip = 0;
    let zoomedPixels = 0;

    for (let pdfPageNumber of _.range(0, this.pdfDocument.numPages)) {
      if (pixelsToZoom + spacesToSkip + oldPageHeights[pdfPageNumber] > oldScrollTop) {
        zoomFactorForPage = this.pageHeights[pdfPageNumber] / oldPageHeights[pdfPageNumber];
        let partOfPageAboveUpperBorder = oldScrollTop - (pixelsToZoom + spacesToSkip);
        zoomedPixels += Math.round(partOfPageAboveUpperBorder * zoomFactorForPage);
        pixelsToZoom += partOfPageAboveUpperBorder;
        break;
      } else {
        pixelsToZoom += oldPageHeights[pdfPageNumber];
        zoomedPixels += this.pageHeights[pdfPageNumber];
      }

      if (pixelsToZoom + spacesToSkip + 20 > oldScrollTop) {
        let partOfPaddingAboveUpperBorder = oldScrollTop - (pixelsToZoom + spacesToSkip);
        spacesToSkip += partOfPaddingAboveUpperBorder;
        break;
      } else {
        spacesToSkip += 20;
      }
    }

    return zoomedPixels + spacesToSkip;
  }

  adjustSize(factor) {
    if (!this.pdfDocument) {
      return;
    }

    let oldScrollTop = this.scrollTop();
    let oldPageHeights = this.pageHeights.slice(0);
    this.currentScale = this.currentScale * factor;
    this.renderPdf(false);

    process.nextTick(() => {
      let newScrollTop = this.computeZoomedScrollTop(oldScrollTop, oldPageHeights);
      this.scrollTop(newScrollTop);
    });

    process.nextTick(() => {
      let newScrollLeft = this.scrollLeft() * factor;
      this.scrollLeft(newScrollLeft);
    });
  }

  getCurrentPageNumber() {
    return this.currentPageNumber;
  }

  getTotalPageNumber() {
    return this.totalPageNumber;
  }

  scrollToPage(pdfPageNumber) {
    if (!this.pdfDocument || isNaN(pdfPageNumber)) {
      return;
    }

    pdfPageNumber = Math.min(pdfPageNumber, this.pdfDocument.numPages);
    pageScrollPosition = (this.pageHeights.slice(0, (pdfPageNumber-1)).reduce(((x,y) => x+y), 0)) + (pdfPageNumber - 1) * 20

    return this.scrollTop(pageScrollPosition);
  }

  serialize() {
    return {filePath: this.filePath, deserializer: 'PdfEditorDeserializer'};
  }

  getTitle() {
    if (this.filePath) {
      return path.basename(this.filePath);
    } else {
      return 'untitled';
    }
  }

  getURI() {
    return this.filePath;
  }

  getPath() {
    return this.filePath;
  }

  destroy() {
    return this.detach();
  }

  onDidChangeTitle() {
    return new Disposable(() => null);
  }

  onDidChangeModified() {
    return new Disposable(() => null);
  }
}
<|endoftext|>


<commit_before>// TODO
game.PlayerEntity = me.Entity.extend({
	//initializes the function
	init: function(x, y, settings){
		this.setSuper();
		this.setPlayerTimers();
		this.setAttributes();
		this.setFlags();
		//to call player entity
		this.type = "PlayerEntity";
		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH);
		this.addAnimation();
		//adds animation to non-moving position
		this.renderable.setCurrentAnimation("idle");
	},
	setSuper: function(){
		this._super(me.Entity, 'init', [x, y, {
			//makes the image of the player appear
			image: "player",
			//the width of the space created
			width: 64,
			//the height of the space created
			height: 64,
			//height for the player
			spriteheight: "64",
			//width for the player
			spritewidth: "64",
			//function of the shape of the screen
			getShape: function(){
				//creates a rectangle of 64 by 64 so the player can go inside
				//0 , 0 is the origin
				return(new me.Rect(0, 0, 64, 64)).toPolygon();
			}
		}]);
	},
	setPlayerTimers: function(){
		//checks the time for the game
		this.now = new Date().getTime();
		this.lastHit = this.now;
		//stop the player from hitting over and over again
		this.lastAttack = new Date().getTime();
	},
	setAttributes: function(){
		//the life of our player
		//made the health base equal the global variable
		this.health = game.data.playerHealth;
		//it sets the speed of the player when it moves to the right
		//y location changes
		//it moved down to the player
		this.body.setVelocity(game.data.playerMoveSpeed, 20);
		this.attack = game.data.playerAttack;
	},
	setFlags: function(){
		//keeps track of which direction your character is going
		this.facing = "right";
		//states the player is alive
		this.dead = false;
	},
	addAnimation: function(){
		//adds animation to standing starting position
		this.renderable.addAnimation("idle", [78]);
		//adds animation to walking
		this.renderable.addAnimation("walk", [117, 118 , 119, 120, 121, 122, 123, 124, 125], 80);
		//animation for character attack
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);
	},
	//updates the function
	//whole set for just the x-axis
	update: function(delta){
		//keeps it up to date
		this.now = new Date().getTime();
		//if statement for health
		//makes the creep kill the character
		if (this.health <= 0){
			//if player is 0 the player dies
			this.dead = true;
		}
		//moves the player right
		if(me.input.isKeyPressed("right")){
			//sets the position of my x by adding the velocity defined above in setVelocity and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;
			//keeps track of which direction your character is going
			this.facing = "right";
			//makes the charcter face to right
			this.flipX(true);

		}
		//moves the player left
		else if(me.input.isKeyPressed("left")){
				//keeps track of which direction your character is going
				this.facing = "left";
				this.body.vel.x -= this.body.accel.x * me.timer.tick;
				this.flipX(false);
		}
			else {
			//for when the right arrow isnt clicked
			this.body.vel.x = 0;
		}
		//set for the y-axis
		//so the player can go right or left and still jump
		//no double jumping
		if(me.input.isKeyPressed("jump") && !this.body.jumping && !this.body.falling){
			this.body.jumping = true;
			this.body.vel.y -= this.body.accel.y * me.timer.tick;
			//makes sound when the player jumps
			me.audio.play("jump");
		}

		if(me.input.isKeyPressed("attack")){
			if(!this.renderable.isCurrentAnimation("attack")){
				console.log(!this.renderable.isCurrentAnimation("attack"));
				//sets the current animation to attck and once that is over goes back to idle animation
				this.renderable.setCurrentAnimation("attack", "idle");
				//makes it so that the next time we start this sequence we begin from the first animation not whenever we left off when we switched to another animation
				this.renderable.setAnimationFrame();
			}
		}

		//if the button is pushed then it will walk but it not it will execute the else code
		else if(this.body.vel.x !==0 && !this.renderable.isCurrentAnimation("attack")){
		if(!this.renderable.isCurrentAnimation("walk")){
			this.renderable.setCurrentAnimation("walk");
		}
	}else if(!this.renderable.isCurrentAnimation("attack")){
		//if not, make it stand still
		this.renderable.setCurrentAnimation("idle");
	}
		//checks for collisions
		me.collision.check(this, true, this.collideHandler.bind(this), true);

		//updates the function to true
		this.body.update(delta);
		//updates the animation
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	//function that is passing the damage parameter
	loseHealth: function(damage){
		this.health = this.health - damage;
		//print out what the haelth is of the player
		console.log(this.health);
	},
	//new function that is passing the parameter response
	//holds info about collision
	collideHandler: function(response){
		if(response.b.type==='EnemyBaseEntity'){
			//the y difference between the players y position and the base y position
			//keep track of the position of both objects
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;
			//makes the player jump on top of the tower
			if(ydif<-40 && xdif<70 && xdif>-35){
				this.body.falling = false;
				this.body.vel.y = -1
			}
			//stops the player on the left side of the tower
			//prevent from over lapping
			else if(xdif>-35 && this.facing==='right' && (xdif<0)){
				//stop the player from moving
				this.body.vel.x = 0;
				//move player backwards
				//this.pos.x = this.pos.x -1;
			}
			//stops the player on the right side of the tower
			//prevent from over lapping
			else if(xdif<70 && this.facing==='left' && xdif>0){
				//stop the player from moving
				this.body.vel.x = 0;
				//moves player forward
				//this.pos.x = this.pos.x +1;
			}
			//added the gloabl variable playerAttackTimer
			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >= game.data.playerAttackTimer){
				//check when it is called
				console.log("tower Hit");
				this.lastHit = this.now;
				//call the losehealth function
				response.b.loseHealth(game.data.playerAttack);
			}
		}
		//if the player collides with the enemy creep, this code will execute
		else if(response.b.type ==='EnemyCreep'){
			//the y difference between the players y position and the creep y position
			//keep track of the position of both objects
			var xdif = this.pos.x - response.b.pos.x;
			var ydif = this.pos.y - response.b.pos.y;

			if (xdif>0){
				//moves it to the right
				//this.pos.x = this.pos.x +1;
				//when the player and creep are facing each other, the player can attack
				if(this.facing==="left"){
					//sets the velocity to 0
					this.body.vel.x = 0;
				}
			}
			else{
				//moves it to the left
				//this.pos.x = this.pos.x -1;
				//when the player and creep are facing each other, the player can attack
				if(this.facing==="right"){
					//sets the velocity to 0
					this.body.vel.x = 0;
				}
			}
			//added the global vaiable playerAttackTimer
			if(this.renderable.isCurrentAnimation("attack") && this.now - this.lastHit >= game.data.playerAttackTimer
					&& (Math.abs(ydif) <=40) &&
					(((xdif>0) && this.facing==="left") || ((xdif<0) && this.facing==="right"))
					){
				this.lastHit = this.now;
				//if the creeps health is less than our attack, execute code in if statement
				if(response.b.health <= game.data.playerAttack){
					//adds one gold for a creep kill
					game.data.gold += 1;
					//keeps track of our gold
					console.log("Current gold: " + game.data.gold);
				}
				//calls the loseHealth function from the creep
				response.b.loseHealth(game.data.playerAttack);
			}
		}
	}

});

game.Player2 = me.Entity.extend({
// sets properties of enemy player
	init: function(x, y, settings){
		this._super(me.Entity, "init", [x, y, {
			image: "Player2",
			width: 100,
			height: 85,
			spritewidth: "100",
			spriteheight: "85",
			getShape: function(){
				return(new me.Rect(0, 0 ,100, 85)).toPolygon();
			}
		}]);

		// sets health of player starting at 10
		this.health = game.data.playerHealth;
		// always keeps the enemy player updated
		this.alwaysUpdate = true;

		// this.attacking lets us know if thge enemy is currently attacking
		this.attacking = false;

		// keeps track of when our creep last attacked anything
		this.lastAttacking = new Date().getTime();

		// keeps track of the ;ast time our creep hit anything
		this.lastHit = new Date().getTime();

		// timer
		this.now = new Date().getTime();

		// sets were player is located
		this.body.setVelocity(3, 20);

		// sets type of player
		this.type = "Player2";

		// sets the animation of enemy player
		this.renderable.addAnimation("walk", [0, 1, 2, 3, 4], 80);
		this.renderable.setCurrentAnimation("walk");
	},

	update: function(delta){
		// Timer set to check collisons refreshes every single time
		this.now = new Date().getTime();

		// makes creeps move
		this.body.vel.x += this.body.accel.x * me.timer.tick;
		this.flipX(true);

		// checks of creep is colliding with player
		// me.collison.check(this, true, this.collideHandler.bind(this), true);

		this.body.update(delta);

		this._super(me.Entity, "update", [delta]);

		return true;

	},

	collideHandler: function(response){
		if (response.b.type === 'EnemyBaseEntity') {
			this.attacking = true;
			// this.lastAttacking = this.now;
			this.body.vel.x = 0;
			// keeps creep moving to the right to maintain its position
			this.pos.x = this.pos.x + 1;
			// checks that it has been at least 1 second since this creep hit a base
			if ((this.now-this.lastHit >= 1000)) {
				// updates the lasthit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes at a damge of 1
				response.b.loseHealth(1);
			}
		}
		else if (response.b.type === 'EnemyCreep') {
			var xdif = this.pos.x - response.b.pos.x;

			this.attacking = true;

			// this.lastAttacking = this.now;

			if (xdif>0) {
			// Kepps moving the creep to the right to maintain its position
				this.pos.x = this.pos.x + 1;
				this.body.vel.x = 0;
			}
			// checks that it has been less then 1 second since it hit somehting
			if ((this.now-this.lastHit >= 1000) && xdif>0) {
				// updates the last hit time
				this.lastHit = this.now;
				// makes the player call its lose health function and passes it as a damage of one
				response.b.loseHealth(1);
			}
		}
	}

});
<commit_msg>coded for video 31; refactored update function<commit_after>// TODO
game.PlayerEntity = me.Entity.extend({
	//initializes the function
	init: function(x, y, settings){
		this.setSuper();
		this.setPlayerTimers();
		this.setAttributes();
		this.setFlags();
		//to call player entity
		this.type = "PlayerEntity";
		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH);
		this.addAnimation();
		//adds animation to non-moving position
		this.renderable.setCurrentAnimation("idle");
	},
	setSuper: function(){
		this._super(me.Entity, 'init', [x, y, {
			//makes the image of the player appear
			image: "player",
			//the width of the space created
			width: 64,
			//the height of the space created
			height: 64,
			//height for the player
			spriteheight: "64",
			//width for the player
			spritewidth: "64",
			//function of the shape of the screen
			getShape: function(){
				//creates a rectangle of 64 by 64 so the player can go inside
				//0 , 0 is the origin
				return(new me.Rect(0, 0, 64, 64)).toPolygon();
			}
		}]);
	},
	setPlayerTimers: function(){
		//checks the time for the game
		this.now = new Date().getTime();
		this.lastHit = this.now;
		//stop the player from hitting over and over again
		this.lastAttack = new Date().getTime();
	},
	setAttributes: function(){
		//the life of our player
		//made the health base equal the global variable
		this.health = game.data.playerHealth;
		//it sets the speed of the player when it moves to the right
		//y location changes
		//it moved down to the player
		this.body.setVelocity(game.data.playerMoveSpeed, 20);
		this.attack = game.data.playerAttack;
	},
	setFlags: function(){
		//keeps track of which direction your character is going
		this.facing = "right";
		//states the player is alive
		this.dead = false;
	},
	addAnimation: function(){
		//adds animation to standing starting position
		this.renderable.addAnimation("idle", [78]);
		//adds animation to walking
		this.renderable.addAnimation("walk", [117, 118 , 119, 120, 121, 122, 123, 124, 125], 80);
		//animation for character attack
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);
	},
	//updates the function
	//whole set for just the x-axis
	update: function(delta){
		//keeps it up to date
		this.now = new Date().getTime();
		this.dead = checkIfDead();
		this.checkKeyPressesAndMove();
		if(me.input.isKeyPressed("attack")){
			if(!this.renderable.isCurrentAnimation("attack")){
				console.log(!this.renderable.isCurrentAnimation("attack"));
				//sets the current animation to attck and once that is over goes back to idle animation
				this.renderable.setCurrentAnimation("attack", "idle");
				//makes it so that the next time we start this sequence we begin from the first animation not whenever we left off when we switched to another animation
				this.renderable.setAnimationFrame();
			}
		}

		//if the button is pushed then it will walk but it not it will execute the else code
		else if(this.body.vel.x !==0 && !this.renderable.isCurrentAnimation("attack")){
		if(!this.renderable.isCurrentAnimation("walk")){
			this.renderable.setCurrentAnimation("walk");
		}
	}else if(!this.renderable.isCurrentAnimation("attack")){
		//if not, make it stand still
		this.renderable.setCurrentAnimation("idle");
	}
		//checks for collisions
		me.collision.check(this, true, this.collideHandler.bind(this), true);

		//updates the function to true
		this.body.update(delta);
		//updates the animation
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	checkIfDead: function(){
		//if statement for health
		//makes the creep kill the character
		if (this.health <= 0){
			//if player is 0 the player dies
			return = true;
		}
		return false;
	},
	checkKeyPressesAndMove: function(){
		//moves the player right
		if(me.input.isKeyPressed("right")){
			this.moveRight();
		}
		//moves the player left
		else if(me.input.isKeyPressed("left")){
			this.moveLeft();
		}
			else {
			//for when the right arrow isnt clicked
			this.body.vel.x = 0;
		}
		//set for the y-axis
		//so the player can go right or left and still jump
		//no double jumping
		if(me.input.isKeyPressed("jump") && !this.body.jumping && !this.body.falling){
			this.jump();
		}
	},
	moveRight: function(){
			//sets the position of my x by adding the velocity defined above in setVelocity and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;
			//keeps track of which direction your character is going
			this.facing = "right";
			//makes the charcter face to right
			this.flipX(true);
	},
	moveLeft: function(){
		//keeps track of which direction your character is going
		this.facing = "left";
		this.body.vel.x -= this.body.accel.x * me.timer.tick;
		this.flipX(false);
	},
	jump: function(){
			this.body.jumping = true;
			this.body.vel.y -= this.body.accel.y * me.timer.tick;
			//makes sound when the player jumps
			me.audio.play("jump");
		},
	//function that is passing the damage parameter
	loseHealth: function(damage){
		this.health = this.health - damage;
		//print out what the haelth is of the player
		console.log(this.health);
	},
	//new function that is passing the parameter response
	//holds info about collision
	collideHandler: function(response){
		if(response.b.type==='EnemyBaseEntity'){
			//the y difference between the players y position and the base y position
			//keep track of the position of both objects
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;
			//makes the player jump on top of the tower
			if(ydif<-40 && xdif<70 && xdif>-35){
				this.body.falling = false;
				this.body.vel.y = -1
			}
			//stops the player on the left side of the tower
			//prevent from over lapping
			else if(xdif>-35 && this.facing==='right' && (xdif<0)){
				//stop the player from moving
				this.body.vel.x = 0;
				//move player backwards
				//this.pos.x = this.pos.x -1;
			}
			//stops the player on the right side of the tower
			//prevent from over lapping
			else if(xdif<70 && this.facing==='left' && xdif>0){
				//stop the player from moving
				this.body.vel.x = 0;
				//moves player forward
				//this.pos.x = this.pos.x +1;
			}
			//added the gloabl variable playerAttackTimer
			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >= game.data.playerAttackTimer){
				//check when it is called
				console.log("tower Hit");
				this.lastHit = this.now;
				//call the losehealth function
				response.b.loseHealth(game.data.playerAttack);
			}
		}
		//if the player collides with the enemy creep, this code will execute
		else if(response.b.type ==='EnemyCreep'){
			//the y difference between the players y position and the creep y position
			//keep track of the position of both objects
			var xdif = this.pos.x - response.b.pos.x;
			var ydif = this.pos.y - response.b.pos.y;

			if (xdif>0){
				//moves it to the right
				//this.pos.x = this.pos.x +1;
				//when the player and creep are facing each other, the player can attack
				if(this.facing==="left"){
					//sets the velocity to 0
					this.body.vel.x = 0;
				}
			}
			else{
				//moves it to the left
				//this.pos.x = this.pos.x -1;
				//when the player and creep are facing each other, the player can attack
				if(this.facing==="right"){
					//sets the velocity to 0
					this.body.vel.x = 0;
				}
			}
			//added the global vaiable playerAttackTimer
			if(this.renderable.isCurrentAnimation("attack") && this.now - this.lastHit >= game.data.playerAttackTimer
					&& (Math.abs(ydif) <=40) &&
					(((xdif>0) && this.facing==="left") || ((xdif<0) && this.facing==="right"))
					){
				this.lastHit = this.now;
				//if the creeps health is less than our attack, execute code in if statement
				if(response.b.health <= game.data.playerAttack){
					//adds one gold for a creep kill
					game.data.gold += 1;
					//keeps track of our gold
					console.log("Current gold: " + game.data.gold);
				}
				//calls the loseHealth function from the creep
				response.b.loseHealth(game.data.playerAttack);
			}
		}
	}

});

game.Player2 = me.Entity.extend({
// sets properties of enemy player
	init: function(x, y, settings){
		this._super(me.Entity, "init", [x, y, {
			image: "Player2",
			width: 100,
			height: 85,
			spritewidth: "100",
			spriteheight: "85",
			getShape: function(){
				return(new me.Rect(0, 0 ,100, 85)).toPolygon();
			}
		}]);

		// sets health of player starting at 10
		this.health = game.data.playerHealth;
		// always keeps the enemy player updated
		this.alwaysUpdate = true;

		// this.attacking lets us know if thge enemy is currently attacking
		this.attacking = false;

		// keeps track of when our creep last attacked anything
		this.lastAttacking = new Date().getTime();

		// keeps track of the ;ast time our creep hit anything
		this.lastHit = new Date().getTime();

		// timer
		this.now = new Date().getTime();

		// sets were player is located
		this.body.setVelocity(3, 20);

		// sets type of player
		this.type = "Player2";

		// sets the animation of enemy player
		this.renderable.addAnimation("walk", [0, 1, 2, 3, 4], 80);
		this.renderable.setCurrentAnimation("walk");
	},

	update: function(delta){
		// Timer set to check collisons refreshes every single time
		this.now = new Date().getTime();

		// makes creeps move
		this.body.vel.x += this.body.accel.x * me.timer.tick;
		this.flipX(true);

		// checks of creep is colliding with player
		// me.collison.check(this, true, this.collideHandler.bind(this), true);

		this.body.update(delta);

		this._super(me.Entity, "update", [delta]);

		return true;

	},

	collideHandler: function(response){
		if (response.b.type === 'EnemyBaseEntity') {
			this.attacking = true;
			// this.lastAttacking = this.now;
			this.body.vel.x = 0;
			// keeps creep moving to the right to maintain its position
			this.pos.x = this.pos.x + 1;
			// checks that it has been at least 1 second since this creep hit a base
			if ((this.now-this.lastHit >= 1000)) {
				// updates the lasthit timer
				this.lastHit = this.now;
				// makes the player base call its loseHealth function and passes at a damge of 1
				response.b.loseHealth(1);
			}
		}
		else if (response.b.type === 'EnemyCreep') {
			var xdif = this.pos.x - response.b.pos.x;

			this.attacking = true;

			// this.lastAttacking = this.now;

			if (xdif>0) {
			// Kepps moving the creep to the right to maintain its position
				this.pos.x = this.pos.x + 1;
				this.body.vel.x = 0;
			}
			// checks that it has been less then 1 second since it hit somehting
			if ((this.now-this.lastHit >= 1000) && xdif>0) {
				// updates the last hit time
				this.lastHit = this.now;
				// makes the player call its lose health function and passes it as a damage of one
				response.b.loseHealth(1);
			}
		}
	}

});
<|endoftext|>


<commit_before>// Copyright (c) 2012 Ivan Kubrakov
// Selectik: a jQuery custom select plugin http://brankub.github.com/selectik/

(function($) {
	// global variables
	var openList = false;
	var selectControl = false;
	var trigger = false;

	$.selectik = function(element, options) {
		// global variables for this instance of plugin
		var count, standardTop, heightItem, heightContainer, disabled, heightList, heightShift, relating, shiftL, heightScroll, scrollL = false, inputSearch = '', change = false, settings;
		// global variables for this instance of plugin (objects)
		var $listContainer, $list, $text, $scroll, $container, $bgScroll, $collection;

			// private method: plugin's default options
	        var _defaults = {
			containerClass: 'custom-select',
			width: 0,
			maxItems: 0,
			customScroll: 1,
			speedAnimation:200
	        }

		var selectik = this;
	        selectik.settings = {}
	        var $cselect = $(element),
	             cselect = element;

		selectik.init = function() {
			// merged properties
	            	settings = $.extend({}, _defaults, options);
				// fire start functions
				_getHtml();
				_handlers();
			}

		// private method: wrap selects in divs and fire html generator
	        var _getHtml = function() {
	            	$cselect.wrap('

');
	            	$container = $cselect.parent();
			_getList({ refreshSelect: false });
	        }

		// private method: list html
		var _getList = function(e){
			var html = '';
			count = cselect.length;
			if (e.refreshSelect){ $('ul', $container).remove(); }

			// loop html
			$collection = $('option', $cselect);

			for (var i = 0; i < $collection.length; i++){
				var $this = $($collection[i]);
				disabled = ($this.attr('disabled') === 'disabled') ? 'disabled' : '';
				var valueOption = $this.attr('value');
				html += '
'+valueOption+'
';
			}

			// html for control
			var scrollHtml = (settings.maxItems > 0 && settings.customScroll == 1) ? '

' : '';
			var scrollClass = (settings.customScroll == 1) ? 'custom-scroll' : 'default-scroll';

			// selected
			var $selected = $('option:selected', $cselect);

			// check if first time or refresh
			if (e.refreshSelect){
				html = '
'+html+'
';
				$(html).prependTo($('.select-list', $container));
			}else{
				html = ''+$selected.attr('value')+'
'+scrollHtml+'
'+html+'

';
				$(html).prependTo($container);
			}

			$list = $('ul', $container);
			$text = $('.custom-text', $container);
			$listContainer = $('.select-list', $container);
			$('li:eq('+($selected.index())+')', $list).addClass('selected');

			// give width to elements
			$container.removeClass('done');
			var width = (settings.width > 0) ? settings.width :  $cselect.outerWidth();

			selectik.setWidthCS(width);
			standardTop = parseInt($listContainer.css('top'));

	            	// fire function for max length
	           	_getLength({refreshSelect: e.refreshSelect });
		}

		var _getLength = function(e){
			if (!e.refreshSelect){ heightItem = $('li:nth-child(1)', $list).outerHeight(); }

			// check if count of options more then max
		  	if (count < settings.maxItems || settings.maxItems == 0) { $listContainer.hide(); $container.addClass('done'); return; }
			scrollL = true;
	            	heightList = heightItem*count;
			heightContainer = heightItem*settings.maxItems;

			// put height for list
			$list.css('height', heightContainer);
			$listContainer.hide();
			$container.addClass('done');
			if (settings.customScroll == 1) {  _getScroll(); }
		}

		// private method: custom scroll
		var _getScroll = function(){
			var shiftL;
			var allHeight = heightItem*count;
			heightShift = -allHeight+heightContainer;
			$bgScroll = $('.select-scroll', $listContainer);
			$bgScroll.css('height', heightContainer);
			$scroll = $('.scroll-drag', $listContainer);
			$listContainer.addClass('maxlength');

			// calculate relate of heights
			relating = allHeight / heightContainer;

			// height of scroll
			heightScroll = heightContainer*(heightContainer / allHeight);
			$scroll.css('height', heightScroll);

			// if selected
			if ($('.selected', $list).length > 0){
				_shift($('.selected', $list).index());
			}
			if (settings.customScroll == true){ _scrollHandlers(); }
		}

		var _scrollHandlers = function(){
			// bind mousewheel
			$list.bind('mousewheel', function(event, deltaY) {
				shiftL = parseInt($list.css('top'))+(deltaY*heightItem);
				_shiftHelper(shiftL);
				return false;
			});

			// bind click on scroll background
			$bgScroll.click(function(e){
				var direction = (((e.pageY - $(this).offset().top)/heightContainer) > 0.5) ? -1 : 1;
				shiftL = parseInt($list.css('top')) + (heightItem * direction);
				_shiftHelper(shiftL);
				return false;
			});

			// draggable handler and calculate
	           	$scroll.on('mousedown', function(e){ _draggable(e, true); });
			$(document).on('mouseup', function(e){ _draggable(e, false); });
		}

		// private method: draggable for scroll
		var _draggable = function(e, on){
			if (on){
				if (e.preventDefault()) { e.preventDefault(); }
				var startPosition = parseInt($scroll.css('top'));
				var helper = e.clientY;
				$(document).bind('mousemove', function(e){
					var newPosition = (helper - e.clientY) - startPosition;
					_shiftHelper(newPosition*relating);
				});
			}else{
				$(document).unbind('mousemove');
					openList = true;
				}
		}

		// private method: shift
		var _shiftHelper = function (e){
			e = (e > 0) ? 0 : e;
			e = (e < heightShift) ? heightShift: e;
			$list.css('top', e);
			$scroll.css('top', -e/relating);
		}

		// private method: shift conrtol
		var _shift = function(indexEl){
			if (indexEl < 0 || indexEl == count) { return; }
			var topShift = (indexEl > count-settings.maxItems) ? heightList-heightContainer : heightItem*indexEl;
			$('.selected', $list).removeClass('selected');
			$('li:nth-child('+(indexEl+1)+')', $list).addClass('selected');
			if (openList && selectControl){
				$text.text($('li:nth-child('+(indexEl-1)+')', $list).data('value'));
			}
			if (!scrollL) { return; }
			_shiftHelper(-topShift);
		}

		// private method: handlers
		var _handlers = function(){
			// change on original select
			$cselect.on('change', function(){
				 if (change) { change = false; return false; }
				_changeSelected($('option:selected', $(this)));
			});
			// click on select
			$text.on('click', function(){
	        	        if( $container.hasClass('disable')) { return false; }
				$cselect.focus();
				_fadeList($listContainer, false);
				return false;
			});
			// click on custom option
			$list.on('click', 'li', function(event){
				 if ($(this).hasClass('disabled')) { return false; }
				_changeSelected($(this));
			});
			$cselect.bind('keyup', function(e) { _keysHandlers(e) });
			if ($.browser.opera){
				$cselect.bind('keydown', function(e) { trigger = true; });
			}
		}

	        // private method: handlers on keys
	        var _keysHandlers = function(e){
			if (e.keyCode == 13 || e.keyCode == 27) { _fadeList($listContainer, true); }
			if (!$listContainer.is(':visible')){
				_fadeList($listContainer, false);
			}
			$cselect.change();
			if (scrollL) { _shift($('option:selected', $cselect).index()); }
	        }

		// private method: change selected
		var _changeSelected = function(e){
			var dataValue = (e.parents('select').length > 0) ? e.attr('value') : e.data('value');
			_changeSelectedHtml(dataValue, e.index()+1);
		}

		// private method: change selected
		var _changeSelectedHtml = function(dataValue, index){
			if (index > count || index == 0) { return false;}
			change = true;
			$cselect.attr('value', dataValue).change();
			$('.selected', $list).removeClass('selected');
			$('li:nth-child('+ index +')', $list).addClass('selected');
			$text.text(dataValue);
		}

		// private method: show/hdie list
		var _fadeList = function(e, out){
			$('.custom-select.open').children('.select-list').stop(true, true).fadeOut(settings.speedAnimation).parent().toggleClass('open');;
			if (out){ return; }
			openList = false;
			selectik.positionCS(e);
			e.stop(true, true).fadeToggle(settings.speedAnimation);
			e.parent().toggleClass('open');
			setTimeout(function(){ openList = true; }, settings.speedAnimation);
		}

		// public method: hide list
		selectik.hideCS = function(){
			$listContainer.fadeOut(settings.speedAnimation);
			$container.removeClass('open');
			openList = true;
		}

		// public method: show list
		selectik.showCS = function(){
			openList = false;
			$listContainer.fadeIn(settings.speedAnimation);
			$container.addClass('open');
		}

		// public method: postion of list
		selectik.positionCS = function(e){
			elParent = e.parent();
			var heightPosition = (scrollL) ? settings.maxItems*heightItem : count*heightItem;
			var quaItems = (scrollL) ? settings.maxItems : count;
			var topPosition = ($(window).outerHeight() - (elParent.offset().top - $(window).scrollTop()) - elParent.outerHeight() < heightPosition) ? -quaItems*heightItem-(elParent.outerHeight()/4) : standardTop;
			topPosition = ((elParent.offset().top - $(window).scrollTop()) < heightPosition) ? standardTop : topPosition;
			e.css('top', topPosition);
		}

		// public method: refresh list
		selectik.refreshCS = function() {
			_getList({ refreshSelect: true });
	        }

		// public method: change active element
		selectik.changeCS = function(val) {
			var index = (val.index > 0) ? val.index : $('option[value="'+val.value+'"]', $cselect).index()+1;
			var dataValue = $('option:nth-child('+(index)+')', $cselect).attr('value');
			_changeSelectedHtml(dataValue, index);
		}

		// public method: disable list
		selectik.disableCS = function(){
			$cselect.attr('disabled', true);
			$container.addClass('disable');
		}

		// public method: enable list
		selectik.enableCS = function(){
			$cselect.attr('disabled', false);
			$container.removeClass('disable');
		}

		// public method: width of select
		selectik.setWidthCS = function(width){
			$list.css('width', width);
			$text.css('width', width);
		}
		selectik.init();
	}

	$.fn.selectik = function(options) {
	        return this.each(function() {
	            if (undefined == $(this).data('selectik')) {
	                // create a new instance of the plugin
	                var selectik = new $.selectik(this, options);
	                $(this).data('selectik', selectik);
	            }
        	});
    	}

	// global handlers
	$(window).resize(function(){
        if (openList){
            if (!$('.custom-select.open').length > 0) { return; }
            $('.custom-select.open').children('select').data('selectik').positionCS($('.select-list:visible'));
        }
	});
	$(document).bind('click', function(){
		if (trigger) { trigger = false; return; }
		if (openList){
			openList = false;
			if ($('.custom-select.open').length > 0){
				$('.custom-select.open').children('select').data('selectik').hideCS();
			}
		}
	});

})(jQuery);<commit_msg>Changes up to issues<commit_after>// Copyright (c) 2012 Ivan Kubrakov
// Selectik: a jQuery custom select plugin http://brankub.github.com/selectik/

(function($) {
	// global variables
	var openList = false;
	var selectControl = false;
	var trigger = false;

	$.selectik = function(element, options) {
		// global variables for this instance of plugin
		var count, standardTop, heightItem, heightContainer, disabled, heightList, heightShift, relating, heightScroll, scrollL = false, change = false, settings;
		// global variables for this instance of plugin (objects)
		var $listContainer, $list, $text, $scroll, $container, $bgScroll, $collection, $selected;

        // private method: plugin's default options
        var _defaults = {
            containerClass: 'custom-select',
            width: 0,
            maxItems: 0,
            customScroll: 1,
            speedAnimation:200
        };

		var selectik = this;
        selectik.settings = {};
        var $cselect = $(element),
             cselect = element;

		selectik.init = function() {
		// merged properties
				settings = $.extend({}, _defaults, options);
			// fire start functions
			_getHtml();
			_handlers();
		};

		// private method: wrap selects in divs and fire html generator
		var _getHtml = function() {
			$cselect.wrap('

');
			$container = $cselect.parent();
			_getList({ refreshSelect: false });
		};

		// private method: list html
		var _getList = function(e){
			var html = '';
			count = cselect.length;
			if (e.refreshSelect){ $('ul', $container).remove(); }

			// loop html
			$collection = $('option', $cselect);

			for (var i = 0; i < $collection.length; i++){
				var $this = $($collection[i]);
				disabled = ($this.attr('disabled') === 'disabled') ? 'disabled' : '';
				var textOption = $this[0].text;
                var valueOption = $this[0].value;
				html += '
'+textOption+'
';
			}

			// html for control
			var scrollHtml = (settings.maxItems > 0 && settings.customScroll == 1) ? '

' : '';
			var scrollClass = (settings.customScroll == 1) ? 'custom-scroll' : 'default-scroll';

			// selected
			$selected = $('option:selected', $cselect);

			// check if first time or refresh
			if (e.refreshSelect){
				html = '
'+html+'
';
				$(html).prependTo($('.select-list', $container));
			}else{
				html = ''+$selected[0].text+'
'+scrollHtml+'
'+html+'

';
				$(html).prependTo($container);
			}

			$list = $('ul', $container);
			$text = $('.custom-text', $container);
			$listContainer = $('.select-list', $container);
			$('li:eq('+($selected.index())+')', $list).addClass('selected');

			// give width to elements
			$container.removeClass('done');
			var width = (settings.width > 0) ? settings.width :  $cselect.outerWidth();

			selectik.setWidthCS(width);
			standardTop = parseInt($listContainer.css('top'));

			// fire function for max length
			_getLength({refreshSelect: e.refreshSelect });
		};

		var _getLength = function(e){
			if (!e.refreshSelect){ heightItem = $('li:nth-child(1)', $list).outerHeight(); }

			// check if count of options more then max
		  	if (count < settings.maxItems || settings.maxItems == 0) { $listContainer.hide(); $container.addClass('done'); return; }
			scrollL = true;
           	heightList = heightItem*count;
			heightContainer = heightItem*settings.maxItems;

			// put height for list
			$list.css('height', heightContainer);
			$listContainer.hide();
			$container.addClass('done');
			if (settings.customScroll == 1) {  _getScroll(); }
		};

		// private method: custom scroll
		var _getScroll = function(){
			var allHeight = heightItem*count;
			heightShift = -allHeight+heightContainer;
			$bgScroll = $('.select-scroll', $listContainer);
			$bgScroll.css('height', heightContainer);
			$scroll = $('.scroll-drag', $listContainer);
			$listContainer.addClass('maxlength');

			// calculate relate of heights
			relating = allHeight / heightContainer;

			// height of scroll
			heightScroll = heightContainer*(heightContainer / allHeight);
			$scroll.css('height', heightScroll);

			// if selected
			if ($('.selected', $list).length > 0){
				_shift($('.selected', $list).index());
			}
			if (settings.customScroll){ _scrollHandlers(); }
		};

		var _scrollHandlers = function(){
            var shiftL;
			// bind mousewheel
			$list.bind('mousewheel', function(event, deltaY) {
				shiftL = parseInt($list.css('top'))+(deltaY*heightItem);
				_shiftHelper(shiftL);
				return false;
			});

			// bind click on scroll background
			$bgScroll.click(function(e){
				var direction = (((e.pageY - $(this).offset().top)/heightContainer) > 0.5) ? -1 : 1;
				shiftL = parseInt($list.css('top')) + (heightItem * direction);
				_shiftHelper(shiftL);
				return false;
			});

			// draggable handler and calculate
	        $scroll.on('mousedown', function(e){ _draggable(e, true); });
			$(document).on('mouseup', function(e){ _draggable(e, false); });
		};

		// private method: draggable for scroll
		var _draggable = function(e, on){
			if (on){
				if (e.preventDefault()) { e.preventDefault(); }
				var startPosition = parseInt($scroll.css('top'));
				var helper = e.clientY;
				$(document).bind('mousemove', function(e){
					var newPosition = (helper - e.clientY) - startPosition;
					_shiftHelper(newPosition*relating);
				});
			}else{
				$(document).unbind('mousemove');
					openList = true;
				}
		};

		// private method: shift
		var _shiftHelper = function (e){
			e = (e > 0) ? 0 : e;
			e = (e < heightShift) ? heightShift: e;
			$list.css('top', e);
			$scroll.css('top', -e/relating);
		};

		// private method: shift conrtol
		var _shift = function(indexEl){
			if (indexEl < 0 || indexEl == count) { return; }
			var topShift = (indexEl > count-settings.maxItems) ? heightList-heightContainer : heightItem*indexEl;
			$('.selected', $list).removeClass('selected');
			$('li:nth-child('+(indexEl+1)+')', $list).addClass('selected');
			if (openList && selectControl){
				$text.text($('li:nth-child('+(indexEl-1)+')', $list).data('value'));
			}
			if (!scrollL) { return; }
			_shiftHelper(-topShift);
		};

		// private method: handlers
		var _handlers = function(){
            // reset button
            var $reset = $('input[type="reset"]', $cselect.parents('form'));
            if ($reset.length > 0){
                $reset.bind('click', function(){
                    var index = ($selected.length > 0) ? $selected.index(): 0;
                    _changeSelected($('option:eq('+index+')', $cselect));
                });
            }

			// change on original select
			$cselect.bind('change', function(){
				 if (change) { change = false; return false; }
				_changeSelected($('option:selected', $(this)));
			});

			// click on select
			$text.bind('click', function(){
	        	if( $container.hasClass('disable')) { return false; }
				$cselect.focus();
				_fadeList($listContainer, false, true);
			});

            // active class
            $cselect.bind('focus', function(){
                $container.addClass('active');
            });
            $cselect.bind('blur', function(){
                $container.removeClass('active');
				if (openList){
					selectik.hideCS();
					$cselect.parent().removeClass('active');
				}
            });

			// click on custom option
			$('li', $list).bind('click', function(){
				 if ($(this).hasClass('disabled')) { return false; }
				_changeSelected($(this));
			});

			$cselect.bind('keyup', function(e) { _keysHandlers(e); });
			if ($.browser.opera){
				$cselect.bind('keydown', function() { trigger = true; });
			};
		};

        // private method: handlers on keys
        var _keysHandlers = function(e){
            if (e.keyCode == 13 && $listContainer.is(':visible')) { _fadeList($listContainer, true, false); }
            if (!$.browser.msie){
                if (e.keyCode == 27 && $listContainer.is(':visible')) { _fadeList($listContainer, true, true); }
            }
            $cselect.change();
            if (scrollL) { _shift($('option:selected', $cselect).index()); }
        };

		// private method: change selected
		var _changeSelected = function(e){
			var dataValue = (e.parents('select').length > 0) ? e.attr('value') : e.data('value');
            var textValue = e.text();
			_changeSelectedHtml(dataValue, textValue, e.index()+1);
		};

		// private method: change selected
		var _changeSelectedHtml = function(dataValue, textValue, index){
			if (index > count || index == 0) { return false;}
			change = true;
			$cselect.attr('value', dataValue).change();
			$('.selected', $list).removeClass('selected');
			$('li:nth-child('+ index +')', $list).addClass('selected');
			$text.text(textValue);
		};

		// private method: show/hdie list
		var _fadeList = function(e, out, text){
			if ($('.'+settings.containerClass+'.open_list').length == 1){
				$('.'+settings.containerClass+'.open_list').children('select').data('selectik').hideCS();
			}
            if (!text){
                $('.'+settings.containerClass+'.open_list').children('.select-list').stop(true, true).fadeOut(settings.speedAnimation).parent().toggleClass('open_list');
                if (out){ return; }
            }
			openList = false;
			selectik.positionCS(e);
			e.stop(true, true).fadeToggle(settings.speedAnimation);
			e.parent().toggleClass('open_list');
			setTimeout(function(){ openList = true; }, settings.speedAnimation);
		};

		// public method: hide list
		selectik.hideCS = function(){
			$listContainer.fadeOut(settings.speedAnimation);
			$container.removeClass('open_list');
            $cselect.focus();
			openList = true;
		};

		// public method: show list
		selectik.showCS = function(){
			openList = false;
			$listContainer.fadeIn(settings.speedAnimation);
			$container.addClass('open_list');
		};

		// public method: postion of list
		selectik.positionCS = function(e){
			elParent = e.parent();
			var heightPosition = (scrollL) ? settings.maxItems*heightItem : count*heightItem;
			var quaItems = (scrollL) ? settings.maxItems : count;
			var topPosition = ($(window).outerHeight() - (elParent.offset().top - $(window).scrollTop()) - elParent.outerHeight() < heightPosition) ? -quaItems*heightItem-(elParent.outerHeight()/4) : standardTop;
			topPosition = ((elParent.offset().top - $(window).scrollTop()) < heightPosition) ? standardTop : topPosition;
			e.css('top', topPosition);
		};

		// public method: refresh list
		selectik.refreshCS = function() {
            _getList({ refreshSelect: true });
	    };

		// public method: change active element
		selectik.changeCS = function(val) {
			var index = (val.index > 0) ? val.index : $('option[value="'+val.value+'"]', $cselect).index()+1;
			var dataValue = $('option:nth-child('+(index)+')', $cselect).attr('value');
			_changeSelectedHtml(dataValue, index);
		};

		// public method: disable list
		selectik.disableCS = function(){
			$cselect.attr('disabled', true);
			$container.addClass('disable');
		};

		// public method: enable list
		selectik.enableCS = function(){
			$cselect.attr('disabled', false);
			$container.removeClass('disable');
		};

        // public method: required
        selectik.requiredCS = function(){
            $text.toggleClass('required');
        };

		// public method: width of select
		selectik.setWidthCS = function(width){
			$list.css('width', width);
			$text.css('width', width);
		};
		selectik.init();
	};

	$.fn.selectik = function(options) {
        return this.each(function() {
            if ($('optgroup', this).length > 0 || $(this).attr('multiple') == 'multiple') { return; }
            if (undefined == $(this).data('selectik')) {
                // create a new instance of the plugin
                var selectik = new $.selectik(this, options);
                $(this).data('selectik', selectik);
            }
        });
    };

	// global handlers
	$(window).resize(function(){
        if (openList){
            if (!$('.open_list').length > 0) { return; }
            $('.open_list').children('select').data('selectik').positionCS($('.select-list:visible'));
        }
	});
	$(document).bind('click', function(){
		if (trigger) { trigger = false; return; }
		if (openList){
			openList = false;
			if ($('.open_list').length > 0){
				$('.open_list').children('select').data('selectik').hideCS();
			}
		}
	});

})(jQuery);<|endoftext|>


<commit_before>var sigGen = require('./sigGen.js');
var modulator = require('./modulator.js');
var fft = require('./fft.js');
var windowing = require('fft-windowing');

function getPsd(centerFreq, span, resBw, vidBw, refLvl, scale, sigFreq) {
	var startFreq = centerFreq - span / 2.0;
	var NENBW = 1.5;
	var df = resBw / NENBW;
	var n = Math.ceil(span / df);
	var T = 1 / ( 2 * n * df); // Get sampling interval.

	var fftResult = getFFT(1, T, sigFreq, 0, n, startFreq);

	var psd = {
		refLvl: refLvl,
		scale: scale,
		resBw: resBw,
		vidBw: vidBw,
		freq: fftResult.freq,
		psd: fftResult.psd,
		power: fftResult.power
	};

	return psd;
}

function getFFT(A, T, f, phi, n, startFreq) {
    var t = new Array(n);
	var bins = new Array(n);
	for (var i = 0; i < n; i++) {
		t[i] = i * T;
		bins[i] = i;
	}

	// Generate the signal
	var noise = sigGen.noise(0, A/1000, n);
	var signal = sigGen.sinusoid(A, f, t, phi);
	signal = sigGen.add([signal, noise]);

	// Heterodyne with LO signal to shift the DFT to the specified start frequency
	if(startFreq != 0)
		signal = sigGen.multiply([signal, sigGen.sinusoid(1, startFreq, t, 0)]);

	signal = windowing.hann(signal);
	var NENBW = 1.5; // Hann window normalized equivalent noise bandwidth

	// Subtract DC offset
	var avg = 0;
	for (var i = 0; i < n; i++)
		avg += signal[i];
	avg /= n;

	for (var i = 0; i < n; i++)
		signal[i] -= avg;

	// Compute the FFT in place
	var reals = signal.slice(0);
	var imag = new Array(n);
	for (var i = 0; i < n; i++)
		imag[i] = 0;
	fft.transform(reals, imag);

	// Get the single-sided power spectrum in dB and the corresponding frequencies
	var n2 = (n % 2 == 0) ? (n / 2) + 1 : (n + 1) / 2;
	var df = 1 / (n * T); // Frequency resolution (bin bandwidth)
	var power = new Array(n2);
	var psd = new Array(n2);
	var freq = new Array(n2);

	for (var i = 0; i < n2; i++) {
		var R2 = Math.pow(reals[i], 2);
		var I2 = Math.pow(imag[i], 2);
		var P = 2 * (R2 + I2) / Math.pow(n, 2);
		power[i] = 10 * Math.log(P) / Math.LN10; // Convert to dB
		psd[i] = 10 * Math.log(P / (df * NENBW)) / Math.LN10;
		freq[i] = startFreq + i * df;
	}

	var data = {
		time: t,
		freq: freq,
		bins: bins,
		signal: signal,
		power: power,
		psd: psd,
		reals: reals,
		imag: imag
	};

	return data;
}

exports.getPsd = getPsd;
exports.getFFT = getFFT;<commit_msg>Added anti-alias FIR filter.<commit_after>var sigGen = require('./sigGen.js');
var modulator = require('./modulator.js');
var fft = require('./fft.js');
var windowing = require('fft-windowing');
var filter = require('./filter.js');

function getPsd(centerFreq, span, resBw, vidBw, refLvl, scale, sigFreq) {
	var startFreq = centerFreq - span / 2.0;
	var NENBW = 1.5;
	var df = resBw / NENBW;
	var n = Math.ceil(span / df);
	var T = 1 / ( 2 * n * df); // Get sampling interval.

	var fftResult = getFFT(1, T, sigFreq, 0, n, startFreq);

	var psd = {
		refLvl: refLvl,
		scale: scale,
		resBw: resBw,
		vidBw: vidBw,
		freq: fftResult.freq,
		psd: fftResult.psd,
		power: fftResult.power
	};

	return psd;
}

function getFFT(A, T, f, phi, n, startFreq) {
    var t = new Array(n);
	var bins = new Array(n);
	for (var i = 0; i < n; i++) {
		t[i] = i * T;
		bins[i] = i;
	}

	// Generate the signal
	var noise = sigGen.noise(0, A/1000, n);
	var signal = sigGen.sinusoid(A, f, t, phi);
	signal = sigGen.add([signal, noise]);

	// Heterodyne with LO signal to shift the DFT to the specified start frequency
	if(startFreq != 0)
		signal = sigGen.multiply([signal, sigGen.sinusoid(1, startFreq, t, 0)]);

	// Anti-alias filter
	//var FIRCoeff = filter.calcFilter(1/T, startFreq, 40, 31, -150);
	//signal = filter.convFilter(FIRCoeff, signal, n, 1);

	// Apply window
	signal = windowing.hann(signal);
	var NENBW = 1.5; // Hann window normalized equivalent noise bandwidth

	// Subtract DC offset
	var avg = 0;
	for (var i = 0; i < n; i++)
		avg += signal[i];
	avg /= n;

	for (var i = 0; i < n; i++)
		signal[i] -= avg;

	// Compute the FFT in place
	var reals = signal.slice(0);
	var imag = new Array(n);
	for (var i = 0; i < n; i++)
		imag[i] = 0;
	fft.transform(reals, imag);

	// Get the single-sided power spectrum in dB and the corresponding frequencies
	var n2 = (n % 2 == 0) ? (n / 2) + 1 : (n + 1) / 2;
	var df = 1 / (n * T); // Frequency resolution (bin bandwidth)
	var power = new Array(n2);
	var psd = new Array(n2);
	var freq = new Array(n2);

	for (var i = 0; i < n2; i++) {
		var R2 = Math.pow(reals[i], 2);
		var I2 = Math.pow(imag[i], 2);
		var P = 2 * (R2 + I2) / Math.pow(n, 2);
		power[i] = 10 * Math.log(P) / Math.LN10; // Convert to dB
		psd[i] = 10 * Math.log(P / (df * NENBW)) / Math.LN10;
		freq[i] = startFreq + i * df;
	}

	var data = {
		time: t,
		freq: freq,
		bins: bins,
		signal: signal,
		power: power,
		psd: psd,
		reals: reals,
		imag: imag
	};

	return data;
}

exports.getPsd = getPsd;
exports.getFFT = getFFT;<|endoftext|>


<commit_before><commit_msg>Finished PM on ADT<commit_after><|endoftext|>


<commit_before>/**
 * Created by DT314 on 2017/4/10.
 */
import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class Percent extends Component {
    constructor(props) {
        super();

        this.state = {
            percent: 0
        };
    }

    /**
     * 百分比数字递增
     */
    numberChange() {
        if (this.state.percent < 100) {
            if (this.state.percent < this.props.endNum) {
                this.setState({
                    percent: this.state.percent + 1
                });
                const time = 300 / (this.props.endNum - this.state.percent);
                this.timer = setTimeout(() => this.numberChange(), time);
                return;
            }
            this.timer = setTimeout(() => this.numberChange(), 30);
        }
    }

    componentDidMount() {
        this.timer = setTimeout(() => this.numberChange(), 30);
    }

    componentWillUnmount() {
        clearTimeout(this.timer);
    }

    render() {
        const {move, endNum, style, className} = this.props;
        const {percent} = this.state;

        const widthStyle = move === true ? {
            width: endNum + '%',
            textAlign: 'right',
            ...style
        } : {
            ...style
        };

        return (

                <div className={`circular-progress-percent ${className}`}
                     style={widthStyle}>
                    {React.Children.map(this.props.children, function (child) {
                        return { child };
                    })}
                    { percent }%

        );
    }
};

Percent.propTypes = {
    className: PropTypes.string,
    style: PropTypes.object,

    endNum: PropTypes.number,
    move: PropTypes.bool
};

Percent.defaultProps = {
    className: '',
    style: {},

    endNum: 100,
    move: false
};<commit_msg>optimize Percent<commit_after>/**
 * Created by DT314 on 2017/4/10.
 */
import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class Percent extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            percent: 0
        };

    }

    /**
     * 百分比数字递增
     */
    numberChange() {
        if (this.state.percent < 100) {
            if (this.state.percent < this.props.endNum) {
                this.setState({
                    percent: this.state.percent + 1
                });
                const time = 300 / (this.props.endNum - this.state.percent);
                this.timer = setTimeout(() => this.numberChange(), time);
                return;
            }
            this.timer = setTimeout(() => this.numberChange(), 30);
        }
    }

    componentDidMount() {
        this.timer = setTimeout(() => this.numberChange(), 30);
    }

    componentWillUnmount() {
        clearTimeout(this.timer);
    }

    render() {

        const {move, endNum, style, className} = this.props,
            {percent} = this.state;

        const widthStyle = move === true ? {
            width: endNum + '%',
            textAlign: 'right',
            ...style
        } : {
            ...style
        };

        return (

                <div className={`circular-progress-percent ${className}`}
                     style={widthStyle}>
                    {React.Children.map(this.props.children, function (child) {
                        return {child};
                    })}
                    {percent}%

        );

    }
};

Percent.propTypes = {
    className: PropTypes.string,
    style: PropTypes.object,

    endNum: PropTypes.number,
    move: PropTypes.bool
};

Percent.defaultProps = {
    className: '',
    style: {},

    endNum: 100,
    move: false
};<|endoftext|>


<commit_before>'use strict';

const debug = require('debug')('stylelint:file-cache');
const fileEntryCache = require('file-entry-cache');
const getCacheFile = require('./getCacheFile');
const path = require('path');

const DEFAULT_CACHE_LOCATION = './.stylelintcache';
const DEFAULT_HASH = '';

/**
 * @param {string} [cacheLocation]
 * @param {string} [hashOfConfig]
 * @constructor
 */
class FileCache {
	constructor(cacheLocation = DEFAULT_CACHE_LOCATION, hashOfConfig = DEFAULT_HASH) {
		const cacheFile = path.resolve(
			getCacheFile(cacheLocation, process.cwd()),
		);

		debug(`Cache file is created at ${cacheFile}`);
		this._fileCache = fileEntryCache.create(cacheFile);
		this._hashOfConfig = hashOfConfig;
	}
		const cacheFile = path.resolve(
			getCacheFile(cacheLocation || DEFAULT_CACHE_LOCATION, process.cwd()),
		);

		debug(`Cache file is created at ${cacheFile}`);
		this._fileCache = fileEntryCache.create(cacheFile);
		this._hashOfConfig = hashOfConfig || DEFAULT_HASH;
	}

	/**
	 * @param {string} absoluteFilepath
	 * @return {boolean}
	 */
	hasFileChanged(absoluteFilepath) {
		// Get file descriptor compares current metadata against cached
		// one and stores the result to "changed" prop.w
		const descriptor = this._fileCache.getFileDescriptor(absoluteFilepath);
		const meta = descriptor.meta || {};
		const changed = descriptor.changed || meta.hashOfConfig !== this._hashOfConfig;

		if (!changed) {
			debug(`Skip linting ${absoluteFilepath}. File hasn't changed.`);
		}

		// Mutate file descriptor object and store config hash to each file.
		// Running lint with different config should invalidate the cache.
		if (meta.hashOfConfig !== this._hashOfConfig) {
			meta.hashOfConfig = this._hashOfConfig;
		}

		return changed;
	}

	reconcile() {
		this._fileCache.reconcile();
	}

	destroy() {
		this._fileCache.destroy();
	}

	/**
	 * @param {string} absoluteFilepath
	 */
	removeEntry(absoluteFilepath) {
		this._fileCache.removeEntry(absoluteFilepath);
	}
}

module.exports = FileCache;
<commit_msg>Remove duplicate code<commit_after>'use strict';

const debug = require('debug')('stylelint:file-cache');
const fileEntryCache = require('file-entry-cache');
const getCacheFile = require('./getCacheFile');
const path = require('path');

const DEFAULT_CACHE_LOCATION = './.stylelintcache';
const DEFAULT_HASH = '';

/**
 * @param {string} [cacheLocation]
 * @param {string} [hashOfConfig]
 * @constructor
 */
class FileCache {
	constructor(cacheLocation = DEFAULT_CACHE_LOCATION, hashOfConfig = DEFAULT_HASH) {
		const cacheFile = path.resolve(getCacheFile(cacheLocation, process.cwd()));

		debug(`Cache file is created at ${cacheFile}`);
		this._fileCache = fileEntryCache.create(cacheFile);
		this._hashOfConfig = hashOfConfig;
	}

	/**
	 * @param {string} absoluteFilepath
	 * @return {boolean}
	 */
	hasFileChanged(absoluteFilepath) {
		// Get file descriptor compares current metadata against cached
		// one and stores the result to "changed" prop.w
		const descriptor = this._fileCache.getFileDescriptor(absoluteFilepath);
		const meta = descriptor.meta || {};
		const changed = descriptor.changed || meta.hashOfConfig !== this._hashOfConfig;

		if (!changed) {
			debug(`Skip linting ${absoluteFilepath}. File hasn't changed.`);
		}

		// Mutate file descriptor object and store config hash to each file.
		// Running lint with different config should invalidate the cache.
		if (meta.hashOfConfig !== this._hashOfConfig) {
			meta.hashOfConfig = this._hashOfConfig;
		}

		return changed;
	}

	reconcile() {
		this._fileCache.reconcile();
	}

	destroy() {
		this._fileCache.destroy();
	}

	/**
	 * @param {string} absoluteFilepath
	 */
	removeEntry(absoluteFilepath) {
		this._fileCache.removeEntry(absoluteFilepath);
	}
}

module.exports = FileCache;
<|endoftext|>


<commit_before>//class
game.PlayerEntity = me.Entity.extend({
	//setting function
	init: function(x, y, settings) {
		//extending the function
		this._super(me.Entity, 'init', [x, y, {
			//setting the player image and its width and height
			image: "player",
			width: 64,
			height: 64,
			spritewidth: "64",
			spriteheight: "64",

			getShape: function(){
				//new shift
				//rectangle to make the character walk into it
				//setting it to the right size
				return (new me.Rect (0, 0, 64, 64)).toPolygon();
			}
		}]);
		//setting the velocity
		this.body.setVelocity(5, 20);
		//keeps track of which direction your character is going
		this.facing = "right";

		this.now = new Date().getTime();
		this.lastHit = this.now;
		this.lastAttack = new Date().getTime();
		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH)
		//adding the pictures of the characters
		this.renderable.addAnimation("idle" , [78]);
		this.renderable.addAnimation("walk", [117, 118 , 119 , 120 , 121 , 122 , 123 , 124 , 125], 80);
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);
		this.renderable.setCurrentAnimation("idle");
	},

	update: function(delta){
		this.now = new Date().getTime();
		//checking if the right key is pressed
		if (me.input.isKeyPressed("right")) {
			//adds to  the position of my x by the velocity defined above in
			//setVelocity() and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;

			//flipping the character
			this.facing = "right";
			this.flipX(true);

			//if other key is pressed it wont work
		}else if (me.input.isKeyPressed("left")){
			this.facing = "left";
			this.body.vel.x -=this.body.accel.x * me.timer.tick;
			this.flipX(false);
		}else{
			this.body.vel.x = 0;
		}
if(me.input.isKeyPressed("jump") && !this.body.jumping && !this.body.falling) {
this.jumping = true;
this.body.vel.y -= this.body.accel.y * me.timer.tick;
}

if(me.input.isKeyPressed("attack")){

	if(!this.renderable.isCurrentAnimation("attack")){
		console.log(!this.renderable.isCurrentAnimation("attack"));
		//sets the current animation to attack and once that is over
	//goes back to the idle animation
	this.renderable.setCurrentAnimation("attack" , "idle");
	//makes it so that the next time we start this sequence we begin
	//from the first animation, not wherever we left off when we
	//switched to another animation
	this.renderable.setAnimationFrame();
	}
}

		else if(this.body.vel.x !== 0 && !this.renderable.isCurrentAnimation("attack")){
			//sets the animation of walking
			if(!this.renderable.isCurrentAnimation("walk")){
				this.renderable.setCurrentAnimation("walk");
			}
		}else if (!this.renderable.isCurrentAnimation("attack")){
			//when no keys are pressed the character sets to idle
			this.renderable.setCurrentAnimation("idle");
		}

	me.collision.check(this, true, this.collideHandler.bind(this), true);
		//updating the game
		this.body.update(delta);

		this._super(me.Entity, "update", [delta]);
		return true;
	},

		collideHandler: function(response) {
		if(response.b.type==='EnemyBaseEntity') {
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;

if (ydif<-40 && xdif< 70 && xdif>-35){
this.body.falling = false;
this.body.vel.y = -1;
}

			else if(xdif>-35 && this.facing==='right' && (xdif<0 && ydif>-0)) {
				this.body.vel.x = 0;
				this.pos.x = this.pos.x -1;
			}else if(xdif<70 && this.facing==='left' && xdif>0) {
				this.body.vel.x = 0;
				this.pos.x = this.pos.x +1;

			}
			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit>= 1000){
				this.lastHit = this.now;
				response.b.loseHealth();
			}
		}
	}
});

game.EnemyBaseEntity = me.Entity.extend({
	init: function(x , y, settings) {
		this._super(me.Entity, 'init', [x, y, {
			image: "tower",
			width: 100,
			height: 100,
			spritewidth: "100",
			spriteheight: "100",
			getShape: function() {
				return (new me.Rect(0, 0, 100, 80)).toPolygon();
			}
		}]);

		this.broken = false;
		this.health = 10;
		this.alwaysUpdate = true;
		this.body.onCollision = this.onCollision.bind(this);
		this.type = "EnemyBaseEntity";
		this.renderable.addAnimation("idle" , [0]);
		this.renderable.addAnimation("broken" , [1]);
		this.renderable.setCurrentAnimation("idle");
	},

	update:function(delta){
		if (this.health<=0) {
			this.broken = true;
			this.renderable.setCurrentAnimation("broken");
		}
		this.body.update(delta);
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	onCollision: function(){

	}
});

game.PlayerBaseEntity = me.Entity.extend({
	init: function(x , y, settings) {
		this._super(me.Entity, 'init', [x, y, {
			image: "tower",
			width: 100,
			height: 100,
			spritewidth: "100",
			spriteheight: "100",
			getShape: function() {
				return (new me.Rect(0, 0, 100, 80)).toPolygon();
			}
		}]);

		this.broken = false;
		this.health = 10;
		this.alwaysUpdate = true;
		this.body.onCollision = this.onCollision.bind(this);
		this.type = "PlayerBaseEntity";

		this.renderable.addAnimation("idle" , [0]);
		this.renderable.addAnimation("broken" , [1]);
		this.renderable.setCurrentAnimation("idle");
	},

	update:function(delta){
		if (this.health<=0) {
			this.broken = true;
			this.renderable.setCurrentAnimation("broken");
		}
		this.body.update(delta);
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	onCollision: function(){

	},
	loseHealth: function(){
		this.health--;
	}
});

game.EnemyCreep = me.Entity.extend({
	init: function(x, y, settings){
		this._super(me.Entity, 'init', [x, y, {
			image: "creep1",
			width: 32,
			height: 64,
			spritewidth: "32",
			spriteheight: "64",
			getShape: function(){
				return (new me.Rect(0, 0, 32, 64)).toPolygon();
			}
		}]);
		this.health = 10;
		this.alwaysUpdate = true;

		this.body.setVelocity(3, 20);
		this.type = "EnemyCreep";
		this.renderable.addAnimation("walk", [3, 4, 5], 80);
		this.renderable.setCurrentAnimation("walk");
	},
	update: function(){

	}
})

game.GameManager = Object.extend({
init: function(x, y, settings){
this.now = new Date().getTime();
this.lastCreep = new Date().getTime();
this.alwaysUpdate = true;
},
update: function(){
this.now = new Date().getTime();

if(Math.round(this.now/1000)%10 ===0 && (this.now - this.lastCreep >= 1000)){
	this.lastCreep = this.now;
	var creepe = me.pool.pull("EnemyCreep", 1000, 0, {});
	me.game.world.addChild(Creepe, 5);
	}
	return true;
}
});<commit_msg>Commented video 11<commit_after>//class
game.PlayerEntity = me.Entity.extend({
	//setting function
	init: function(x, y, settings) {
		//extending the function
		this._super(me.Entity, 'init', [x, y, {
			//setting the player image and its width and height
			image: "player",
			width: 64,
			height: 64,
			spritewidth: "64",
			spriteheight: "64",

			getShape: function(){
				//new shift
				//rectangle to make the character walk into it
				//setting it to the right size
				return (new me.Rect (0, 0, 64, 64)).toPolygon();
			}
		}]);
		//setting the velocity
		this.body.setVelocity(5, 20);
		//keeps track of which direction your character is going
		this.facing = "right";

		this.now = new Date().getTime();
		this.lastHit = this.now;
		this.lastAttack = new Date().getTime();
		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH)
		//adding the pictures of the characters
		this.renderable.addAnimation("idle" , [78]);
		this.renderable.addAnimation("walk", [117, 118 , 119 , 120 , 121 , 122 , 123 , 124 , 125], 80);
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);
		this.renderable.setCurrentAnimation("idle");
	},

	update: function(delta){
		this.now = new Date().getTime();
		//checking if the right key is pressed
		if (me.input.isKeyPressed("right")) {
			//adds to  the position of my x by the velocity defined above in
			//setVelocity() and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;

			//flipping the character
			this.facing = "right";
			this.flipX(true);

			//if other key is pressed it wont work
		}else if (me.input.isKeyPressed("left")){
			this.facing = "left";
			this.body.vel.x -=this.body.accel.x * me.timer.tick;
			this.flipX(false);
		}else{
			this.body.vel.x = 0;
		}
if(me.input.isKeyPressed("jump") && !this.body.jumping && !this.body.falling) {
this.jumping = true;
this.body.vel.y -= this.body.accel.y * me.timer.tick;
}

if(me.input.isKeyPressed("attack")){

	if(!this.renderable.isCurrentAnimation("attack")){
		console.log(!this.renderable.isCurrentAnimation("attack"));
		//sets the current animation to attack and once that is over
	//goes back to the idle animation
	this.renderable.setCurrentAnimation("attack" , "idle");
	//makes it so that the next time we start this sequence we begin
	//from the first animation, not wherever we left off when we
	//switched to another animation
	this.renderable.setAnimationFrame();
	}
}

		else if(this.body.vel.x !== 0 && !this.renderable.isCurrentAnimation("attack")){
			//sets the animation of walking
			if(!this.renderable.isCurrentAnimation("walk")){
				this.renderable.setCurrentAnimation("walk");
			}
		}else if (!this.renderable.isCurrentAnimation("attack")){
			//when no keys are pressed the character sets to idle
			this.renderable.setCurrentAnimation("idle");
		}

	me.collision.check(this, true, this.collideHandler.bind(this), true);
		//updating the game
		this.body.update(delta);
		//reaches to the constructor of Entity
		this._super(me.Entity, "update", [delta]);
		return true;
	},

		collideHandler: function(response) {
		if(response.b.type==='EnemyBaseEntity') {
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;

if (ydif<-40 && xdif< 70 && xdif>-35){
this.body.falling = false;
this.body.vel.y = -1;
}

			else if(xdif>-35 && this.facing==='right' && (xdif<0 && ydif>-0)) {
				this.body.vel.x = 0;
				this.pos.x = this.pos.x -1;
			}else if(xdif<70 && this.facing==='left' && xdif>0) {
				this.body.vel.x = 0;
				this.pos.x = this.pos.x +1;

			}
			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit>= 1000){
				this.lastHit = this.now;
				response.b.loseHealth();
			}
		}
	}
});

game.EnemyBaseEntity = me.Entity.extend({
	init: function(x , y, settings) {
		this._super(me.Entity, 'init', [x, y, {

			//adding the tower image , and setting its size
			image: "tower",
			width: 100,
			height: 100,
			spritewidth: "100",
			spriteheight: "100",
			getShape: function() {
				//setting the rectangle thats for the tower
				return (new me.Rect(0, 0, 100, 80)).toPolygon();
			}
		}]);
		//the health of the power .. if you hit it more than 10 times , then it will blow up
		this.broken = false;
		this.health = 10;
		this.alwaysUpdate = true;
		this.body.onCollision = this.onCollision.bind(this);
		this.type = "EnemyBaseEntity";
		this.renderable.addAnimation("idle" , [0]);
		this.renderable.addAnimation("broken" , [1]);
		this.renderable.setCurrentAnimation("idle");
	},

	update:function(delta){
		//the tower when it has no hits yet , (health is at 0 )
		if (this.health<=0) {
			this.broken = true;
			this.renderable.setCurrentAnimation("broken");
		}
		this.body.update(delta);
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	onCollision: function(){

	}
});

game.PlayerBaseEntity = me.Entity.extend({
	init: function(x , y, settings) {
		// the constructor of Entity
		this._super(me.Entity, 'init', [x, y, {
			//choosing the second tower and setting its size
			image: "tower",
			width: 100,
			height: 100,
			spritewidth: "100",
			spriteheight: "100",
			getShape: function() {
				//setting the rectangle that the tower is in
				return (new me.Rect(0, 0, 100, 80)).toPolygon();
			}
		}]);
		//the health of the tower
		this.broken = false;
		this.health = 10;
		this.alwaysUpdate = true;
		this.body.onCollision = this.onCollision.bind(this);
		this.type = "PlayerBaseEntity";

		this.renderable.addAnimation("idle" , [0]);
		this.renderable.addAnimation("broken" , [1]);
		this.renderable.setCurrentAnimation("idle");
	},

	update:function(delta){
		//towers health at 0
		if (this.health<=0) {
			this.broken = true;
			this.renderable.setCurrentAnimation("broken");
		}
		this.body.update(delta);
		this._super(me.Entity, "update", [delta]);
		return true;
	},
	onCollision: function(){

	},
	loseHealth: function(){
		this.health--;
	}
});

game.EnemyCreep = me.Entity.extend({
	init: function(x, y, settings){
		this._super(me.Entity, 'init', [x, y, {
			image: "creep1",
			width: 32,
			height: 64,
			spritewidth: "32",
			spriteheight: "64",
			getShape: function(){
				return (new me.Rect(0, 0, 32, 64)).toPolygon();
			}
		}]);
		this.health = 10;
		this.alwaysUpdate = true;

		this.body.setVelocity(3, 20);
		this.type = "EnemyCreep";
		this.renderable.addAnimation("walk", [3, 4, 5], 80);
		this.renderable.setCurrentAnimation("walk");
	},
	update: function(){

	}
})

game.GameManager = Object.extend({
init: function(x, y, settings){
this.now = new Date().getTime();
this.lastCreep = new Date().getTime();
this.alwaysUpdate = true;
},
update: function(){
this.now = new Date().getTime();

if(Math.round(this.now/1000)%10 ===0 && (this.now - this.lastCreep >= 1000)){
	this.lastCreep = this.now;
	var creepe = me.pool.pull("EnemyCreep", 1000, 0, {});
	me.game.world.addChild(Creepe, 5);
	}
	return true;
}
});<|endoftext|>