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

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');

    if (!this.data) {
      return;
    }

    this.basePath = this.data.substring(0, this.data.lastIndexOf('data.json'));

    this.pages = null;
    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    // Elements
    this.canvasEl = null;
    this.canvas = null;

    // options
    this._fitscreen = false;
    this._opacity = 0.025;
    this._fitPanels = false;
    this._preloadPages = true;
    this._appendBasePath = true;

    // Dimensions
    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;

    this.loadedEvent = new CustomEvent('loaded');

    this._createCanvas();
    this._addEventListeners();

    // Start a loadingSpinner after 250 milliseconds.
    this._loadingTimer = window.setTimeout(this._showLoading.bind(this), 300);

    this._loadData().then(data => {
      this.data = data;
      this.pages = this.data.pages;

      this._createPagination();

      const hashPagination = this._getPaginationFromHash();
      if (hashPagination) {
        this.currentPageIndex = hashPagination[0];
        this.currentPanelIndex = hashPagination[1];
      }

      this._setPage(this.currentPageIndex)
        .then(_ => {
          this._recalcPage();
          this._drawPanels(this.currentPanelIndex);
          this._setPaginationHash();
          this._setActivePagination();
          this._positionView();
          if (this._preloadPages) {
            this._preloadNextPage();
          }

          // Clear the timer.
          if (this._loadingTimer) {
            window.clearTimeout(this._loadingTimer);
            this._hideLoading();
            this._loadingTimer = null;
          }

          this.dispatchEvent(this.loadedEvent);
        });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    Array.from(this.querySelectorAll('.pagination a')).forEach(link => link.classList.remove('active'));
    Array.from(this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]'))
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _showLoading() {
    const spinner = document.createElement('div');
    spinner.classList.add('manga-reader__spinner');
    this.appendChild(spinner);
    console.log('Show loading!!');
  }

  _hideLoading() {
    const spinner = document.querySelector('.manga-reader__spinner');
    if (spinner) {
      spinner.remove();
    }
  }

  _recalcPage() {
    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    const BCR = this.canvasEl.getBoundingClientRect();
    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen(on = true) {
    if (on) {
      this.canvasEl.style.height = this.screenHeight + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _loadImage(url) {

    // If the image url is an remote url
    // use that, otherwise append the base path of the
    // json.
    try {
      new URL(url);
    } catch(e) {
      if (this._appendBasePath) {
        url = this.basePath + url;
      }
    }

    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _preloadNextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return;
    }
    const nextPage = this.currentPageIndex + 1;
    const nextPageImage = this.pages[nextPage].image;

    this._loadImage(nextPageImage).then(_ => {
      console.log('preloaded page: '+ (nextPage + 1));
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    this._loadingTimer = window.setTimeout(this._showLoading.bind(this), 300);

    return this._setPage(this.currentPageIndex).then(_ => {
      if (this._loadingTimer) {
        window.clearTimeout(this._loadingTimer);
        this._loadingTimer = null;
        this._hideLoading();
      }

      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();

      if (this._preloadPages) {
        this._preloadNextPage();
      }
    });
  }

  previousPage() {
    if (this.currentPageIndex == 0) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const panel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];

    if (!panel) {
      return;
    }

    this._recalcPage();

    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (panel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (panel.x * this.pageDimensions.width / 100) + offsetX;

    const panelHeight = panel.height * this.pageDimensions.height / 100;

    if (this._fitscreen) {
      this.fitscreen(true);
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen(true);
    } else if (this._fitPanels && panelHeight > this.screenHeight) {
      console.log('auto resize');
      const desiredHeight = this.screenHeight;
      const resizeTo = this.pageDimensions.height * ((this.screenHeight - this.pageDimensions.top) / panelHeight);
      this.canvasEl.style.height = resizeTo + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }

    this._recalcPage();
    window.scrollTo(panelX, panelY);
  }
}
<commit_msg>add click event<commit_after>/**
 * @license MIT License
 *
 * Copyright (c) 2016 Rick Lancee @ricklancee
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

'use strict';

class MangaReader extends HTMLElement {

  createdCallback() {
    this.data = this.getAttribute('data');

    if (!this.data) {
      return;
    }

    this.basePath = this.data.substring(0, this.data.lastIndexOf('data.json'));

    this.pages = null;
    this.currentPageIndex = 0;
    this.currentPanelIndex = 0;

    // Elements
    this.canvasEl = null;
    this.canvas = null;

    // options
    this._fitscreen = false;
    this._opacity = 0.025;
    this._fitPanels = false;
    this._preloadPages = true;
    this._appendBasePath = true;

    // Dimensions
    this.pageDimensions = null;
    this.screenWidth = window.innerWidth;
    this.screenHeight = window.innerHeight;

    // Events
    this.loadedEvent = new CustomEvent('loaded');

    this._createCanvas();
    this._addEventListeners();

    // Start a loadingSpinner after 250 milliseconds.
    this._loadingTimer = window.setTimeout(this._showLoading.bind(this), 300);

    this._loadData().then(data => {
      this.data = data;
      this.pages = this.data.pages;

      this._createPagination();

      const hashPagination = this._getPaginationFromHash();
      if (hashPagination) {
        this.currentPageIndex = hashPagination[0];
        this.currentPanelIndex = hashPagination[1];
      }

      this._setPage(this.currentPageIndex)
        .then(_ => {
          this._recalcPage();
          this._drawPanels(this.currentPanelIndex);
          this._setPaginationHash();
          this._setActivePagination();
          this._positionView();
          if (this._preloadPages) {
            this._preloadNextPage();
          }

          // Clear the timer.
          if (this._loadingTimer) {
            window.clearTimeout(this._loadingTimer);
            this._hideLoading();
            this._loadingTimer = null;
          }

          this.dispatchEvent(this.loadedEvent);
        });
    });
  }

  _getPaginationFromHash() {
    let match = window.location.hash.match(/(\d+)-(\d+)/);

    if (!match)
      return false;

    const pageIndex = Math.min(
      Math.max(parseInt(parseInt(match[1]) - 1), 0), this.pages.length - 1);

    const maxPanels = this.pages[pageIndex].panels.length - 1;
    const panelIndex = Math.min(
      Math.max(parseInt(parseInt(match[2]) - 1), 0), maxPanels);

    return [pageIndex, panelIndex];
  }

  _setPaginationHash() {
    let currentPage = this.currentPageIndex + 1;
    let currentPanel = this.currentPanelIndex + 1;

    if (currentPage < 10)
      currentPage = '0' + currentPage;

    if (currentPanel < 10)
      currentPanel = '0' + currentPanel;

    window.history.pushState({}, null, '#' + currentPage + '-' + currentPanel);
  }

  _setActivePagination() {
    Array.from(this.querySelectorAll('.pagination a')).forEach(link => link.classList.remove('active'));
    Array.from(this.querySelectorAll('.pagination a[data-index="'+this.currentPageIndex+'"]'))
    .forEach(link => link.classList.add('active'));
  }

  _addEventListeners() {
    window.addEventListener('resize', _ => {
      this._recalcPage();
    });

    this.canvasEl.addEventListener('click', event => {
      this.nextPanel();
    });

    window.addEventListener('keydown', event => {
      if (event.keyCode === 39) { // right
        this.nextPanel();
        event.preventDefault();
      }
      if (event.keyCode === 37) { // left
        this.previousPanel();
        event.preventDefault();
      }
    });

    window.addEventListener('hashchange', event => {
      window.location.reload();
    });
  }

  _showLoading() {
    const spinner = document.createElement('div');
    spinner.classList.add('manga-reader__spinner');
    this.appendChild(spinner);
    console.log('Show loading!!');
  }

  _hideLoading() {
    const spinner = document.querySelector('.manga-reader__spinner');
    if (spinner) {
      spinner.remove();
    }
  }

  _recalcPage() {
    this.screenHeight = window.innerHeight;
    this.screenWidth = window.innerWidth;

    const BCR = this.canvasEl.getBoundingClientRect();
    this.pageDimensions = {
      top: BCR.top + window.scrollY,
      left: BCR.left + window.scrollX,
      width: BCR.width,
      height: BCR.height,
    };
  }

  fitscreen(on = true) {
    if (on) {
      this.canvasEl.style.height = this.screenHeight + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }
  }

  _loadData() {
    return new Promise((resolve, reject) => {
      fetch(this.data)
        .then(response => { return response.json() })
        .then(resolve)
        .catch(reject)
    });
  }

  _loadImage(url) {

    // If the image url is an remote url
    // use that, otherwise append the base path of the
    // json.
    try {
      new URL(url);
    } catch(e) {
      if (this._appendBasePath) {
        url = this.basePath + url;
      }
    }

    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        resolve(img);
      };

      img.onerror = _ => {
        reject('failed to load image');
      };

      img.src = url;
    });
  }

  _preloadNextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return;
    }
    const nextPage = this.currentPageIndex + 1;
    const nextPageImage = this.pages[nextPage].image;

    this._loadImage(nextPageImage).then(_ => {
      console.log('preloaded page: '+ (nextPage + 1));
    });
  }

  _createCanvas() {
    if (this.canvasEl) {
      return;
    }

    const canvas = document.createElement('canvas');
    this.canvasEl = canvas;
    this.canvas = canvas.getContext('2d');
    this.appendChild(canvas);
  }

  _createPagination() {
    const list = document.createElement('ol');
    list.classList.add('pagination');

    const maxPages = this.pages.length;

    for (var i = 0; i < maxPages; i++) {
      const li = document.createElement('li');
      const a = document.createElement('a');
      let page = (i + 1 < 10) ? '0' + (i + 1) : i + 1;
      a.innerHTML = page;
      a.classList.add('pagination-link');
      a.setAttribute('data-index', i);
      a.href = '#'+page+'-01';

      li.appendChild(a);
      list.appendChild(li);
    }

    this.insertBefore(list, this.firstChild);

    this.lastChild.parentNode.insertBefore(list.cloneNode(true), this.lastChild.nextSibling);
  }

  _setPage(index) {
      const imageUrl = this.pages[index].image;

      // Clear the canvas;
      this.canvas.clearRect(0, 0, this.canvasEl.width, this.canvasEl.height);

      return new Promise(resolve => {

        this._loadImage(imageUrl)
          .then(image => {
            this.currentImage = image;
            this._drawPage(image);
            resolve();
          });
      });
  }

  _drawPage(image) {
    this.canvas.save();
    this.canvasEl.width = image.width;
    this.canvasEl.height = image.height;
    this.canvas.globalAlpha = this._opacity;
    this.canvas.drawImage(image, 0, 0);
    this.canvas.globalAlpha = 1;
    this.canvas.restore();
  }

  _drawPanels(to) {
    const max = this.pages[this.currentPageIndex].panels.length;
    const len = Math.min(Math.max(parseInt(to + 1), 0), max);

    for (var i = 0; i < len; i++) {
      this._drawPanel(i);
    }
  }

  _drawPanel(index) {
    const path = this.pages[this.currentPageIndex].panels[index].path.split(',');
    const len = path.length;

    this.canvas.save();
    this.canvas.beginPath();
    for (var i = 0; i < len; i++) {
      const coards = path[i].split(' ');

      const x = coards[0] * this.canvasEl.width / 100;
      const y = coards[1] * this.canvasEl.height / 100;

      if (len == 0) {
        this.canvas.moveTo(x, y);
      } else {
        this.canvas.lineTo(x, y);
      }
    }
    this.canvas.closePath();
    this.canvas.clip();

    this.canvas.drawImage(this.currentImage, 0, 0);

    this.canvas.restore();
  }

  nextPanel() {
    const max = this.pages[this.currentPageIndex].panels.length - 1;

    this.currentPanelIndex++;
    if (this.currentPanelIndex > max) {

      if (this.currentPageIndex < this.pages.length - 1) {
        this.nextPage();
        console.log('Go to next page');
        return;
      }

      console.log('Last panel');
      this.currentPanelIndex = max;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  previousPanel() {
    this.currentPanelIndex--;

    if (this.currentPanelIndex < 0) {

      if (this.currentPageIndex > 0) {
        this.previousPage();
        console.log('go to previous page');
        return;
      }

      console.log('First panel');
      this.currentPanelIndex = 0;
      return;
    }

    this._drawPage(this.currentImage);
    this._drawPanels(this.currentPanelIndex);
    this._setPaginationHash();
    this._positionView();
  }

  nextPage() {
    if (this.currentPageIndex == this.pages.length - 1) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex++;
    this.currentPanelIndex = 0;

    this._loadingTimer = window.setTimeout(this._showLoading.bind(this), 300);

    return this._setPage(this.currentPageIndex).then(_ => {
      if (this._loadingTimer) {
        window.clearTimeout(this._loadingTimer);
        this._loadingTimer = null;
        this._hideLoading();
      }

      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();

      if (this._preloadPages) {
        this._preloadNextPage();
      }
    });
  }

  previousPage() {
    if (this.currentPageIndex == 0) {
      return new Promise(resolve => {});
    }

    this.currentPageIndex--;
    this.currentPanelIndex = this.pages[this.currentPageIndex].panels.length - 1;

    return this._setPage(this.currentPageIndex).then(_ => {
      this._recalcPage();
      this._drawPanels(this.currentPanelIndex);
      this._setPaginationHash();
      this._setActivePagination();
      this._positionView();
    });
  }

  _positionView() {
    const panel = this.pages[this.currentPageIndex].panels[this.currentPanelIndex];

    if (!panel) {
      return;
    }

    this._recalcPage();

    const offsetY = this.pageDimensions.top - 15;
    const offsetX = this.pageDimensions.left - 15;
    const panelY = (panel.y * this.pageDimensions.height / 100) + offsetY;
    const panelX = (panel.x * this.pageDimensions.width / 100) + offsetX;

    const panelHeight = panel.height * this.pageDimensions.height / 100;

    if (this._fitscreen) {
      this.fitscreen(true);
    } else if (this.pages[this.currentPageIndex].fitscreen) {
      this.fitscreen(true);
    } else if (this._fitPanels && panelHeight > this.screenHeight) {
      console.log('auto resize');
      const desiredHeight = this.screenHeight;
      const resizeTo = this.pageDimensions.height * ((this.screenHeight - this.pageDimensions.top) / panelHeight);
      this.canvasEl.style.height = resizeTo + 'px';
      this.canvasEl.style.width = 'auto';
    } else {
      this.canvasEl.style.height = '';
      this.canvasEl.style.width = '';
    }

    this._recalcPage();
    window.scrollTo(panelX, panelY);
  }
}
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var mongojs = require('mongojs');
var config = require('../config');
var DB = mongojs(config.DB);
var dataList = [
  {
    'db':DB.collection('municipalities'),
    'file': 'init/data/municipalities.json'
  },
  {
    'db':DB.collection('departments'),
    'file': 'init/data/departments.json'
  },
  {
    'db':DB.collection('journals'),
    'file': 'init/data/journals.json'
  },
  {
    'db':DB.collection('recruitments'),
    'file': 'init/data/recruitments.json'
  }
];

function getData(opts, callback){
  fs.readFile(opts.file, function(err, data){
    if(err){
      return callback(err, null);
    } else {
      opts.data = JSON.parse(data.toString());
      return callback(null, opts);
    }
  });
}

function addDocument(db, post){
  db.save(post, function (err, success) {
    if(err){
      console.error(err);
    } else {
      console.log(success);
    }
  });
}

function addDocuments(err, opts){
  if(err){
    console.error(err);
  } else {
    opts.db.drop();
    opts.data.forEach(function(document){
      addDocument(opts.db, document);
    });
  }
}

//Imports all init data
dataList.forEach(function(opts){
  getData(opts, addDocuments);
});<commit_msg>Changes to please jscs<commit_after>'use strict';

var fs = require('fs');
var mongojs = require('mongojs');
var config = require('../config');
var DB = mongojs(config.DB);
var dataList = [
  {
    'db':DB.collection('municipalities'),
    'file': 'init/data/municipalities.json'
  },
  {
    'db':DB.collection('departments'),
    'file': 'init/data/departments.json'
  },
  {
    'db':DB.collection('journals'),
    'file': 'init/data/journals.json'
  },
  {
    'db':DB.collection('recruitments'),
    'file': 'init/data/recruitments.json'
  }
];

function getData(opts, callback) {
  fs.readFile(opts.file, function(err, data) {
    if (err) {
      return callback(err, null);
    } else {
      opts.data = JSON.parse(data.toString());
      return callback(null, opts);
    }
  });
}

function addDocument(db, post) {
  db.save(post, function(err, success) {
    if (err) {
      console.error(err);
    } else {
      console.log(success);
    }
  });
}

function addDocuments(err, opts) {
  if (err) {
    console.error(err);
  } else {
    opts.db.drop();
    opts.data.forEach(function(document) {
      addDocument(opts.db, document);
    });
  }
}

//Imports all init data
dataList.forEach(function(opts) {
  getData(opts, addDocuments);
});
<|endoftext|>


<commit_before><commit_msg>Notifier looking good<commit_after><|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'EasyDAO',
  extends: 'foam.dao.ProxyDAO',
  implements: [ 'foam.mlang.Expressions' ],

  documentation: function() {/*
    Facade for easily creating decorated DAOs.

    Most DAOs are most easily created and configured with EasyDAO.
    Simply require foam.dao.EasyDAO and create() with the flags
    to indicate what behavior you're looking for. Under the hood, EasyDAO
    will create one or more DAO instances to service your requirements and then
  */},

  requires: [
    'foam.box.Context',
    'foam.box.HTTPBox',
    'foam.box.SessionClientBox',
    'foam.box.SocketBox',
    'foam.box.WebSocketBox',
    'foam.box.TimeoutBox',
    'foam.box.RetryBox',
    'foam.dao.CachingDAO',
    'foam.dao.CompoundDAODecorator',
    'foam.dao.ContextualizingDAO',
    'foam.dao.DecoratedDAO',
    'foam.dao.DeDupDAO',
    'foam.dao.GUIDDAO',
    'foam.dao.IDBDAO',
    'foam.dao.JDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.MDAO',
    'foam.dao.PromisedDAO',
    'foam.dao.RequestResponseClientDAO',
    'foam.dao.SequenceNumberDAO',
    'foam.dao.SyncDAO',
    'foam.dao.TimingDAO'
  ],

  imports: [ 'document' ],

  constants: {
    // Aliases for daoType
    ALIASES: {
      ARRAY:  'foam.dao.ArrayDAO',
      CLIENT: 'foam.dao.RequestResponseClientDAO',
      IDB:    'foam.dao.IDBDAO',
      LOCAL:  'foam.dao.LocalStorageDAO',
      MDAO:   'foam.dao.MDAO'
    }
  },

  properties: [
    {
      /** The developer-friendly name for this EasyDAO. */
      class: 'String',
      name: 'name',
      factory: function() { return this.of.id; }
    },
    {
      /** This is set automatically when you create an EasyDAO.
        @private */
      name: 'delegate',
      javaFactory: `
foam.dao.DAO delegate = getInnerDAO() == null ?
  new foam.dao.MapDAO(getX(), getOf()) :
  getInnerDAO();

if ( delegate instanceof foam.dao.MDAO ) setMdao((foam.dao.MDAO)delegate);

if ( getJournaled() ) {
  delegate = new foam.dao.JDAO(getX(), delegate, getJournalName());
}

if ( getPm() ) {
  delegate = new foam.dao.PMDAO(delegate);
}

if ( getGuid() && getSeqNo() ) {
  throw new RuntimeException("EasyDAO GUID and SeqNo are mutually exclusive");
}

if ( getGuid() ) {
  delegate = new foam.dao.GUIDDAO(delegate);
}

if ( getSeqNo() ) {
  delegate = new foam.dao.SequenceNumberDAO(delegate);
}

return delegate;
`
    },
    {
      class: 'Object',
      javaType: 'foam.dao.DAO',
      name: 'innerDAO'
    },
    {
      /** Have EasyDAO use a sequence number to index items. Note that
        .seqNo and .guid features are mutually
        exclusive. */
      class: 'Boolean',
      name: 'seqNo',
      value: false
    },
    {
      /** Have EasyDAO generate guids to index items. Note that .seqNo and .guid features are mutually exclusive. */
      class: 'Boolean',
      name: 'guid',
      label: 'GUID',
      value: false
    },
    {
      /** The property on your items to use to store the sequence number or guid. This is required for .seqNo or .guid mode. */
      name: 'seqProperty',
      generateJava: false,
      class: 'Property'
    },
    {
      /** Enable local in-memory caching of the DAO. */
      class: 'Boolean',
      name: 'cache',
      generateJava: false,
      value: false
    },
    {
      /** Enable value de-duplication to save memory when caching. */
      class: 'Boolean',
      name: 'dedup',
      generateJava: false,
      value: false,
    },
    {
      /** Keep a history of all state changes to the DAO. */
      class: 'Boolean',
      name: 'journaled',
      value: false
    },
    {
      class: 'String',
      name: 'journalName'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.Journal',
      generateJava: false,
      name: 'journal'
    },
    {
      /** Enable logging on the DAO. */
      class: 'Boolean',
      name: 'logging',
      value: false,
      generateJava: false
    },
    {
      /** Enable time tracking for concurrent DAO operations. */
      class: 'Boolean',
      name: 'timing',
      generateJava: false,
      value: false
    },
    {
      class: 'Boolean',
      name: 'pm',
      value: false
    },
    {
      /** Contextualize objects on .find, re-creating them with this EasyDAO's
        exports, as if they were children of this EasyDAO. */
      class: 'Boolean',
      generateJava: false,
      name: 'contextualize',
      value: false
    },
//     {
//       class: 'Boolean',
//       name: 'cloning',
//       value: false,
//       //documentation: "True to clone results on select"
//     },
    {
      /**

Selects the basic functionality this EasyDAO should provide.
        You can specify an instance of a DAO model definition such as
        MDAO, or a constant indicating your requirements.

Choices are:

IDB: Use IndexDB for storage.

LOCAL: Use local storage.

MDAO: Use non-persistent in-memory storage.

      */
      name: 'daoType',
      generateJava: false,
      value: 'foam.dao.IDBDAO'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.MDAO',
      name: 'mdao'
    },
    {
      /** Automatically generate indexes as necessary, if using an MDAO or cache. */
      class: 'Boolean',
      generateJava: false,
      name: 'autoIndex',
      value: false
    },
//     {
//       /** Creates an internal MigrationDAO and applies the given array of MigrationRule. */
//       class: 'FObjectArray',
//       name: 'migrationRules',
//       of: 'foam.core.dao.MigrationRule',
//     },
    {
      /** Turn on to activate synchronization with a server. Specify serverUri
        and syncProperty as well. */
      class: 'Boolean',
      name: 'syncWithServer',
      generateJava: false,
      value: false
    },
    {
      /** Turn on to enable remote listener support. Only useful with daoType = CLIENT. */
      class: 'Boolean',
      generateJava: false,
      name: 'remoteListenerSupport',
      value: false
    },
    {
      /** Setting to true activates polling, periodically checking in with
        the server. If sockets are used, polling is optional as the server
        can push changes to this client. */
      class: 'Boolean',
      generateJava: false,
      name: 'syncPolling',
      value: true
    },
    {
      /** Set to true if you are running this on a server, and clients will
        synchronize with this DAO. */
      class: 'Boolean',
      generateJava: false,
      name: 'isServer',
      value: false
    },
    {
      /** The property to synchronize on. This is typically an integer value
        indicating the version last seen on the remote. */
      name: 'syncProperty',
      generateJava: false
    },
    {
      /** Destination address for server. */
      name: 'serverBox',
      generateJava: false,
      factory: function() {
        // TODO: This should come from the server via a lookup from a NamedBox.
        return this.SessionClientBox.create({ delegate: this.RetryBox.create({ delegate:
          this.TimeoutBox.create({ delegate:
          this.remoteListenerSupport ?
              this.WebSocketBox.create({ uri: this.serviceName }) :
              this.HTTPBox.create({ url: this.serviceName })
        })})});
      }
    },
    {
      /** Simpler alternative than providing serverBox. */
      name: 'serviceName',
      generateJava: false
    },
    {
      class: 'FObjectArray',
      of: 'foam.dao.DAODecorator',
      generateJava: false,
      name: 'decorators'
    },
    {
      name: 'testData',
      generateJava: false
    }
  ],

  methods: [
    function init() {
      /**

On initialization, the EasyDAO creates an appropriate chain of
        internal EasyDAO instances based on the EasyDAO
        property settings.

This process is transparent to the developer, and you can use your
        EasyDAO like any other DAO.

      */
      this.SUPER.apply(this, arguments);

      var daoType = typeof this.daoType === 'string' ?
        this.ALIASES[this.daoType] || this.daoType :
        this.daoType;

      var params = { of: this.of };

      if ( daoType == 'foam.dao.RequestResponseClientDAO' ) {
        foam.assert(this.hasOwnProperty('serverBox') || this.serviceName, 'EasyDAO "client" type requires a serveBox or serviceName');

        // The RequestResonseClientDAO generates listener events locally
        // but with remoteListenerSupport, this isn't needed, so switch
        // to the regular ClientDAO instead.
        if ( this.remoteListenerSupport ) {
          daoType = 'foam.dao.ClientDAO';
        }

        params.delegate = this.serverBox;
      }

      var daoModel = typeof daoType === 'string' ?
        this.lookup(daoType) || global[daoType] :
        daoType;

      if ( ! daoModel ) {
        this.warn(
          "EasyDAO: Unknown DAO Type.  Add '" + daoType + "' to requires: list."
        );
      }

      if ( this.name && daoModel.getAxiomByName('name') ) params.name = this.name;
      if ( daoModel.getAxiomByName('autoIndex') ) params.autoIndex = this.autoIndex;
      //if ( this.seqNo || this.guid ) params.property = this.seqProperty;

      var dao = daoModel.create(params, this.__subContext__);

      // Not used by decorators.
      delete params['name'];

      if ( this.MDAO.isInstance(dao) ) {
        this.mdao = dao;
        if ( this.dedup ) dao = this.DeDupDAO.create({delegate: dao});
      } else {
//         if ( this.migrationRules && this.migrationRules.length ) {
//           dao = this.MigrationDAO.create({
//             delegate: dao,
//             rules: this.migrationRules,
//             name: this.model.id + "_" + daoModel.id + "_" + this.name
//           });
//         }
        if ( this.cache ) {
          this.mdao = this.MDAO.create({of: params.of});
          dao = this.CachingDAO.create({
            cache: this.dedup ?
              this.mdao :
              this.DeDupDAO.create({delegate: this.mdao}),
            src: dao,
            of: this.model});
        }
      }

      if ( this.journal ) {
        dao = this.JDAO.create({
          delegate: dao,
          journal: this.journal
        });
      }

      if ( this.seqNo && this.guid ) throw "EasyDAO 'seqNo' and 'guid' features are mutually exclusive.";

      if ( this.seqNo ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.SequenceNumberDAO.create(args);
      }

      if ( this.guid ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.GUIDDAO.create(args);
      }

      var cls = this.of;

      if ( this.syncWithServer && this.isServer ) throw "isServer and syncWithServer are mutually exclusive.";

      if ( this.syncWithServer || this.isServer ) {
        if ( ! this.syncProperty ) {
          this.syncProperty = cls.SYNC_PROPERTY;
          if ( ! this.syncProperty ) {
            throw "EasyDAO sync with class " + cls.id + " invalid. Sync requires a sync property be set, or be of a class including a property 'sync_property'.";
          }
        }
      }

      if ( this.syncWithServer ) {
        foam.assert(this.serverBox, 'syncWithServer requires serverBox');

        dao = this.SyncDAO.create({
          remoteDAO: this.RequestResponseClientDAO.create({
              name: this.name,
              delegate: this.serverBox
          }, boxContext),
          syncProperty: this.syncProperty,
          delegate: dao,
          pollingFrequency: 1000
        });
        dao.syncRecordDAO = foam.dao.EasyDAO.create({
          of: dao.SyncRecord,
          cache: true,
          daoType: this.daoType,
          name: this.name + '_SyncRecords'
        });
      }

//       if ( this.isServer ) {
//         dao = this.VersionNoDAO.create({
//           delegate: dao,
//           property: this.syncProperty,
//           version: 2
//         });
//       }

      if ( this.contextualize ) {
        dao = this.ContextualizingDAO.create({delegate: dao});
      }

      if ( this.decorators.length ) {
        var decorated = this.DecoratedDAO.create({
          decorator: this.CompoundDAODecorator.create({
            decorators: this.decorators
          }),
          delegate: dao
        });
        dao = decorated;
      }

      if ( this.timing  ) {
        dao = this.TimingDAO.create({ name: this.name + 'DAO', delegate: dao });
      }

      if ( this.logging ) {
        dao = this.LoggingDAO.create({ delegate: dao });
      }

      var self = this;

      if ( decorated ) decorated.dao = dao;

      if ( this.testData ) {
        var delegate = dao;

        dao = this.PromisedDAO.create({
          promise: new Promise(function(resolve, reject) {
            delegate.select(self.COUNT()).then(function(c) {
              // Only load testData if DAO is empty
              if ( c.value ) {
                resolve(delegate);
                return;
              }

              self.log("Loading test data");
              Promise.all(foam.json.parse(self.testData, self.of, self).map(
                function(o) { return delegate.put(o); }
              )).then(function() {
                self.log("Loaded", self.testData.length, "records.");
                resolve(delegate);
              }, reject);
            });
          })
        });
      }

      this.delegate = dao;
    },

    /** Only relevant if cache is true or if daoType
       was set to MDAO, but harmless otherwise. Generates an index
       for a query over all specified properties together.
       @param var_args specify any number of Properties to be indexed.
    */
    {
      name: 'addPropertyIndex',
      returns: 'foam.dao.EasyDAO',
      javaReturns: 'foam.dao.EasyDAO',
      args: [ { javaType: 'foam.core.PropertyInfo', name: 'prop' } ],
      code:     function addPropertyIndex() {
        this.mdao && this.mdao.addPropertyIndex.apply(this.mdao, arguments);
        return this;
      },
      javaCode: `
if ( getMdao() != null ) {
  getMdao().addIndex(prop);
}
return this;
`
    },

    /** Only relevant if cache is true or if daoType
      was set to MDAO, but harmless otherwise. Adds an existing index
      to the MDAO.
      @param index The index to add.
    */
    {
      name: 'addIndex',
      returns: 'foam.dao.EasyDAO',
      javaReturns: 'foam.dao.EasyDAO',
      args: [ { javaType: 'foam.dao.index.Index', name: 'index' } ],
      code: function addIndex(index) {
        this.mdao && this.mdao.addIndex.apply(this.mdao, arguments);
        return this;
      },
      javaCode: `
if ( getMdao() != null ) {
  getMdao().addIndex(index);
}
return this;
`
    }
  ]
});
<commit_msg>Java EasyDAO: Move PM decorator to be outermost decorator.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'EasyDAO',
  extends: 'foam.dao.ProxyDAO',
  implements: [ 'foam.mlang.Expressions' ],

  documentation: function() {/*
    Facade for easily creating decorated DAOs.

    Most DAOs are most easily created and configured with EasyDAO.
    Simply require foam.dao.EasyDAO and create() with the flags
    to indicate what behavior you're looking for. Under the hood, EasyDAO
    will create one or more DAO instances to service your requirements and then
  */},

  requires: [
    'foam.box.Context',
    'foam.box.HTTPBox',
    'foam.box.SessionClientBox',
    'foam.box.SocketBox',
    'foam.box.WebSocketBox',
    'foam.box.TimeoutBox',
    'foam.box.RetryBox',
    'foam.dao.CachingDAO',
    'foam.dao.CompoundDAODecorator',
    'foam.dao.ContextualizingDAO',
    'foam.dao.DecoratedDAO',
    'foam.dao.DeDupDAO',
    'foam.dao.GUIDDAO',
    'foam.dao.IDBDAO',
    'foam.dao.JDAO',
    'foam.dao.LoggingDAO',
    'foam.dao.MDAO',
    'foam.dao.PromisedDAO',
    'foam.dao.RequestResponseClientDAO',
    'foam.dao.SequenceNumberDAO',
    'foam.dao.SyncDAO',
    'foam.dao.TimingDAO'
  ],

  imports: [ 'document' ],

  constants: {
    // Aliases for daoType
    ALIASES: {
      ARRAY:  'foam.dao.ArrayDAO',
      CLIENT: 'foam.dao.RequestResponseClientDAO',
      IDB:    'foam.dao.IDBDAO',
      LOCAL:  'foam.dao.LocalStorageDAO',
      MDAO:   'foam.dao.MDAO'
    }
  },

  properties: [
    {
      /** The developer-friendly name for this EasyDAO. */
      class: 'String',
      name: 'name',
      factory: function() { return this.of.id; }
    },
    {
      /** This is set automatically when you create an EasyDAO.
        @private */
      name: 'delegate',
      javaFactory: `
foam.dao.DAO delegate = getInnerDAO() == null ?
  new foam.dao.MapDAO(getX(), getOf()) :
  getInnerDAO();

if ( delegate instanceof foam.dao.MDAO ) setMdao((foam.dao.MDAO)delegate);

if ( getJournaled() ) {
  delegate = new foam.dao.JDAO(getX(), delegate, getJournalName());
}

if ( getGuid() && getSeqNo() ) {
  throw new RuntimeException("EasyDAO GUID and SeqNo are mutually exclusive");
}

if ( getGuid() ) {
  delegate = new foam.dao.GUIDDAO(delegate);
}

if ( getSeqNo() ) {
  delegate = new foam.dao.SequenceNumberDAO(delegate);
}

if ( getPm() ) {
  delegate = new foam.dao.PMDAO(delegate);
}

return delegate;
`
    },
    {
      class: 'Object',
      javaType: 'foam.dao.DAO',
      name: 'innerDAO'
    },
    {
      /** Have EasyDAO use a sequence number to index items. Note that
        .seqNo and .guid features are mutually
        exclusive. */
      class: 'Boolean',
      name: 'seqNo',
      value: false
    },
    {
      /** Have EasyDAO generate guids to index items. Note that .seqNo and .guid features are mutually exclusive. */
      class: 'Boolean',
      name: 'guid',
      label: 'GUID',
      value: false
    },
    {
      /** The property on your items to use to store the sequence number or guid. This is required for .seqNo or .guid mode. */
      name: 'seqProperty',
      generateJava: false,
      class: 'Property'
    },
    {
      /** Enable local in-memory caching of the DAO. */
      class: 'Boolean',
      name: 'cache',
      generateJava: false,
      value: false
    },
    {
      /** Enable value de-duplication to save memory when caching. */
      class: 'Boolean',
      name: 'dedup',
      generateJava: false,
      value: false,
    },
    {
      /** Keep a history of all state changes to the DAO. */
      class: 'Boolean',
      name: 'journaled',
      value: false
    },
    {
      class: 'String',
      name: 'journalName'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.Journal',
      generateJava: false,
      name: 'journal'
    },
    {
      /** Enable logging on the DAO. */
      class: 'Boolean',
      name: 'logging',
      value: false,
      generateJava: false
    },
    {
      /** Enable time tracking for concurrent DAO operations. */
      class: 'Boolean',
      name: 'timing',
      generateJava: false,
      value: false
    },
    {
      class: 'Boolean',
      name: 'pm',
      value: false
    },
    {
      /** Contextualize objects on .find, re-creating them with this EasyDAO's
        exports, as if they were children of this EasyDAO. */
      class: 'Boolean',
      generateJava: false,
      name: 'contextualize',
      value: false
    },
//     {
//       class: 'Boolean',
//       name: 'cloning',
//       value: false,
//       //documentation: "True to clone results on select"
//     },
    {
      /**

Selects the basic functionality this EasyDAO should provide.
        You can specify an instance of a DAO model definition such as
        MDAO, or a constant indicating your requirements.

Choices are:

IDB: Use IndexDB for storage.

LOCAL: Use local storage.

MDAO: Use non-persistent in-memory storage.

      */
      name: 'daoType',
      generateJava: false,
      value: 'foam.dao.IDBDAO'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.dao.MDAO',
      name: 'mdao'
    },
    {
      /** Automatically generate indexes as necessary, if using an MDAO or cache. */
      class: 'Boolean',
      generateJava: false,
      name: 'autoIndex',
      value: false
    },
//     {
//       /** Creates an internal MigrationDAO and applies the given array of MigrationRule. */
//       class: 'FObjectArray',
//       name: 'migrationRules',
//       of: 'foam.core.dao.MigrationRule',
//     },
    {
      /** Turn on to activate synchronization with a server. Specify serverUri
        and syncProperty as well. */
      class: 'Boolean',
      name: 'syncWithServer',
      generateJava: false,
      value: false
    },
    {
      /** Turn on to enable remote listener support. Only useful with daoType = CLIENT. */
      class: 'Boolean',
      generateJava: false,
      name: 'remoteListenerSupport',
      value: false
    },
    {
      /** Setting to true activates polling, periodically checking in with
        the server. If sockets are used, polling is optional as the server
        can push changes to this client. */
      class: 'Boolean',
      generateJava: false,
      name: 'syncPolling',
      value: true
    },
    {
      /** Set to true if you are running this on a server, and clients will
        synchronize with this DAO. */
      class: 'Boolean',
      generateJava: false,
      name: 'isServer',
      value: false
    },
    {
      /** The property to synchronize on. This is typically an integer value
        indicating the version last seen on the remote. */
      name: 'syncProperty',
      generateJava: false
    },
    {
      /** Destination address for server. */
      name: 'serverBox',
      generateJava: false,
      factory: function() {
        // TODO: This should come from the server via a lookup from a NamedBox.
        return this.SessionClientBox.create({ delegate: this.RetryBox.create({ delegate:
          this.TimeoutBox.create({ delegate:
          this.remoteListenerSupport ?
              this.WebSocketBox.create({ uri: this.serviceName }) :
              this.HTTPBox.create({ url: this.serviceName })
        })})});
      }
    },
    {
      /** Simpler alternative than providing serverBox. */
      name: 'serviceName',
      generateJava: false
    },
    {
      class: 'FObjectArray',
      of: 'foam.dao.DAODecorator',
      generateJava: false,
      name: 'decorators'
    },
    {
      name: 'testData',
      generateJava: false
    }
  ],

  methods: [
    function init() {
      /**

On initialization, the EasyDAO creates an appropriate chain of
        internal EasyDAO instances based on the EasyDAO
        property settings.

This process is transparent to the developer, and you can use your
        EasyDAO like any other DAO.

      */
      this.SUPER.apply(this, arguments);

      var daoType = typeof this.daoType === 'string' ?
        this.ALIASES[this.daoType] || this.daoType :
        this.daoType;

      var params = { of: this.of };

      if ( daoType == 'foam.dao.RequestResponseClientDAO' ) {
        foam.assert(this.hasOwnProperty('serverBox') || this.serviceName, 'EasyDAO "client" type requires a serveBox or serviceName');

        // The RequestResonseClientDAO generates listener events locally
        // but with remoteListenerSupport, this isn't needed, so switch
        // to the regular ClientDAO instead.
        if ( this.remoteListenerSupport ) {
          daoType = 'foam.dao.ClientDAO';
        }

        params.delegate = this.serverBox;
      }

      var daoModel = typeof daoType === 'string' ?
        this.lookup(daoType) || global[daoType] :
        daoType;

      if ( ! daoModel ) {
        this.warn(
          "EasyDAO: Unknown DAO Type.  Add '" + daoType + "' to requires: list."
        );
      }

      if ( this.name && daoModel.getAxiomByName('name') ) params.name = this.name;
      if ( daoModel.getAxiomByName('autoIndex') ) params.autoIndex = this.autoIndex;
      //if ( this.seqNo || this.guid ) params.property = this.seqProperty;

      var dao = daoModel.create(params, this.__subContext__);

      // Not used by decorators.
      delete params['name'];

      if ( this.MDAO.isInstance(dao) ) {
        this.mdao = dao;
        if ( this.dedup ) dao = this.DeDupDAO.create({delegate: dao});
      } else {
//         if ( this.migrationRules && this.migrationRules.length ) {
//           dao = this.MigrationDAO.create({
//             delegate: dao,
//             rules: this.migrationRules,
//             name: this.model.id + "_" + daoModel.id + "_" + this.name
//           });
//         }
        if ( this.cache ) {
          this.mdao = this.MDAO.create({of: params.of});
          dao = this.CachingDAO.create({
            cache: this.dedup ?
              this.mdao :
              this.DeDupDAO.create({delegate: this.mdao}),
            src: dao,
            of: this.model});
        }
      }

      if ( this.journal ) {
        dao = this.JDAO.create({
          delegate: dao,
          journal: this.journal
        });
      }

      if ( this.seqNo && this.guid ) throw "EasyDAO 'seqNo' and 'guid' features are mutually exclusive.";

      if ( this.seqNo ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.SequenceNumberDAO.create(args);
      }

      if ( this.guid ) {
        var args = {__proto__: params, delegate: dao, of: this.of};
        if ( this.seqProperty ) args.property = this.seqProperty;
        dao = this.GUIDDAO.create(args);
      }

      var cls = this.of;

      if ( this.syncWithServer && this.isServer ) throw "isServer and syncWithServer are mutually exclusive.";

      if ( this.syncWithServer || this.isServer ) {
        if ( ! this.syncProperty ) {
          this.syncProperty = cls.SYNC_PROPERTY;
          if ( ! this.syncProperty ) {
            throw "EasyDAO sync with class " + cls.id + " invalid. Sync requires a sync property be set, or be of a class including a property 'sync_property'.";
          }
        }
      }

      if ( this.syncWithServer ) {
        foam.assert(this.serverBox, 'syncWithServer requires serverBox');

        dao = this.SyncDAO.create({
          remoteDAO: this.RequestResponseClientDAO.create({
              name: this.name,
              delegate: this.serverBox
          }, boxContext),
          syncProperty: this.syncProperty,
          delegate: dao,
          pollingFrequency: 1000
        });
        dao.syncRecordDAO = foam.dao.EasyDAO.create({
          of: dao.SyncRecord,
          cache: true,
          daoType: this.daoType,
          name: this.name + '_SyncRecords'
        });
      }

//       if ( this.isServer ) {
//         dao = this.VersionNoDAO.create({
//           delegate: dao,
//           property: this.syncProperty,
//           version: 2
//         });
//       }

      if ( this.contextualize ) {
        dao = this.ContextualizingDAO.create({delegate: dao});
      }

      if ( this.decorators.length ) {
        var decorated = this.DecoratedDAO.create({
          decorator: this.CompoundDAODecorator.create({
            decorators: this.decorators
          }),
          delegate: dao
        });
        dao = decorated;
      }

      if ( this.timing  ) {
        dao = this.TimingDAO.create({ name: this.name + 'DAO', delegate: dao });
      }

      if ( this.logging ) {
        dao = this.LoggingDAO.create({ delegate: dao });
      }

      var self = this;

      if ( decorated ) decorated.dao = dao;

      if ( this.testData ) {
        var delegate = dao;

        dao = this.PromisedDAO.create({
          promise: new Promise(function(resolve, reject) {
            delegate.select(self.COUNT()).then(function(c) {
              // Only load testData if DAO is empty
              if ( c.value ) {
                resolve(delegate);
                return;
              }

              self.log("Loading test data");
              Promise.all(foam.json.parse(self.testData, self.of, self).map(
                function(o) { return delegate.put(o); }
              )).then(function() {
                self.log("Loaded", self.testData.length, "records.");
                resolve(delegate);
              }, reject);
            });
          })
        });
      }

      this.delegate = dao;
    },

    /** Only relevant if cache is true or if daoType
       was set to MDAO, but harmless otherwise. Generates an index
       for a query over all specified properties together.
       @param var_args specify any number of Properties to be indexed.
    */
    {
      name: 'addPropertyIndex',
      returns: 'foam.dao.EasyDAO',
      javaReturns: 'foam.dao.EasyDAO',
      args: [ { javaType: 'foam.core.PropertyInfo', name: 'prop' } ],
      code:     function addPropertyIndex() {
        this.mdao && this.mdao.addPropertyIndex.apply(this.mdao, arguments);
        return this;
      },
      javaCode: `
if ( getMdao() != null ) {
  getMdao().addIndex(prop);
}
return this;
`
    },

    /** Only relevant if cache is true or if daoType
      was set to MDAO, but harmless otherwise. Adds an existing index
      to the MDAO.
      @param index The index to add.
    */
    {
      name: 'addIndex',
      returns: 'foam.dao.EasyDAO',
      javaReturns: 'foam.dao.EasyDAO',
      args: [ { javaType: 'foam.dao.index.Index', name: 'index' } ],
      code: function addIndex(index) {
        this.mdao && this.mdao.addIndex.apply(this.mdao, arguments);
        return this;
      },
      javaCode: `
if ( getMdao() != null ) {
  getMdao().addIndex(index);
}
return this;
`
    }
  ]
});
<|endoftext|>


<commit_before>/* GAME.JS
 *
 */

"use strict";

////////////////////////////////////////////////////////////////////////////////

var player = null;

function gameInit(){
	// Test Objects
	var g = new GridObject(1,1, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	var g = new GridObject(0,0, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	var g = new GridObject(2,3, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	// Define Player
	player = new GameObject(PlayerThink, 4, 0.5, new GameAsset(0.8,0.8,[],null));
	GameObject.gameObjects.push(player);
}

////////////////////////////////////////////////////////////////////////////////

function PlayerThink(player) {
	player.wx = 0;
	player.wy = 0;
	if (Keyboard.LEFT)  {
		player.wx = -1;
	}
	if (Keyboard.RIGHT) {
		player.wx = 1;
	}
	if (Keyboard.UP)    {
		player.wy = -1;
	}
	if (Keyboard.DOWN)  {
		player.wy = 1;
	}
}

////////////////////////////////////////////////////////////////////////////////

function gameUpdate(){
	draw();
	requestAnimationFrame(gameUpdate);

	// Move Camera
	Camera.x = Camera.x * 0.7 + player.x * 0.3;
	Camera.y = Camera.y * 0.7 + player.y * 0.3;
}
<commit_msg>renamed playerThink()<commit_after>/* GAME.JS
 *
 */

"use strict";

////////////////////////////////////////////////////////////////////////////////

var player = null;

function gameInit(){
	// Test Objects
	var g = new GridObject(1,1, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	var g = new GridObject(0,0, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	var g = new GridObject(2,3, new GameAsset(1,1,[[true]],null));
	GridObject.gridObjects.push(g);

	// Define Player
	player = new GameObject(playerThink, 4, 0.5, new GameAsset(0.8,0.8,[],null));
	GameObject.gameObjects.push(player);
}

////////////////////////////////////////////////////////////////////////////////

function playerThink(player) {
	player.wx = 0;
	player.wy = 0;
	if (Keyboard.LEFT)  {
		player.wx = -1;
	}
	if (Keyboard.RIGHT) {
		player.wx = 1;
	}
	if (Keyboard.UP)    {
		player.wy = -1;
	}
	if (Keyboard.DOWN)  {
		player.wy = 1;
	}
}

////////////////////////////////////////////////////////////////////////////////

function gameUpdate(){
	draw();
	requestAnimationFrame(gameUpdate);

	// Move Camera
	Camera.x = Camera.x * 0.7 + player.x * 0.3;
	Camera.y = Camera.y * 0.7 + player.y * 0.3;
}
<|endoftext|>


<commit_before>//     keymaster.js
//     (c) 2011 Thomas Fuchs
//     keymaster.js may be freely distributed under the MIT license.

;(function(global){
  var k,
    _handlers = {},
    _mods = { 16: false, 18: false, 17: false, 91: false },
    _scope = 'all',
    // modifier keys
    _MODIFIERS = {
      '⇧': 16, shift: 16,
      option: 18, '⌥': 18, alt: 18,
      ctrl: 17, control: 17,
      command: 91, '⌘': 91
    },
    // special keys
    _MAP = {
      backspace: 8, tab: 9, clear: 12,
      enter: 13, 'return': 13,
      esc: 27, escape: 27, space: 32,
      left: 37, up: 38,
      right: 39, down: 40,
      del: 46, 'delete': 46,
      home: 36, end: 35,
      pageup: 33, pagedown: 34,
      ',': 188, '.': 190, '/': 191,
      '`': 192, '-': 189, '=': 187,
      ';': 186, '\'': 222,
      '[': 219, ']': 221, '\\': 220
    };

  for(k=1;k<20;k++) _MODIFIERS['f'+k] = 111+k;

  // IE doesn't support Array#indexOf, so have a simple replacement
  function index(array, item){
    var i = array.length;
    while(i--) if(array[i]===item) return i;
    return -1;
  }

  // handle keydown event
  function dispatch(event){
    var key, tagName, handler, k, i, modifiersMatch;
    tagName = (event.target || event.srcElement).tagName;
    key = event.keyCode;

    // if a modifier key, set the key.<modifierkeyname> property to true and return
    if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
    if(key in _mods) {
      _mods[key] = true;
      // 'assignKey' from inside this closure is exported to window.key
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
      return;
    }

    // ignore keypressed in any elements that support keyboard data input
    if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') return;

    // abort if no potentially matching shortcuts found
    if (!(key in _handlers)) return;

    // for each potential shortcut
    for (i = 0; i < _handlers[key].length; i++) {
      handler = _handlers[key][i];

      // see if it's in the current scope
      if(handler.scope == _scope || handler.scope == 'all'){
        // check if modifiers match if any
        modifiersMatch = handler.mods.length > 0;
        for(k in _mods)
          if((!_mods[k] && index(handler.mods, +k) > -1) ||
            (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
        // call the handler and stop the event if neccessary
        if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
          if(handler.method(event, handler)===false){
            if(event.preventDefault) event.preventDefault();
              else event.returnValue = false;
            if(event.stopPropagation) event.stopPropagation();
            if(event.cancelBubble) event.cancelBubble = true;
          }
        }
      }
	}
  };

  // unset modifier keys on keyup
  function clearModifier(event){
    var key = event.keyCode, k;
    if(key == 93 || key == 224) key = 91;
    if(key in _mods) {
      _mods[key] = false;
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
    }
  };

  // parse and assign shortcut
  function assignKey(key, scope, method){
    var keys, mods, i, mi;
    if (method === undefined) {
      method = scope;
      scope = 'all';
    }
    key = key.replace(/\s/g,'');
    keys = key.split(',');

    if((keys[keys.length-1])=='')
      keys[keys.length-2] += ',';
    // for each shortcut
    for (i = 0; i < keys.length; i++) {
      // set modifier keys if any
      mods = [];
      key = keys[i].split('+');
      if(key.length > 1){
        mods = key.slice(0,key.length-1);
        for (mi = 0; mi < mods.length; mi++)
          mods[mi] = _MODIFIERS[mods[mi]];
        key = [key[key.length-1]];
      }
      // convert to keycode and...
      key = key[0]
      key = _MAP[key] || key.toUpperCase().charCodeAt(0);
      // ...store handler
      if (!(key in _handlers)) _handlers[key] = [];
      _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
    }
  };

  // initialize key.<modifier> to false
  for(k in _MODIFIERS) assignKey[k] = false;

  // set current scope (default 'all')
  function setScope(scope){ _scope = scope || 'all' };

  // cross-browser events
  function addEvent(object, event, method) {
    if (object.addEventListener)
      object.addEventListener(event, method, false);
    else if(object.attachEvent)
      object.attachEvent('on'+event, function(){ method(window.event) });
  };

  // set the handlers globally on document
  addEvent(document, 'keydown', dispatch);
  addEvent(document, 'keyup', clearModifier);

  // set window.key and window.key.setScope
  global.key = assignKey;
  global.key.setScope = setScope;

  if(typeof module !== 'undefined') module.exports = key;

})(this);
<commit_msg>Added a getScope method.<commit_after>//     keymaster.js
//     (c) 2011 Thomas Fuchs
//     keymaster.js may be freely distributed under the MIT license.

;(function(global){
  var k,
    _handlers = {},
    _mods = { 16: false, 18: false, 17: false, 91: false },
    _scope = 'all',
    // modifier keys
    _MODIFIERS = {
      '⇧': 16, shift: 16,
      option: 18, '⌥': 18, alt: 18,
      ctrl: 17, control: 17,
      command: 91, '⌘': 91
    },
    // special keys
    _MAP = {
      backspace: 8, tab: 9, clear: 12,
      enter: 13, 'return': 13,
      esc: 27, escape: 27, space: 32,
      left: 37, up: 38,
      right: 39, down: 40,
      del: 46, 'delete': 46,
      home: 36, end: 35,
      pageup: 33, pagedown: 34,
      ',': 188, '.': 190, '/': 191,
      '`': 192, '-': 189, '=': 187,
      ';': 186, '\'': 222,
      '[': 219, ']': 221, '\\': 220
    };

  for(k=1;k<20;k++) _MODIFIERS['f'+k] = 111+k;

  // IE doesn't support Array#indexOf, so have a simple replacement
  function index(array, item){
    var i = array.length;
    while(i--) if(array[i]===item) return i;
    return -1;
  }

  // handle keydown event
  function dispatch(event){
    var key, tagName, handler, k, i, modifiersMatch;
    tagName = (event.target || event.srcElement).tagName;
    key = event.keyCode;

    // if a modifier key, set the key.<modifierkeyname> property to true and return
    if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
    if(key in _mods) {
      _mods[key] = true;
      // 'assignKey' from inside this closure is exported to window.key
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
      return;
    }

    // ignore keypressed in any elements that support keyboard data input
    if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') return;

    // abort if no potentially matching shortcuts found
    if (!(key in _handlers)) return;

    // for each potential shortcut
    for (i = 0; i < _handlers[key].length; i++) {
      handler = _handlers[key][i];

      // see if it's in the current scope
      if(handler.scope == _scope || handler.scope == 'all'){
        // check if modifiers match if any
        modifiersMatch = handler.mods.length > 0;
        for(k in _mods)
          if((!_mods[k] && index(handler.mods, +k) > -1) ||
            (_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
        // call the handler and stop the event if neccessary
        if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
          if(handler.method(event, handler)===false){
            if(event.preventDefault) event.preventDefault();
              else event.returnValue = false;
            if(event.stopPropagation) event.stopPropagation();
            if(event.cancelBubble) event.cancelBubble = true;
          }
        }
      }
	}
  };

  // unset modifier keys on keyup
  function clearModifier(event){
    var key = event.keyCode, k;
    if(key == 93 || key == 224) key = 91;
    if(key in _mods) {
      _mods[key] = false;
      for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
    }
  };

  // parse and assign shortcut
  function assignKey(key, scope, method){
    var keys, mods, i, mi;
    if (method === undefined) {
      method = scope;
      scope = 'all';
    }
    key = key.replace(/\s/g,'');
    keys = key.split(',');

    if((keys[keys.length-1])=='')
      keys[keys.length-2] += ',';
    // for each shortcut
    for (i = 0; i < keys.length; i++) {
      // set modifier keys if any
      mods = [];
      key = keys[i].split('+');
      if(key.length > 1){
        mods = key.slice(0,key.length-1);
        for (mi = 0; mi < mods.length; mi++)
          mods[mi] = _MODIFIERS[mods[mi]];
        key = [key[key.length-1]];
      }
      // convert to keycode and...
      key = key[0]
      key = _MAP[key] || key.toUpperCase().charCodeAt(0);
      // ...store handler
      if (!(key in _handlers)) _handlers[key] = [];
      _handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
    }
  };

  // initialize key.<modifier> to false
  for(k in _MODIFIERS) assignKey[k] = false;

  // set current scope (default 'all')
  function setScope(scope){ _scope = scope || 'all' };
  function getScope(){ return scope || 'all' };

  // cross-browser events
  function addEvent(object, event, method) {
    if (object.addEventListener)
      object.addEventListener(event, method, false);
    else if(object.attachEvent)
      object.attachEvent('on'+event, function(){ method(window.event) });
  };

  // set the handlers globally on document
  addEvent(document, 'keydown', dispatch);
  addEvent(document, 'keyup', clearModifier);

  // set window.key and window.key.setScope
  global.key = assignKey;
  global.key.setScope = setScope;
  global.key.getScope = getScope;

  if(typeof module !== 'undefined') module.exports = key;

})(this);
<|endoftext|>


<commit_before>var cls = window.cls || ( window.cls = {} );

/**
  * @constructor
  * @extends ViewBase
  */

cls.CommandLineView = function(id, name, container_class, html, default_handler)
{

  var self = this;

  var __frame_index = -1;

  var __console_output = null;
  var __console_input = null;
  var __prefix = null;

  var submit_buffer = [];
  var line_buffer = [];
  var line_buffer_cursor = 0;

  var __selection_start = -1;

  var handleEval = function(xml, runtime_id, obj_id)
  {
    var value_type = xml.getNodeData('data-type');
    var status = xml.getNodeData('status');
    if( status == 'completed' )
    {
      var return_value = xml.getElementsByTagName('string')[0];
      if(return_value)
      {
        var value = return_value.firstChild && return_value.firstChild.nodeValue || '';
        if( !obj_id )
        {
          switch (value_type)
          {
            case 'string':
            {
              value = '"' + value + '"';
              break;
            }
            case 'null':
            case 'undefined':
            {
              value = value_type;
              break;
            }
          }
        }
        __console_output.render
        (
          ['pre', value ].concat(
                    obj_id
                    ? ['handler', 'inspect-object-link', 'rt-id', runtime_id, 'obj-id', obj_id]
                    : [] )
        );
        var container = __console_output.parentNode.parentNode;
        container.scrollTop = container.scrollHeight;
      }
      else if (return_value = xml.getElementsByTagName('object-id')[0])
      {
        var object_id = return_value.textContent;
        var object_ref_name = "$" + object_id;
        var tag = tagManager.setCB(null, handleEval, [runtime_id, object_id] );
        var script_string  = "return " + object_ref_name + ".toString()";
        services['ecmascript-debugger'].eval(
          tag, runtime_id, '', '', "<![CDATA["+script_string+"]]>", [object_ref_name, object_id]);
      }
    }
    else
    {
      var error_id = xml.getNodeData('object-id');
      if( error_id )
      {
        var tag = tagManager.setCB(null, handleError);
        services['ecmascript-debugger'].examineObjects(tag, runtime_id, error_id);
      }
    }

  }

  var handleError = function(xml)
  {
    var return_value = xml.getElementsByTagName('string')[0];
    if(return_value)
    {
      __console_output.render(['pre', return_value.firstChild.nodeValue]);
      var container = __console_output.parentNode.parentNode;
      container.scrollTop = container.scrollHeight;
    }
  }

  var markup = "\

\

\

\
        >>> \

<textarea handler='commandline' rows='1' title='hold shift to add a new line'></textarea>
\

\

";

  var templates = {};

  templates.consoleInput = function(entry)
  {
    var lines_count = entry.msg.split(/\r?\n/).length;
    var line_head = '>>>';
    while( --lines_count > 1 )
    {
      line_head += '\n...';
    }
    return [['div', line_head], ['pre', entry.msg]];
  }

  eventHandlers.click['console-focus-input'] = function(event, ele)
  {
    ele.getElementsByTagName('textarea')[0].focus();
  }

  var submit = function(input)
  {
    var
    rt_id = runtimes.getSelectedRuntimeId(),
    frame_id = '',
    thread_id = '';

    if(rt_id)
    {
      if( __frame_index > -1 )
      {
        frame_id = __frame_index;
        thread_id = stop_at.getThreadId();

        //opera.postError(JSON.stringify(frame_inspection.getSelectedObjectData()));
      }
      var tag = tagManager.setCB(null, handleEval, [rt_id] );
      var script_string  = submit_buffer.join('');
      services['ecmascript-debugger'].eval(tag, rt_id, thread_id, frame_id, "<![CDATA["+script_string+"]]>");
      submit_buffer = [];
    }
    else
    {
      alert(ui_strings.S_INFO_NO_RUNTIME_SELECTED);
    }
  }

  var line_buffer_push = function(line)
  {
    line_buffer[line_buffer.length] = line.replace(/\r\n/g, "");
    if( line_buffer.length > 100 )
    {
      line_buffer = line_buffer.slice(line_buffer.length - 100);
    }
    line_buffer_cursor = line_buffer.length;
  }

  eventHandlers.keyup['commandline'] = function(event)
  {
    if(event.keyCode == 38 || event.keyCode == 40)
    {

      line_buffer_cursor += event.keyCode == 38 ? -1 : 1;
      line_buffer_cursor =
        line_buffer_cursor < 0 ? line_buffer.length-1 : line_buffer_cursor > line_buffer.length-1 ? 0 : line_buffer_cursor;
      event.target.value = (line_buffer.length ? line_buffer[line_buffer_cursor] : '').replace(/\r\n/g, '');
      event.preventDefault();
      return;
    }
    const CRLF = "\r\n";
    var value = event.target.value;
    var lastCRLFIndex = value.lastIndexOf(CRLF);
    if(lastCRLFIndex != -1)
    {
      if ( value.length -2 != lastCRLFIndex )
      {
        value = value.slice(0, lastCRLFIndex) + value.slice(lastCRLFIndex + 2) + CRLF;
      }
      __console_output.render(
        ['div', ( submit_buffer.length ? "... " : ">>> " ) + value, 'class', 'log-entry']);
      line_buffer_push( submit_buffer[submit_buffer.length] = value );
      if(!event.shiftKey)
      {
        submit();
      }
      __prefix.textContent = submit_buffer.length ? "... " : ">>> ";
      event.target.value = '';
      var container = __console_output.parentNode.parentNode;
      container.scrollTop = container.scrollHeight;
    }

  }

  eventHandlers.keypress['commandline'] = function(event)
  {
    var target = event.target;
    switch(event.keyCode)
    {
      case 16:
      {
        break;
      }
      case 9:
      {
        event.preventDefault();
        if( __selection_start == -1 )
        {
          __selection_start = target.selectionStart;
        }
        var cur_str = target.value.slice(0, __selection_start);
        var suggest = autocomplete.getSuggest(cur_str, event.shiftKey, arguments);
        if( suggest )
        {
          target.value = cur_str + suggest;
        }
        break;
      }
      default:
      {
        __selection_start = -1;
      }
    }
  }

  var autocomplete = new function()
  {
    var str_input = '';
    var path = '';
    var id = '';
    var scope = null;
    var current_path = '';
    var match = [];
    var match_cur = 0;
    var local_frame_index = 0;

    const
    SCRIPT = "(function(){var a = '', b= ''; for( a in %s ){ b += a + '_,_'; }; return b; })()",
    KEY = 0,
    DEPTH = 3;

    var get_scope = function(path, old_args)
    {

      var
      rt_id = runtimes.getSelectedRuntimeId(),
      frame_id = '',
      thread_id = '';

      if(rt_id)
      {
        if( !path && __frame_index == -1 )
        {
          path = 'this';
        }
        if( __frame_index.toString() + rt_id + path == current_path )
        {
          return scope;
        }
        if( __frame_index > -1 )
        {
          frame_id = __frame_index;
          thread_id = stop_at.getThreadId();
          if( !path )
          {
            var selectedObject = frame_inspection.getSelectedObject()
            var data = frame_inspection.getData(selectedObject.rt_id, selectedObject.obj_id, -1, arguments);
            if( data )
            {
              var i = 2, prop = null;
              scope = [];
              for( ; prop = data[i]; i++ )
              {
                if( prop[DEPTH] == 0 )
                {
                  scope[scope.length] = prop[KEY];
                }
              }
              current_path = __frame_index.toString() + rt_id + path;
              return scope;
            }
            else
            {
              return null;
            }
          }
        }
        var tag = tagManager.setCB(null, handleEvalScope, [__frame_index, rt_id, path, old_args] );
        services['ecmascript-debugger'].eval(tag, rt_id, thread_id,
          frame_id, "<![CDATA["+SCRIPT.replace(/%s/, path)+"]]>");
      }
      else
      {
        alert('select a window');
      }
      return null;
    }

    var handleEvalScope = function(xml, __frame_index, rt_id, path, old_args)
    {
      var status = xml.getNodeData('status');
      if( status == 'completed' )
      {
        var return_value = xml.getElementsByTagName('string')[0];
        if(return_value)
        {
          scope = return_value.textContent.split('_,_');
          current_path = __frame_index.toString() + rt_id + path;
          if( !old_args[0].__call_count )
          {
            old_args[0].__call_count = 1;
            old_args.callee.call(null, old_args[0]);
          }
        }
      }
      else
      {
         opera.postError("getting scope failed in autocomplete view-commandline");
      }
    }

    this.getSuggest = function(str, shift_key, old_args)
    {
      if( !str || str != str_input )
      {
        var last_dot = str.lastIndexOf('.'), new_path = '', new_id = '', ret = '';
        if(last_dot > -1)
        {
          new_path = str.slice(0, last_dot);
          new_id = str.slice(last_dot + 1);
        }
        else
        {
          new_id = str;
        }
        if( path != new_path || !scope )
        {
          match = [];
          if( !( scope = get_scope(new_path, old_args) ) )
          {
            return '';
          }
          path = new_path;
        }
        if( !match.length || id != new_id )
        {
          match = [];
          match_cur = 0;
          var prop = '', i = 0;
          for( ; prop = scope[i]; i++)
          {
            if( prop.indexOf(new_id) == 0 )
            {
              match[match.length] = prop;
            }
          }
          id = new_id;
        }
        str_input = str;

      }
      ret = match[match_cur] || '';
      if(shift_key)
      {
        match_cur--;
        if( match_cur < 0 )
        {
          match_cur = match.length ? match.length - 1 : 0;
        }
      }
      else
      {
        match_cur++;
        if( match_cur >= match.length )
        {
          match_cur = 0;
        }
      }

      return  ret.slice(id.length);
    }

    this.clear = function(frame_index)
    {
      // it could be that this check is too simple
      // basically the global scope is invalided with a new thread
      // but the tab completion feature is not very helpfull
      // with sites with intervals or timeouts
      if( frame_index > -1 || frame_index != local_frame_index )
      {
        local_frame_index = -1;
        str_input = '';
        path = '';
        id = '';
        scope = null;
        current_path = '';
        match = [];
        match_cur = 0;
      }
    };

  };

  this.createView = function(container)
  {
    container.innerHTML = markup;
    container.scrollTop = container.scrollHeight;
    __console_output = container.getElementsByTagName('div')[1];
    __console_input = container.getElementsByTagName('div')[2];
    __prefix = __console_input.getElementsByTagName('span')[0];
  }

  this.ondestroy = function()
  {
    __console_output = null;
    __console_input = null;
    __prefix = null;
  }

  var onFrameSelected = function(msg)
  {
    __frame_index = msg.frame_index;
    autocomplete.clear(__frame_index);
  }

  messages.addListener('frame-selected', onFrameSelected);

  this.init(id, name, container_class, html, default_handler);

}

cls.CommandLineView.prototype = ViewBase;
new cls.CommandLineView('command_line', ui_strings.M_VIEW_LABEL_COMMAND_LINE, 'scroll', '', 'cmd-focus');

eventHandlers.click['cmd-focus'] = function(event, target)
{
  target.getElementsByTagName('textarea')[0].focus();
}
<commit_msg>fix for Bug 331219, switching 'tab' clears command line, and added a 'clear' command to clear the view<commit_after>var cls = window.cls || ( window.cls = {} );

/**
  * @constructor
  * @extends ViewBase
  */

cls.CommandLineView = function(id, name, container_class, html, default_handler)
{

  var self = this;

  var __frame_index = -1;

  var __container = null;
  var __console_output = null;
  var __console_input = null;
  var __prefix = null;
  var __textarea = null;
  var __textarea_value = '';

  var submit_buffer = [];
  var line_buffer = [];
  var line_buffer_cursor = 0;

  var __selection_start = -1;

  var console_output_data = [];

  var cons_out_render_return_val = function(entry)
  {
    if( __console_output )
    {
      __console_output.render
      (
        ['pre', entry.value ].concat
        (
          entry.obj_id
          ? [
              'handler', 'inspect-object-link',
              'rt-id', entry.runtime_id,
              'obj-id', entry.obj_id
            ]
          : []
        )
      );
    }
  }

  var cons_out_render_input = function(entry)
  {
    if( __console_output )
    {
      __console_output.render(['div', entry.value, 'class', 'log-entry']);
    }
  }

  var type_map =
  {
    "return-value": cons_out_render_return_val,
    "input-value": cons_out_render_input
  }

  var cons_out_update = function()
  {
    if( __console_output )
    {
      __console_output.innerHTML = '';
      var entry = null, i = 0;
      for( ; entry = console_output_data[i]; i++ )
      {
        type_map[entry.type](entry);
      }
      __container.scrollTop = __container.scrollHeight;
    };
    if( __textarea )
    {
      __textarea.value = __textarea_value;
      __textarea.selectionEnd = __textarea.selectionStart = __textarea_value.length;
      __textarea.focus();
    };
  }

  var handleEval = function(xml, runtime_id, obj_id)
  {
    var value_type = xml.getNodeData('data-type');
    var status = xml.getNodeData('status');
    if( status == 'completed' )
    {
      var return_value = xml.getElementsByTagName('string')[0];
      if(return_value)
      {
        var value = return_value.firstChild && return_value.firstChild.nodeValue || '';
        if( !obj_id )
        {
          switch (value_type)
          {
            case 'string':
            {
              value = '"' + value + '"';
              break;
            }
            case 'null':
            case 'undefined':
            {
              value = value_type;
              break;
            }
          }
        }
        cons_out_render_return_val
        (
          console_output_data[console_output_data.length] =
          {
            type: "return-value",
            obj_id: obj_id,
            runtime_id: runtime_id,
            value: value
          }
        );
        __container.scrollTop = __container.scrollHeight;
      }
      else if (return_value = xml.getElementsByTagName('object-id')[0])
      {
        var object_id = return_value.textContent;
        var object_ref_name = "$" + object_id;
        var tag = tagManager.setCB(null, handleEval, [runtime_id, object_id] );
        var script_string  = "return " + object_ref_name + ".toString()";
        services['ecmascript-debugger'].eval(
          tag, runtime_id, '', '', "<![CDATA["+script_string+"]]>", [object_ref_name, object_id]);
      }
    }
    else
    {
      var error_id = xml.getNodeData('object-id');
      if( error_id )
      {
        var tag = tagManager.setCB(null, handleError);
        services['ecmascript-debugger'].examineObjects(tag, runtime_id, error_id);
      }
    }

  }

  var handleError = function(xml)
  {
    var return_value = xml.getElementsByTagName('string')[0];
    if(return_value)
    {
      cons_out_render_return_val
      (
        console_output_data[console_output_data.length] =
        {
          type: "return-value",
          value: return_value.firstChild.nodeValue
        }
      );
      __container.scrollTop = __container.scrollHeight;
    }
  }

  var markup = "\

\

\

\
        >>> \

<textarea handler='commandline' rows='1' title='hold shift to add a new line'></textarea>
\

\

";

  var templates = {};

  templates.consoleInput = function(entry)
  {
    var lines_count = entry.msg.split(/\r?\n/).length;
    var line_head = '>>>';
    while( --lines_count > 1 )
    {
      line_head += '\n...';
    }
    return [['div', line_head], ['pre', entry.msg]];
  }

  eventHandlers.click['console-focus-input'] = function(event, ele)
  {
    ele.getElementsByTagName('textarea')[0].focus();
  }

  var command_map =
  {
    "clear": function()
    {
      console_output_data = [];
      cons_out_update();
    }
  };

  var submit = function(input)
  {
    var
    rt_id = runtimes.getSelectedRuntimeId(),
    frame_id = '',
    thread_id = '',
    script_string  = '',
    command = '',
    opening_brace = 0,
    closing_brace = 0,
    tag = 0;

    if(rt_id)
    {
      if( __frame_index > -1 )
      {
        frame_id = __frame_index;
        thread_id = stop_at.getThreadId();
      }
      script_string  = submit_buffer.join('');
      opening_brace = script_string.indexOf('(');
      command = ( opening_brace != -1 && script_string.slice(0, opening_brace) || '' ).
        replace(/ +$/, '').replace(/^ +/, '');
      closing_brace = script_string.lastIndexOf(')');
      if ( command && command in command_map && closing_brace != -1 )
      {
        command_map[command](script_string.slice(opening_brace + 1, closing_brace));
      }
      else
      {
        tag = tagManager.setCB(null, handleEval, [rt_id] );
        services['ecmascript-debugger'].eval(tag, rt_id, thread_id, frame_id, "<![CDATA["+script_string+"]]>");
      }
      submit_buffer = [];
    }
    else
    {
      alert(ui_strings.S_INFO_NO_RUNTIME_SELECTED);
    }
  }

  var line_buffer_push = function(line)
  {
    line_buffer[line_buffer.length] = line.replace(/\r\n/g, "");
    if( line_buffer.length > 100 )
    {
      line_buffer = line_buffer.slice(line_buffer.length - 100);
    }
    line_buffer_cursor = line_buffer.length;
  }

  eventHandlers.keyup['commandline'] = function(event)
  {
    if(event.keyCode == 38 || event.keyCode == 40)
    {

      line_buffer_cursor += event.keyCode == 38 ? -1 : 1;
      line_buffer_cursor =
        line_buffer_cursor < 0 ? line_buffer.length-1 : line_buffer_cursor > line_buffer.length-1 ? 0 : line_buffer_cursor;
      __textarea_value = event.target.value = (line_buffer.length ? line_buffer[line_buffer_cursor] : '').replace(/\r\n/g, '');
      event.preventDefault();
      return;
    }
    const CRLF = "\r\n";
    var value = event.target.value;
    var lastCRLFIndex = value.lastIndexOf(CRLF);
    if(lastCRLFIndex != -1)
    {
      if ( value.length - 2 != lastCRLFIndex )
      {
        value = value.slice(0, lastCRLFIndex) + value.slice(lastCRLFIndex + 2) + CRLF;
      }
      cons_out_render_input
      (
        console_output_data[console_output_data.length] =
        {
          type: "input-value",
          value: ( submit_buffer.length ? "... " : ">>> " ) + value
        }
      );
      line_buffer_push( submit_buffer[submit_buffer.length] = value );
      if(!event.shiftKey)
      {
        submit();
      }
      __prefix.textContent = submit_buffer.length ? "... " : ">>> ";
      event.target.value = '';
      __container.scrollTop = __container.scrollHeight;
      __textarea.scrollTop = 0;
    }
    __textarea_value = event.target.value;

  }

  eventHandlers.keypress['commandline'] = function(event)
  {
    var target = event.target;
    switch(event.keyCode)
    {
      case 16:
      {
        break;
      }
      case 9:
      {
        event.preventDefault();
        if( __selection_start == -1 )
        {
          __selection_start = target.selectionStart;
        }
        var cur_str = target.value.slice(0, __selection_start);
        var suggest = autocomplete.getSuggest(cur_str, event.shiftKey, arguments);
        if( suggest )
        {
          target.value = cur_str + suggest;
        }
        break;
      }
      default:
      {
        __selection_start = -1;
      }
    }
  }

  var autocomplete = new function()
  {
    var str_input = '';
    var path = '';
    var id = '';
    var scope = null;
    var current_path = '';
    var match = [];
    var match_cur = 0;
    var local_frame_index = 0;

    const
    SCRIPT = "(function(){var a = '', b= ''; for( a in %s ){ b += a + '_,_'; }; return b; })()",
    KEY = 0,
    DEPTH = 3;

    var get_scope = function(path, old_args)
    {

      var
      rt_id = runtimes.getSelectedRuntimeId(),
      frame_id = '',
      thread_id = '';

      if(rt_id)
      {
        if( !path && __frame_index == -1 )
        {
          path = 'this';
        }
        if( __frame_index.toString() + rt_id + path == current_path )
        {
          return scope;
        }
        if( __frame_index > -1 )
        {
          frame_id = __frame_index;
          thread_id = stop_at.getThreadId();
          if( !path )
          {
            var selectedObject = frame_inspection.getSelectedObject()
            var data = frame_inspection.getData(selectedObject.rt_id, selectedObject.obj_id, -1, arguments);
            if( data )
            {
              var i = 2, prop = null;
              scope = [];
              for( ; prop = data[i]; i++ )
              {
                if( prop[DEPTH] == 0 )
                {
                  scope[scope.length] = prop[KEY];
                }
              }
              current_path = __frame_index.toString() + rt_id + path;
              return scope;
            }
            else
            {
              return null;
            }
          }
        }
        var tag = tagManager.setCB(null, handleEvalScope, [__frame_index, rt_id, path, old_args] );
        services['ecmascript-debugger'].eval(tag, rt_id, thread_id,
          frame_id, "<![CDATA["+SCRIPT.replace(/%s/, path)+"]]>");
      }
      else
      {
        alert('select a window');
      }
      return null;
    }

    var handleEvalScope = function(xml, __frame_index, rt_id, path, old_args)
    {
      var status = xml.getNodeData('status');
      if( status == 'completed' )
      {
        var return_value = xml.getElementsByTagName('string')[0];
        if(return_value)
        {
          scope = return_value.textContent.split('_,_');
          current_path = __frame_index.toString() + rt_id + path;
          if( !old_args[0].__call_count )
          {
            old_args[0].__call_count = 1;
            old_args.callee.call(null, old_args[0]);
          }
        }
      }
      else
      {
         opera.postError("getting scope failed in autocomplete view-commandline");
      }
    }

    this.getSuggest = function(str, shift_key, old_args)
    {
      if( !str || str != str_input )
      {
        var last_dot = str.lastIndexOf('.'), new_path = '', new_id = '', ret = '';
        if(last_dot > -1)
        {
          new_path = str.slice(0, last_dot);
          new_id = str.slice(last_dot + 1);
        }
        else
        {
          new_id = str;
        }
        if( path != new_path || !scope )
        {
          match = [];
          if( !( scope = get_scope(new_path, old_args) ) )
          {
            return '';
          }
          path = new_path;
        }
        if( !match.length || id != new_id )
        {
          match = [];
          match_cur = 0;
          var prop = '', i = 0;
          for( ; prop = scope[i]; i++)
          {
            if( prop.indexOf(new_id) == 0 )
            {
              match[match.length] = prop;
            }
          }
          id = new_id;
        }
        str_input = str;

      }
      ret = match[match_cur] || '';
      if(shift_key)
      {
        match_cur--;
        if( match_cur < 0 )
        {
          match_cur = match.length ? match.length - 1 : 0;
        }
      }
      else
      {
        match_cur++;
        if( match_cur >= match.length )
        {
          match_cur = 0;
        }
      }

      return  ret.slice(id.length);
    }

    this.clear = function(frame_index)
    {
      // it could be that this check is too simple
      // basically the global scope is invalided with a new thread
      // but the tab completion feature is not very helpfull
      // with sites with intervals or timeouts
      if( frame_index > -1 || frame_index != local_frame_index )
      {
        local_frame_index = -1;
        str_input = '';
        path = '';
        id = '';
        scope = null;
        current_path = '';
        match = [];
        match_cur = 0;
      }
    };

  };

  this.createView = function(container)
  {
    container.innerHTML = markup;
    container.scrollTop = container.scrollHeight;
    __container = container;
    __console_output = container.getElementsByTagName('div')[1];
    __console_input = container.getElementsByTagName('div')[2];
    __prefix = __console_input.getElementsByTagName('span')[0];
    __textarea = container.getElementsByTagName('textarea')[0];
    cons_out_update();
  }

  this.ondestroy = function()
  {
    __console_output = null;
    __console_input = null;
    __prefix = null;
  }

  var onFrameSelected = function(msg)
  {
    __frame_index = msg.frame_index;
    autocomplete.clear(__frame_index);
  }

  messages.addListener('frame-selected', onFrameSelected);

  this.init(id, name, container_class, html, default_handler);

}

cls.CommandLineView.prototype = ViewBase;
new cls.CommandLineView('command_line', ui_strings.M_VIEW_LABEL_COMMAND_LINE, 'scroll', '', 'cmd-focus');

eventHandlers.click['cmd-focus'] = function(event, target)
{
  target.getElementsByTagName('textarea')[0].focus();
}
<|endoftext|>


<commit_before>export default function send(req) {
  return new Promise((resolve, reject) => {
    resolve(true);
  });
}
<commit_msg>sending messages<commit_after>import aws from 'aws-sdk';

export default function send(req) {
  return new Promise((resolve, reject) => {
    aws.config.update({
      accessKeyId: process.env.SES_KEY_ID,
      secretAccessKey: process.env.SES_ACCESS_KEY,
    });
    aws.config.update({region: 'eu-west-1'});
    const ses = new aws.SES({apiVersion: '2010-12-01'});
    const to = ['account@createdigital.me'];
    const from = 'robot@createdigital.me';
    ses.sendEmail({
      Source: from,
      Destination: { ToAddresses: to },
      Message: {
        Subject: {
          Data: 'Сообщение из АудитаПродукта',
        },
        Body: {
          Text: {
            Data: `E-mail: ${req.body.email},
            Text: ${req.body.text}`
          }
        }
      }
    }, (err, data) => {
      if (err) {
        console.log(err);
        reject(true);
      } else {
        resolve(data);
      }
    });
  });
}
<|endoftext|>