<commit_before>'use babel';

import semver from 'semver';
import { $, ScrollView } from 'atom-space-pen-views';
import {
  getObject,
  resizeCursor,
  elapsedTime,
  resolveTree,
  getSelectedTree,
} from '../helpers';
import DirectoryView from './directory-view';

let hideLocalTreeError = false;

function hideLocalTree() {
  const treeView = getObject({
    obj: atom.packages.loadedPackages,
    keys: ['tree-view', 'mainModule', 'treeView'],
  });

  if (treeView && typeof treeView.detach === 'function') { // Fix for Issue 433 ( workaround to stop throwing error)
    try {
      treeView.detach();
    } catch (e) {
      if (hideLocalTreeError === false) {
        atom.notifications.addWarning('Remote FTP: See issue #433', {
          dismissable: false,
        });
        hideLocalTreeError = true;
      }
    }
  }
}

function showLocalTree() {
  const treeView = getObject({
    obj: atom.packages.loadedPackages,
    keys: ['tree-view', 'mainModule', 'treeView'],
  });
  if (treeView && typeof treeView.detach === 'function') treeView.attach();
}

class TreeView extends ScrollView {

  static content() {
    return this.div({
      class: 'remote-ftp-view ftptree-view-resizer tool-panel',
      'data-show-on-right-side': atom.config.get('tree-view.showOnRightSide'),
      'data-use-dock-integration': atom.config.get('Remote-FTP.tree.useDockIntegration'),
    }, () => {
      this.ul({
        class: 'list-inline tab-bar inset-panel show',
        is: 'atom-tabs',
        tabindex: -1,
        location: 'left',
        outlet: 'tabPanel',
      }, () => {
        this.li({
          class: 'tab',
          is: 'tabs-tab',
          'data-type': 'TreeView',
        }, () => {
          this.div({
            class: 'title',
          }, () => {
            this.text('Remote-FTP');
          });
        });
      });

      this.div({
        class: 'scroller',
        outlet: 'scroller',
      }, () => {
        this.ol({
          class: 'ftptree-view full-menu list-tree has-collapsable-children focusable-panel',
          tabindex: -1,
          outlet: 'list',
        });
      });

      this.div({
        class: 'resize-handle',
        outlet: 'horizontalResize',
        style: `cursor:${resizeCursor}`, // platform specific cursor
      });

      this.div({
        class: 'queue tool-panel panel-bottom',
        tabindex: -1,
        outlet: 'queue',
      }, () => {
        this.ul({
          class: 'progress tool-panel panel-top',
          tabindex: -1,
          outlet: 'progress',
        });
        this.ul({
          class: 'list',
          tabindex: -1,
          outlet: 'debug',
        });
        return this.div({
          class: 'resize-handle',
          outlet: 'verticalResize',
        });
      });

      this.div({
        class: 'offline',
        tabindex: -1,
        outlet: 'offline',
      });
    });
  }

  initialize(...args) {
    super.initialize(...args);

    // Supported for old API
    this.getSelected = getSelectedTree;
    this.resolve = resolveTree;

    const html = `

Connect

Edit Configuration

Edit Ignore Configuration

Close Panel

`;

    this.offline.html(html);

    if (atom.project.remoteftp.isConnected()) {
      this.showOnline();
    } else {
      this.showOffline();
    }

    this.root = new DirectoryView(atom.project.remoteftp.root);
    this.root.expand();
    this.list.append(this.root);

    // Events
    atom.config.onDidChange('tree-view.showOnRightSide', () => {
      if (this.isVisible()) {
        setTimeout(() => {
          this.detach();
          this.attach();
        }, 1);
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.hideLocalWhenDisplayed', (values) => {
      if (values.newValue) {
        if (this.isVisible()) {
          hideLocalTree();
        }
      } else if (this.isVisible()) {
        this.detach();
        showLocalTree();
        this.attach();
      } else {
        showLocalTree();
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.useDockIntegration', () => {
      if (typeof atom.workspace.getRightDock === 'undefined') {
        atom.notifications.addWarning('Your editor is deprecated.
This option is available only >=1.17.0 version.');
        atom.config.set('Remote-FTP.tree.useDockIntegration', 'false');
      } else if (this.isVisible()) {
        setTimeout(() => {
          this.detach();
          this.attach();
        }, 1);
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.enableDragAndDrop', (value) => {
      if (value.newValue) {
        this.createDragAndDrops();
      } else {
        this.disposeDragAndDrops();
      }
    });

    atom.project.remoteftp.onDidDebug((msg) => {
      this.debug.prepend(`<li>${msg}</li>`);
      const children = this.debug.children();

      if (children.length > 20) {
        children.last().remove();
      }
    });

    atom.project.remoteftp.onDidQueueChanged(() => {
      this.progress.empty();

      const queues = [];
      if (atom.project.remoteftp.current) {
        queues.push(atom.project.remoteftp.current);
      }

      atom.project.remoteftp.queue.forEach((queueElem) => {
        queues.push(queueElem);
      });

      if (queues.length === 0) {
        this.progress.hide();
      } else {
        this.progress.show();

        queues.forEach((queue) => {
          const $li = $(`<li><progress class="inline-block" /><div class="name">${queue[0]}</div><div class="eta">-</div></li>`);
          const $progress = $li.children('progress');
          const $eta = $li.children('.eta');
          const progress = queue[2];

          this.progress.append($li);

          progress.on('progress', (percent) => {
            if (percent === -1) {
              $progress.removeAttr('max').removeAttr('value');
              $eta.text('-');
            } else {
              $progress.attr('max', 100).attr('value', parseInt(percent * 100, 10));
              const eta = progress.getEta();

              $eta.text(elapsedTime(eta));
            }
          });

          progress.once('done', () => {
            progress.removeAllListeners('progress');
          });
        });
      }
    });

    this.offline.on('click', '[role="connect"]', () => {
      atom.project.remoteftp.readConfig(() => {
        atom.project.remoteftp.connect();
      });
    });

    this.offline.on('click', '[role="configure"]', () => {
      atom.workspace.open(atom.project.remoteftp.getConfigPath());
    });

    this.offline.on('click', '[role="configure_ignored"]', () => {
      atom.workspace.open(atom.project.getDirectories()[0].resolve('.ftpignore'));
    });

    this.offline.on('click', '[role="toggle"]', () => {
      this.toggle();
    });

    this.horizontalResize.on('dblclick', (e) => { this.resizeToFitContent(e); });
    this.horizontalResize.on('mousedown', (e) => { this.resizeHorizontalStarted(e); });
    this.verticalResize.on('mousedown', (e) => { this.resizeVerticalStarted(e); });
    this.list.on('keydown', (e) => { this.remoteKeyboardNavigation(e); });

    atom.project.remoteftp.onDidConnected(() => {
      this.showOnline();
    });

    atom.project.remoteftp.onDidDisconnected(() => {
      this.showOffline();
    });

    this.getTitle = () => 'Remote-FTP';
  }

  attach() {
    const enableDock = atom.config.get('Remote-FTP.tree.useDockIntegration');
    const showOnRightSide = atom.config.get('tree-view.showOnRightSide');
    const hideLocalDisplay = atom.config.get('Remote-FTP.tree.hideLocalWhenDisplayed');

    if (showOnRightSide && enableDock) {
      // if show on right side && use new integration
      const activePane = atom.workspace.getRightDock().paneContainer.getActivePane();

      this.panel = activePane.addItem(this);
      activePane.activateItemforURI(this.getTitle());
    } else if (!showOnRightSide && enableDock) {
      // if not show on right side && use new integration
      const activePane = atom.workspace.getLeftDock().paneContainer.getActivePane();

      this.panel = activePane.addItem(this);
      activePane.activateItem(this.panel);
    } else if (showOnRightSide && !enableDock) {
      // if show on right side && not use new integration
      this.panel = atom.workspace.addRightPanel({ item: this });
    } else if (!showOnRightSide && !enableDock) {
      // if not show on right side && not use new integration
      this.panel = atom.workspace.addLeftPanel({ item: this });
    }

    if (hideLocalDisplay) {
      hideLocalTree();
    } else {
      showLocalTree();
    }
  }

  attached() {
    const enableDock = atom.config.get('Remote-FTP.tree.useDockIntegration');

    if (semver.satisfies('<1.17.0')) {
      this.classList.add('oldversion');
    }

    if (!enableDock && semver.satisfies(atom.appVersion.split('-')[0], '>=1.17.0')) {
      this.tabPanel.addClass('show');
    } else {
      this.tabPanel.removeClass('show');
    }

    this.attr('data-use-dock-integration', enableDock);
  }

  detach(...args) {
    super.detach(...args);

    if (this.panel) {
      if (typeof this.panel.destroy === 'function') {
        this.panel.destroy();
      } else if (typeof atom.workspace.paneForItem === 'function') {
        if (typeof atom.workspace.paneForItem(this.panel) !== 'undefined') {
          atom.workspace.paneForItem(this.panel).destroyItem(this.panel, true);
        }
      }

      this.panel = null;
    }
  }

  createDragAndDrops() {
    this.root.getViews().forEach((view) => {
      view.dragEventsActivate();
    });
  }
  disposeDragAndDrops() {
    this.root.getViews().forEach((view) => {
      view.dragEventsDestroy();
    });
  }

  toggle() {
    if (typeof this.panel !== 'undefined' && this.panel !== null) {
      this.detach();
    } else {
      this.attach();
    }
  }

  showOffline() {
    this.list.hide();
    this.queue.hide();
    this.offline.css('display', 'flex');
  }

  showOnline() {
    this.list.show();
    this.queue.show();
    this.offline.hide();
  }

  resizeVerticalStarted(e) {
    e.preventDefault();

    const $doc = $(document);

    this.resizeHeightStart = this.queue.height();
    this.resizeMouseStart = e.pageY;

    $doc.on('mousemove', this.resizeVerticalView.bind(this));
    $doc.on('mouseup', this.resizeVerticalStopped);
  }

  resizeVerticalStopped() {
    delete this.resizeHeightStart;
    delete this.resizeMouseStart;

    const $doc = $(document);

    $doc.off('mousemove', this.resizeVerticalView);
    $doc.off('mouseup', this.resizeVerticalStopped);
  }

  resizeVerticalView(e) {
    if (e.which !== 1) { return this.resizeVerticalStopped(); }

    const delta = e.pageY - this.resizeMouseStart;
    const height = Math.max(26, this.resizeHeightStart - delta);

    this.queue.height(height);
    this.scroller.css('bottom', `${height}px`);

    return true;
  }

  resizeHorizontalStarted(e) {
    e.preventDefault();

    this.resizeWidthStart = this.width();
    this.resizeMouseStart = e.pageX;

    const $doc = $(document);

    $doc.on('mousemove', this.resizeHorizontalView.bind(this));
    $doc.on('mouseup', this.resizeHorizontalStopped);
  }

  resizeHorizontalStopped() {
    delete this.resizeWidthStart;
    delete this.resizeMouseStart;

    const $doc = $(document);

    $doc.off('mousemove', this.resizeHorizontalView);
    $doc.off('mouseup', this.resizeHorizontalStopped);
  }

  resizeHorizontalView(e) {
    if (e.which !== 1) { return this.resizeHorizontalStopped(); }

    const delta = e.pageX - this.resizeMouseStart;
    const width = Math.max(50, this.resizeWidthStart + delta);

    this.width(width);

    return true;
  }

  resizeToFitContent(e) {
    e.preventDefault();

    this.width(1);
    this.width(this.list.outerWidth());
  }

  remoteKeyboardNavigation(e) {
    const arrows = { left: 37, up: 38, right: 39, down: 40 };
    const keyCode = e.keyCode || e.which;

    switch (keyCode) {
      case arrows.up:
        this.remoteKeyboardNavigationUp();
        break;
      case arrows.down:
        this.remoteKeyboardNavigationDown();
        break;
      case arrows.left:
        this.remoteKeyboardNavigationLeft();
        break;
      case arrows.right:
        this.remoteKeyboardNavigationRight();
        break;
      default:
        return;
    }

    e.preventDefault();
    e.stopPropagation();

    this.remoteKeyboardNavigationMovePage();
  }

  remoteKeyboardNavigationUp() {
    const current = this.list.find('.selected');

    let next = current.prev('.entry:visible');

    if (next.length) {
      while (next.is('.expanded') && next.find('.entries .entry:visible').length) {
        next = next.find('.entries .entry:visible');
      }
    } else {
      next = current.closest('.entries').closest('.entry:visible');
    }

    if (next.length) {
      current.removeClass('selected');
      next.last().addClass('selected');
    }
  }

  remoteKeyboardNavigationDown() {
    const current = this.list.find('.selected');

    let next = current.find('.entries .entry:visible');
    let tmp = null;

    if (!next.length) {
      tmp = current;

      do {
        next = tmp.next('.entry:visible');

        if (!next.length) {
          tmp = tmp.closest('.entries').closest('.entry:visible');
        }
      } while (!next.length && !tmp.is('.project-root'));
    }
    if (next.length) {
      current.removeClass('selected');
      next.first().addClass('selected');
    }
  }

  remoteKeyboardNavigationLeft() {
    const current = this.list.find('.selected');

    let next = null;

    if (!current.is('.directory')) {
      next = current.closest('.directory');
      next.view().collapse();

      current.removeClass('selected');
      next.first().addClass('selected');
    } else {
      current.view().collapse();
    }
  }

  remoteKeyboardNavigationRight() {
    const current = this.list.find('.selected');

    if (current.is('.directory')) {
      const view = current.view();

      view.open();
      view.expand();
    }
  }

  remoteKeyboardNavigationMovePage() {
    const current = this.list.find('.selected');

    if (current.length) {
      const scrollerTop = this.scroller.scrollTop();
      const selectedTop = current.position().top;

      if (selectedTop < scrollerTop - 10) {
        this.scroller.pageUp();
      } else if (selectedTop > scrollerTop + (this.scroller.height() - 10)) {
        this.scroller.pageDown();
      }
    }
  }
}

export default TreeView;
<commit_msg>Fix old version check<commit_after>'use babel';

import semver from 'semver';
import { $, ScrollView } from 'atom-space-pen-views';
import {
  getObject,
  resizeCursor,
  elapsedTime,
  resolveTree,
  getSelectedTree,
} from '../helpers';
import DirectoryView from './directory-view';

let hideLocalTreeError = false;

function hideLocalTree() {
  const treeView = getObject({
    obj: atom.packages.loadedPackages,
    keys: ['tree-view', 'mainModule', 'treeView'],
  });

  if (treeView && typeof treeView.detach === 'function') { // Fix for Issue 433 ( workaround to stop throwing error)
    try {
      treeView.detach();
    } catch (e) {
      if (hideLocalTreeError === false) {
        atom.notifications.addWarning('Remote FTP: See issue #433', {
          dismissable: false,
        });
        hideLocalTreeError = true;
      }
    }
  }
}

function showLocalTree() {
  const treeView = getObject({
    obj: atom.packages.loadedPackages,
    keys: ['tree-view', 'mainModule', 'treeView'],
  });
  if (treeView && typeof treeView.detach === 'function') treeView.attach();
}

class TreeView extends ScrollView {

  static content() {
    return this.div({
      class: 'remote-ftp-view ftptree-view-resizer tool-panel',
      'data-show-on-right-side': atom.config.get('tree-view.showOnRightSide'),
      'data-use-dock-integration': atom.config.get('Remote-FTP.tree.useDockIntegration'),
    }, () => {
      this.ul({
        class: 'list-inline tab-bar inset-panel show',
        is: 'atom-tabs',
        tabindex: -1,
        location: 'left',
        outlet: 'tabPanel',
      }, () => {
        this.li({
          class: 'tab',
          is: 'tabs-tab',
          'data-type': 'TreeView',
        }, () => {
          this.div({
            class: 'title',
          }, () => {
            this.text('Remote-FTP');
          });
        });
      });

      this.div({
        class: 'scroller',
        outlet: 'scroller',
      }, () => {
        this.ol({
          class: 'ftptree-view full-menu list-tree has-collapsable-children focusable-panel',
          tabindex: -1,
          outlet: 'list',
        });
      });

      this.div({
        class: 'resize-handle',
        outlet: 'horizontalResize',
        style: `cursor:${resizeCursor}`, // platform specific cursor
      });

      this.div({
        class: 'queue tool-panel panel-bottom',
        tabindex: -1,
        outlet: 'queue',
      }, () => {
        this.ul({
          class: 'progress tool-panel panel-top',
          tabindex: -1,
          outlet: 'progress',
        });
        this.ul({
          class: 'list',
          tabindex: -1,
          outlet: 'debug',
        });
        return this.div({
          class: 'resize-handle',
          outlet: 'verticalResize',
        });
      });

      this.div({
        class: 'offline',
        tabindex: -1,
        outlet: 'offline',
      });
    });
  }

  initialize(...args) {
    super.initialize(...args);

    // Supported for old API
    this.getSelected = getSelectedTree;
    this.resolve = resolveTree;

    const html = `

Connect

Edit Configuration

Edit Ignore Configuration

Close Panel

`;

    this.offline.html(html);

    if (atom.project.remoteftp.isConnected()) {
      this.showOnline();
    } else {
      this.showOffline();
    }

    this.root = new DirectoryView(atom.project.remoteftp.root);
    this.root.expand();
    this.list.append(this.root);

    // Events
    atom.config.onDidChange('tree-view.showOnRightSide', () => {
      if (this.isVisible()) {
        setTimeout(() => {
          this.detach();
          this.attach();
        }, 1);
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.hideLocalWhenDisplayed', (values) => {
      if (values.newValue) {
        if (this.isVisible()) {
          hideLocalTree();
        }
      } else if (this.isVisible()) {
        this.detach();
        showLocalTree();
        this.attach();
      } else {
        showLocalTree();
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.useDockIntegration', () => {
      if (typeof atom.workspace.getRightDock === 'undefined') {
        atom.notifications.addWarning('Your editor is deprecated.
This option is available only >=1.17.0 version.');
        atom.config.set('Remote-FTP.tree.useDockIntegration', 'false');
      } else if (this.isVisible()) {
        setTimeout(() => {
          this.detach();
          this.attach();
        }, 1);
      }
    });

    atom.config.onDidChange('Remote-FTP.tree.enableDragAndDrop', (value) => {
      if (value.newValue) {
        this.createDragAndDrops();
      } else {
        this.disposeDragAndDrops();
      }
    });

    atom.project.remoteftp.onDidDebug((msg) => {
      this.debug.prepend(`<li>${msg}</li>`);
      const children = this.debug.children();

      if (children.length > 20) {
        children.last().remove();
      }
    });

    atom.project.remoteftp.onDidQueueChanged(() => {
      this.progress.empty();

      const queues = [];
      if (atom.project.remoteftp.current) {
        queues.push(atom.project.remoteftp.current);
      }

      atom.project.remoteftp.queue.forEach((queueElem) => {
        queues.push(queueElem);
      });

      if (queues.length === 0) {
        this.progress.hide();
      } else {
        this.progress.show();

        queues.forEach((queue) => {
          const $li = $(`<li><progress class="inline-block" /><div class="name">${queue[0]}</div><div class="eta">-</div></li>`);
          const $progress = $li.children('progress');
          const $eta = $li.children('.eta');
          const progress = queue[2];

          this.progress.append($li);

          progress.on('progress', (percent) => {
            if (percent === -1) {
              $progress.removeAttr('max').removeAttr('value');
              $eta.text('-');
            } else {
              $progress.attr('max', 100).attr('value', parseInt(percent * 100, 10));
              const eta = progress.getEta();

              $eta.text(elapsedTime(eta));
            }
          });

          progress.once('done', () => {
            progress.removeAllListeners('progress');
          });
        });
      }
    });

    this.offline.on('click', '[role="connect"]', () => {
      atom.project.remoteftp.readConfig(() => {
        atom.project.remoteftp.connect();
      });
    });

    this.offline.on('click', '[role="configure"]', () => {
      atom.workspace.open(atom.project.remoteftp.getConfigPath());
    });

    this.offline.on('click', '[role="configure_ignored"]', () => {
      atom.workspace.open(atom.project.getDirectories()[0].resolve('.ftpignore'));
    });

    this.offline.on('click', '[role="toggle"]', () => {
      this.toggle();
    });

    this.horizontalResize.on('dblclick', (e) => { this.resizeToFitContent(e); });
    this.horizontalResize.on('mousedown', (e) => { this.resizeHorizontalStarted(e); });
    this.verticalResize.on('mousedown', (e) => { this.resizeVerticalStarted(e); });
    this.list.on('keydown', (e) => { this.remoteKeyboardNavigation(e); });

    atom.project.remoteftp.onDidConnected(() => {
      this.showOnline();
    });

    atom.project.remoteftp.onDidDisconnected(() => {
      this.showOffline();
    });

    this.getTitle = () => 'Remote-FTP';
  }

  attach() {
    const enableDock = atom.config.get('Remote-FTP.tree.useDockIntegration');
    const showOnRightSide = atom.config.get('tree-view.showOnRightSide');
    const hideLocalDisplay = atom.config.get('Remote-FTP.tree.hideLocalWhenDisplayed');

    if (showOnRightSide && enableDock) {
      // if show on right side && use new integration
      const activePane = atom.workspace.getRightDock().paneContainer.getActivePane();

      this.panel = activePane.addItem(this);
      activePane.activateItemforURI(this.getTitle());
    } else if (!showOnRightSide && enableDock) {
      // if not show on right side && use new integration
      const activePane = atom.workspace.getLeftDock().paneContainer.getActivePane();

      this.panel = activePane.addItem(this);
      activePane.activateItem(this.panel);
    } else if (showOnRightSide && !enableDock) {
      // if show on right side && not use new integration
      this.panel = atom.workspace.addRightPanel({ item: this });
    } else if (!showOnRightSide && !enableDock) {
      // if not show on right side && not use new integration
      this.panel = atom.workspace.addLeftPanel({ item: this });
    }

    if (hideLocalDisplay) {
      hideLocalTree();
    } else {
      showLocalTree();
    }
  }

  attached() {
    const enableDock = atom.config.get('Remote-FTP.tree.useDockIntegration');
    const appVersion = atom.appVersion.split('-')[0];

    if (semver.satisfies(appVersion, '<1.17.0')) {
      this.classList.add('oldversion');
    }

    if (!enableDock && semver.satisfies(appVersion, '>=1.17.0')) {
      this.tabPanel.addClass('show');
    } else {
      this.tabPanel.removeClass('show');
    }

    this.attr('data-use-dock-integration', enableDock);
  }

  detach(...args) {
    super.detach(...args);

    if (this.panel) {
      if (typeof this.panel.destroy === 'function') {
        this.panel.destroy();
      } else if (typeof atom.workspace.paneForItem === 'function') {
        if (typeof atom.workspace.paneForItem(this.panel) !== 'undefined') {
          atom.workspace.paneForItem(this.panel).destroyItem(this.panel, true);
        }
      }

      this.panel = null;
    }
  }

  createDragAndDrops() {
    this.root.getViews().forEach((view) => {
      view.dragEventsActivate();
    });
  }
  disposeDragAndDrops() {
    this.root.getViews().forEach((view) => {
      view.dragEventsDestroy();
    });
  }

  toggle() {
    if (typeof this.panel !== 'undefined' && this.panel !== null) {
      this.detach();
    } else {
      this.attach();
    }
  }

  showOffline() {
    this.list.hide();
    this.queue.hide();
    this.offline.css('display', 'flex');
  }

  showOnline() {
    this.list.show();
    this.queue.show();
    this.offline.hide();
  }

  resizeVerticalStarted(e) {
    e.preventDefault();

    const $doc = $(document);

    this.resizeHeightStart = this.queue.height();
    this.resizeMouseStart = e.pageY;

    $doc.on('mousemove', this.resizeVerticalView.bind(this));
    $doc.on('mouseup', this.resizeVerticalStopped);
  }

  resizeVerticalStopped() {
    delete this.resizeHeightStart;
    delete this.resizeMouseStart;

    const $doc = $(document);

    $doc.off('mousemove', this.resizeVerticalView);
    $doc.off('mouseup', this.resizeVerticalStopped);
  }

  resizeVerticalView(e) {
    if (e.which !== 1) { return this.resizeVerticalStopped(); }

    const delta = e.pageY - this.resizeMouseStart;
    const height = Math.max(26, this.resizeHeightStart - delta);

    this.queue.height(height);
    this.scroller.css('bottom', `${height}px`);

    return true;
  }

  resizeHorizontalStarted(e) {
    e.preventDefault();

    this.resizeWidthStart = this.width();
    this.resizeMouseStart = e.pageX;

    const $doc = $(document);

    $doc.on('mousemove', this.resizeHorizontalView.bind(this));
    $doc.on('mouseup', this.resizeHorizontalStopped);
  }

  resizeHorizontalStopped() {
    delete this.resizeWidthStart;
    delete this.resizeMouseStart;

    const $doc = $(document);

    $doc.off('mousemove', this.resizeHorizontalView);
    $doc.off('mouseup', this.resizeHorizontalStopped);
  }

  resizeHorizontalView(e) {
    if (e.which !== 1) { return this.resizeHorizontalStopped(); }

    const delta = e.pageX - this.resizeMouseStart;
    const width = Math.max(50, this.resizeWidthStart + delta);

    this.width(width);

    return true;
  }

  resizeToFitContent(e) {
    e.preventDefault();

    this.width(1);
    this.width(this.list.outerWidth());
  }

  remoteKeyboardNavigation(e) {
    const arrows = { left: 37, up: 38, right: 39, down: 40 };
    const keyCode = e.keyCode || e.which;

    switch (keyCode) {
      case arrows.up:
        this.remoteKeyboardNavigationUp();
        break;
      case arrows.down:
        this.remoteKeyboardNavigationDown();
        break;
      case arrows.left:
        this.remoteKeyboardNavigationLeft();
        break;
      case arrows.right:
        this.remoteKeyboardNavigationRight();
        break;
      default:
        return;
    }

    e.preventDefault();
    e.stopPropagation();

    this.remoteKeyboardNavigationMovePage();
  }

  remoteKeyboardNavigationUp() {
    const current = this.list.find('.selected');

    let next = current.prev('.entry:visible');

    if (next.length) {
      while (next.is('.expanded') && next.find('.entries .entry:visible').length) {
        next = next.find('.entries .entry:visible');
      }
    } else {
      next = current.closest('.entries').closest('.entry:visible');
    }

    if (next.length) {
      current.removeClass('selected');
      next.last().addClass('selected');
    }
  }

  remoteKeyboardNavigationDown() {
    const current = this.list.find('.selected');

    let next = current.find('.entries .entry:visible');
    let tmp = null;

    if (!next.length) {
      tmp = current;

      do {
        next = tmp.next('.entry:visible');

        if (!next.length) {
          tmp = tmp.closest('.entries').closest('.entry:visible');
        }
      } while (!next.length && !tmp.is('.project-root'));
    }
    if (next.length) {
      current.removeClass('selected');
      next.first().addClass('selected');
    }
  }

  remoteKeyboardNavigationLeft() {
    const current = this.list.find('.selected');

    let next = null;

    if (!current.is('.directory')) {
      next = current.closest('.directory');
      next.view().collapse();

      current.removeClass('selected');
      next.first().addClass('selected');
    } else {
      current.view().collapse();
    }
  }

  remoteKeyboardNavigationRight() {
    const current = this.list.find('.selected');

    if (current.is('.directory')) {
      const view = current.view();

      view.open();
      view.expand();
    }
  }

  remoteKeyboardNavigationMovePage() {
    const current = this.list.find('.selected');

    if (current.length) {
      const scrollerTop = this.scroller.scrollTop();
      const selectedTop = current.position().top;

      if (selectedTop < scrollerTop - 10) {
        this.scroller.pageUp();
      } else if (selectedTop > scrollerTop + (this.scroller.height() - 10)) {
        this.scroller.pageDown();
      }
    }
  }
}

export default TreeView;
<|endoftext|>


<commit_before>// Shamelessly ripped from FB's create-react-app

import chalk from 'chalk'
import opn from 'opn'
import { execSync } from 'child_process'
import path from 'path'

function clearConsole () {
  process.stdout.write('\x1B[2J\x1B[0f')
}

const friendlySyntaxErrorLabel = 'Syntax error:'

function isLikelyASyntaxError (message) {
  return message.indexOf(friendlySyntaxErrorLabel) !== -1
}

// This is a little hacky.
// It would be easier if webpack provided a rich error object.
function formatMessage (message) {
  return message
    // Make some common errors shorter:
    .replace(
      // Babel syntax error
      'Module build failed: SyntaxError:',
      friendlySyntaxErrorLabel
    )
    .replace(
      // Webpack file not found error
      /Module not found: Error: Cannot resolve 'file' or 'directory'/,
      'Module not found:'
    )
    // Internal stacks are generally useless so we strip them
    .replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y
    // Webpack loader names obscure CSS filenames
    .replace('./~/css-loader!./~/postcss-loader!', '')
}

export const compileDev = (compiler, port) => {
  compiler.plugin('invalid', function () {
    clearConsole()
    console.log(chalk.yellow('Compiling...'))
    console.log()
  })
  compiler.plugin('done', function (stats) {
    clearConsole()
    const hasErrors = stats.hasErrors()
    const hasWarnings = stats.hasWarnings()
    if (!hasErrors && !hasWarnings) {
      console.log(chalk.green('Compiled successfully!'))
      console.log()
      console.log('The app is running at http://bonde.devel:' + port + '/')
      console.log()
      console.log(chalk.gray('Note that the development build is not optimized.'))
      console.log(chalk.gray('To create a production build, use ' + chalk.cyan('npm run build') + '.'))
      console.log()
      return
    }

    // We have switched off the default Webpack output in WebpackDevServer
    // options so we are going to "massage" the warnings and errors and present
    // them in a readable focused way.
    // We use stats.toJson({}, true) to make output more compact and readable:
    // https://github.com/facebookincubator/create-react-app/issues/401#issuecomment-238291901
    const json = stats.toJson({}, true)
    let formattedErrors = json.errors.map(message =>
      'Error in ' + formatMessage(message)
    )
    const formattedWarnings = json.warnings.map(message =>
      'Warning in ' + formatMessage(message)
    )
    if (hasErrors) {
      console.log(chalk.red('Failed to compile.'))
      console.log()
      if (formattedErrors.some(isLikelyASyntaxError)) {
        // If there are any syntax errors, show just them.
        // This prevents a confusing ESLint parsing error
        // preceding a much more useful Babel syntax error.
        formattedErrors = formattedErrors.filter(isLikelyASyntaxError)
      }
      formattedErrors.forEach(message => {
        console.log(message)
        console.log()
      })
      // If errors exist, ignore warnings.
      return
    }
    if (hasWarnings) {
      console.log(chalk.yellow('Compiled with warnings.'))
      console.log()
      formattedWarnings.forEach(message => {
        console.log(message)
        console.log()
      })
      // Teach some ESLint tricks.
      console.log('You may use special comments to disable some warnings.')
      console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.')
      console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.')
      console.log()
    }
  })

  return compiler
}

function openBrowser (port) {
  if (process.platform === 'darwin') {
    try {
      // Try our best to reuse existing tab
      // on OS X Google Chrome with AppleScript
      execSync('ps cax | grep "Google Chrome"')
      execSync(
        'osascript ' +
        path.resolve(__dirname, 'chrome.applescript') +
        ' http://localhost:' + port + '/'
      )
      return
    } catch (err) {
      // Ignore errors.
    }
  }
  // Fallback to opn
  // (It will always open new tab)
  opn('http://localhost:' + port + '/')
}

export const startDev = (port, err) => {
  clearConsole()
  if (err) {
    console.log(chalk.red(err))
  }
  console.log(chalk.cyan('Starting development server...'))
  console.log()
  openBrowser(port)
}
<commit_msg>Open browser if APP_DOMAIN environment variable is setted, otherwise, keeps the default #411<commit_after>// Shamelessly ripped from FB's create-react-app

import chalk from 'chalk'
import opn from 'opn'
import { execSync } from 'child_process'
import path from 'path'

function clearConsole () {
  process.stdout.write('\x1B[2J\x1B[0f')
}

const friendlySyntaxErrorLabel = 'Syntax error:'

function isLikelyASyntaxError (message) {
  return message.indexOf(friendlySyntaxErrorLabel) !== -1
}

// This is a little hacky.
// It would be easier if webpack provided a rich error object.
function formatMessage (message) {
  return message
    // Make some common errors shorter:
    .replace(
      // Babel syntax error
      'Module build failed: SyntaxError:',
      friendlySyntaxErrorLabel
    )
    .replace(
      // Webpack file not found error
      /Module not found: Error: Cannot resolve 'file' or 'directory'/,
      'Module not found:'
    )
    // Internal stacks are generally useless so we strip them
    .replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y
    // Webpack loader names obscure CSS filenames
    .replace('./~/css-loader!./~/postcss-loader!', '')
}

export const compileDev = (compiler, port) => {
  compiler.plugin('invalid', function () {
    clearConsole()
    console.log(chalk.yellow('Compiling...'))
    console.log()
  })
  compiler.plugin('done', function (stats) {
    clearConsole()
    const hasErrors = stats.hasErrors()
    const hasWarnings = stats.hasWarnings()
    if (!hasErrors && !hasWarnings) {
      console.log(chalk.green('Compiled successfully!'))
      console.log()
      console.log('The app is running at http://bonde.devel:' + port + '/')
      console.log()
      console.log(chalk.gray('Note that the development build is not optimized.'))
      console.log(chalk.gray('To create a production build, use ' + chalk.cyan('npm run build') + '.'))
      console.log()
      return
    }

    // We have switched off the default Webpack output in WebpackDevServer
    // options so we are going to "massage" the warnings and errors and present
    // them in a readable focused way.
    // We use stats.toJson({}, true) to make output more compact and readable:
    // https://github.com/facebookincubator/create-react-app/issues/401#issuecomment-238291901
    const json = stats.toJson({}, true)
    let formattedErrors = json.errors.map(message =>
      'Error in ' + formatMessage(message)
    )
    const formattedWarnings = json.warnings.map(message =>
      'Warning in ' + formatMessage(message)
    )
    if (hasErrors) {
      console.log(chalk.red('Failed to compile.'))
      console.log()
      if (formattedErrors.some(isLikelyASyntaxError)) {
        // If there are any syntax errors, show just them.
        // This prevents a confusing ESLint parsing error
        // preceding a much more useful Babel syntax error.
        formattedErrors = formattedErrors.filter(isLikelyASyntaxError)
      }
      formattedErrors.forEach(message => {
        console.log(message)
        console.log()
      })
      // If errors exist, ignore warnings.
      return
    }
    if (hasWarnings) {
      console.log(chalk.yellow('Compiled with warnings.'))
      console.log()
      formattedWarnings.forEach(message => {
        console.log(message)
        console.log()
      })
      // Teach some ESLint tricks.
      console.log('You may use special comments to disable some warnings.')
      console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.')
      console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.')
      console.log()
    }
  })

  return compiler
}

function openBrowser (port) {
  if (process.platform === 'darwin') {
    try {
      const appDomain = process.env.APP_DOMAIN || `localhost:${port}`
      // Try our best to reuse existing tab
      // on OS X Google Chrome with AppleScript
      execSync('ps cax | grep "Google Chrome"')
      execSync(
        'osascript ' +
        path.resolve(__dirname, 'chrome.applescript') +
        ` http://${appDomain}/`
      )
      return
    } catch (err) {
      // Ignore errors.
    }
  }
  // Fallback to opn
  // (It will always open new tab)
  opn('http://localhost:' + port + '/')
}

export const startDev = (port, err) => {
  clearConsole()
  if (err) {
    console.log(chalk.red(err))
  }
  console.log(chalk.cyan('Starting development server...'))
  console.log()
  openBrowser(port)
}
<|endoftext|>


<commit_before>var logger      = require("../util/Logger.js").getLogger()
var DBHelper    = require("../database/Helper.js").DBHelper

var bcrypt          = require("bcrypt")
var passport        = require('passport')
var LocalStrategy   = require('passport-local').Strategy

passport.use(new LocalStrategy(
    function(login, password, done) {

        process.nextTick(function () {

            DBHelper.User.findOne({login: login}, function (err, admin) {

                if (err) {
                    return done(err)
                }

                if (!admin) {
                    return done(null, false, {message: 'Unknown user'})
                }

                bcrypt.compare(password, admin.password, function(err, res) {
                    if(err) {
                        return done(err)
                    }
                    if(!res) {
                        return done(null, false, {message: 'Invalid password'})
                    }
                    return done(null, admin)
                })
            })
        })
    }

))

passport.serializeUser(function(user, done) {
    done(null, user.login)
})

passport.deserializeUser(function(login, done) {
    DBHelper.User.findOne({login: login}, function (err, user) {
        done(err, user)
    })
})

function UsersService(app) {

    return function() {

        app.post("/", function(req, res) {

            var user = req.body

            DBHelper.User.find({}, function(err, users) {
                if(err) {
                    return res.error(err)
                }
                if(users.length === 0 || req.isAuthenticated()) {
                    if(user && user.name && user.pass1 && user.pass2) {
                        if(user.pass1.length >= 4 && user.pass1 === user.pass2) {

                            bcrypt.genSalt(10, function(err, salt) {
                                bcrypt.hash(user.pass1, salt, function(err, hash) {

                                    var newUser = {
                                        login       : user.name,
                                        password    : hash
                                    }

                                    DBHelper.User.save(newUser, function(err, savedUser) {
                                        if(err) {
                                            return res.error(err)
                                        }
                                        if(req.user) {
                                            res.redirect('/admin')
                                        } else {
                                            res.redirect('/')
                                        }
                                    })
                                })
                            })
                        } else {
                            res.error(new Error("Passwords mismatch"))
                        }
                    } else {
                        res.error(new Error("Incomplete data"))
                    }
                } else {
                    res.error(new Error("Unauthorized request"), 401)
                }
            })

        })

        /**
         * Login
         */
        app.post('/login', function(req, res, next) {
            passport.authenticate('local', function(err, user, info) {
                if (err) {
                    return next(err)
                }
                if (!user) {
                    if(info.message = 'Invalid password') {
                        var ip = req.ip
                        var ips = req.ips.length>0 ? req.ips : undefined
                        var ipsList = ''
                        if(ips) {
                            ipsList = '(' + ips.join('; ') + ')'
                        }
                        logger.warn('Authentication as [' + req.body.login + '] failed from ip: ' + ip + ipsList)
                    }
                    return res.redirect('/')
                }
                req.logIn(user, function(err) {
                    if (err) {
                        return next(err)
                    }
                    return res.redirect('/admin')
                })
            })(req, res, next)
        })

        /**
         * To know if this is the first login or if the user is authenticated
         */
        app.get("/status", function(req, res) {

            var status = {}

            if(req.user) {
                status.isAuthenticated = true
                return res.send(status)
            }

            DBHelper.User.find({}, function(err, users) {

                if(err) {
                    return res.error(err)
                }

                status.isFirstLogin = users.length === 0
                res.send(status)

            })
        })

        /**
         * Logout
         */
        app.get('/logout', function(req, res){
            req.logOut()
            res.redirect('/')
        })

        /**
         * Return a user
         */
        app.get("/user/:login", function(req, res) {

            var login = req.param("login")

            DBHelper.User.findOne({login: login}, function(err, user) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(user)
                }
            })
        })

        /**
         * Return every users
         */
        app.get("/getUsers", function(req, res) {

            DBHelper.User.find({}, {sort: [['login','asc']]}, function(err, user) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(user)
                }
            })
        })

        /**
         * Search for users
         */
        app.post("/search", function(req, res) {

            var login = req.body.login

            var query = {login: {$regex : login, $options: 'i'}}

            DBHelper.User.find(query, function(err, users) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(users)
                }
            })
        })

    }
}

module.exports = UsersService<commit_msg>deleted obsolete WS API<commit_after>var logger      = require("../util/Logger.js").getLogger()
var DBHelper    = require("../database/Helper.js").DBHelper

var bcrypt          = require("bcrypt")
var passport        = require('passport')
var LocalStrategy   = require('passport-local').Strategy

passport.use(new LocalStrategy(
    function(login, password, done) {

        process.nextTick(function () {

            DBHelper.User.findOne({login: login}, function (err, admin) {

                if (err) {
                    return done(err)
                }

                if (!admin) {
                    return done(null, false, {message: 'Unknown user'})
                }

                bcrypt.compare(password, admin.password, function(err, res) {
                    if(err) {
                        return done(err)
                    }
                    if(!res) {
                        return done(null, false, {message: 'Invalid password'})
                    }
                    return done(null, admin)
                })
            })
        })
    }

))

passport.serializeUser(function(user, done) {
    done(null, user.login)
})

passport.deserializeUser(function(login, done) {
    DBHelper.User.findOne({login: login}, function (err, user) {
        done(err, user)
    })
})

function UsersService(app) {

    return function() {

        /**
         * Login
         */
        app.post('/login', function(req, res, next) {
            passport.authenticate('local', function(err, user, info) {
                if (err) {
                    return next(err)
                }
                if (!user) {
                    if(info.message = 'Invalid password') {
                        var ip = req.ip
                        var ips = req.ips.length>0 ? req.ips : undefined
                        var ipsList = ''
                        if(ips) {
                            ipsList = '(' + ips.join('; ') + ')'
                        }
                        logger.warn('Authentication as [' + req.body.login + '] failed from ip: ' + ip + ipsList)
                    }
                    return res.redirect('/admin')
                }
                req.logIn(user, function(err) {
                    if (err) {
                        return next(err)
                    }
                    return res.redirect('/admin')
                })
            })(req, res, next)
        })

        /**
         * To know if this is the first login or if the user is authenticated
         */
        app.get("/status", function(req, res) {

            var status = {}

            if(req.user) {
                status.isAuthenticated = true
                return res.send(status)
            }

            DBHelper.User.find({}, function(err, users) {

                if(err) {
                    return res.error(err)
                }

                status.isFirstLogin = users.length === 0
                res.send(status)

            })
        })

        /**
         * Logout
         */
        app.get('/logout', function(req, res){
            req.logOut()
            res.redirect('/')
        })

        /**
         * Return a user
         */
        app.get("/user/:login", function(req, res) {

            var login = req.param("login")

            DBHelper.User.findOne({login: login}, function(err, user) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(user)
                }
            })
        })

        /**
         * Return every users
         */
        app.get("/getUsers", function(req, res) {

            DBHelper.User.find({}, {sort: [['login','asc']]}, function(err, user) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(user)
                }
            })
        })

        /**
         * Search for users
         */
        app.post("/search", function(req, res) {

            var login = req.body.login

            var query = {login: {$regex : login, $options: 'i'}}

            DBHelper.User.find(query, function(err, users) {
                if(err) {
                    res.error(err)
                } else {
                    res.send(users)
                }
            })
        })

    }
}

module.exports = UsersService<|endoftext|>


<commit_before>module.exports = {
    '/':{
      controller: require('../../../controllers/application-country'),
      fields: ['apply-uk', 'application-country'],
      controller: require('../../../controllers/go-overseas'),
      backLink: '/../priority_service_170215/get-urgent-passport/premium-online',
      next: '/what-do-you-want-to-do', /* if Yes is selected */
      nextAlt: '../not-eligible', /* if they are from Germany/France */
      nextAltAlt:'what-do-you-want-to-do-overseas'
    },
    '/what-do-you-want-to-do': {
        fields: ['what-to-do'],
        backLink: './',
        next: '/dob'
    },
    '/dob': {
      fields: ['age-day', 'age-year', 'age-month'],
      controller: require('../../../controllers/go-overseas'),
      backLink: './what-do-you-want-to-do',
      next: '/../filter', /* if they are from the UK */
      nextAlt: '../overseas', /* if they are from Germany/France */
      nextAltAlt:'../overseas-not-eligible' /* if they are from Afganistan */
    }
};
<commit_msg>Fix back link on filter-common<commit_after>module.exports = {
    '/':{
      controller: require('../../../controllers/application-country'),
      fields: ['apply-uk', 'application-country'],
      controller: require('../../../controllers/go-overseas'),
      backLink: '/../priority_service_170315/get-urgent-passport/premium-online',
      next: '/what-do-you-want-to-do', /* if Yes is selected */
      nextAlt: '../not-eligible', /* if they are from Germany/France */
      nextAltAlt:'what-do-you-want-to-do-overseas'
    },
    '/what-do-you-want-to-do': {
        fields: ['what-to-do'],
        backLink: './',
        next: '/dob'
    },
    '/dob': {
      fields: ['age-day', 'age-year', 'age-month'],
      controller: require('../../../controllers/go-overseas'),
      backLink: './what-do-you-want-to-do',
      next: '/../filter', /* if they are from the UK */
      nextAlt: '../overseas', /* if they are from Germany/France */
      nextAltAlt:'../overseas-not-eligible' /* if they are from Afganistan */
    }
};
<|endoftext|>


<commit_before>// Copyright 2021, University of Colorado Boulder

'use strict';

/**
 * The base eslint configuration for the project
 * values for rules:
 * 0 - no error
 * 1 - warn
 * 2 - error
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
module.exports = {

  // Use all of the default rules from eslint, unless overridden below.
  extends: 'eslint:recommended', // TODO: is this needed? https://github.com/phetsims/phet-info/issues/150

  // specify that this file is the root of the eslintrc tree, so eslint won't search past this file looking for a file
  // in a parent dir
  root: true, // TODO: is this needed? https://github.com/phetsims/phet-info/issues/150

  // The new rules, overrides, etc.
  rules: {

    // enforce line breaks after opening and before closing array brackets
    // https://eslint.org/docs/rules/array-bracket-newline
    // TODO: enable? semver-major
    'array-bracket-newline': [ 'off', 'consistent' ], // object option alternative: { multiline: true, minItems: 3 }

    // enforce line breaks between array elements
    // https://eslint.org/docs/rules/array-element-newline
    // TODO: enable? semver-major
    'array-element-newline': [ 'off', { multiline: true, minItems: 3 } ],

    // enforce spacing inside single-line blocks
    // https://eslint.org/docs/rules/block-spacing
    'block-spacing': [ 'error', 'always' ],

    // enforce one true brace style
    'brace-style': [ 'error', 'stroustrup', { allowSingleLine: true } ],

    // require camel case names
    camelcase: [ 'error', { properties: 'never', ignoreDestructuring: false } ],

    // enforce or disallow capitalization of the first letter of a comment
    // https://eslint.org/docs/rules/capitalized-comments
    'capitalized-comments': [ 'off', 'never', {
      line: {
        ignorePattern: '.*',
        ignoreInlineComments: true,
        ignoreConsecutiveComments: true
      },
      block: {
        ignorePattern: '.*',
        ignoreInlineComments: true,
        ignoreConsecutiveComments: true
      }
    } ],

    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': [ 'error', 'never' ],

    // requires function names to match the name of the variable or property to which they are
    // assigned
    // https://eslint.org/docs/rules/func-name-matching
    'func-name-matching': [ 'off', 'always', {
      includeCommonJSModuleExports: false,
      considerPropertyDescriptor: true
    } ],

    // require function expressions to have a name
    // https://eslint.org/docs/rules/func-names
    // 'func-names': 'warn',

    // enforces use of function declarations or expressions
    // https://eslint.org/docs/rules/func-style
    // TODO: enable
    'func-style': [ 'off', 'expression' ],

    // enforce consistent line breaks inside function parentheses
    // https://eslint.org/docs/rules/function-paren-newline
    // TODO: https://github.com/phetsims/phet-info/issues/150 should we turn this on?
    // I disabled it for compatibility with some formatting I saw in fourier
    // 'function-paren-newline': [ 'error', 'consistent' ],

    // Blacklist certain identifiers to prevent them being used
    // https://eslint.org/docs/rules/id-blacklist
    // TODO: semver-major, remove once eslint v7.4+ is required
    'id-blacklist': 'off',

    // disallow specified identifiers
    // https://eslint.org/docs/rules/id-denylist
    'id-denylist': 'off',

    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 'off',

    // require identifiers to match the provided regular expression
    'id-match': 'off',

    // Enforce the location of arrow function bodies with implicit returns
    // https://eslint.org/docs/rules/implicit-arrow-linebreak
    // 'implicit-arrow-linebreak': [ 'error', 'beside' ],

    // this option sets a specific tab width for your code
    // https://eslint.org/docs/rules/indent
    indent: [ 'error', 2, {
      SwitchCase: 1,
      VariableDeclarator: 'first',
      outerIIFEBody: 1,

      // MemberExpression: null,
      FunctionDeclaration: {
        parameters: 'first',
        body: 1
      },
      FunctionExpression: {
        parameters: 'first',
        body: 1
      },
      CallExpression: {
        arguments: 1
      },
      ArrayExpression: 'first',
      ObjectExpression: 'first',
      ImportDeclaration: 'first',
      flatTernaryExpressions: true,

      // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js
      ignoredNodes: [ 'ConditionalExpression' ],
      ignoreComments: false
    } ],

    // specify whether double or single quotes should be used in JSX attributes
    // https://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [ 'off', 'prefer-double' ],

    // require a space before & after certain keywords
    'keyword-spacing': [ 'error', {
      before: true,
      after: true,
      overrides: {
        return: { after: true },
        throw: { after: true },
        case: { after: true }
      }
    } ],

    // enforce position of line comments
    // https://eslint.org/docs/rules/line-comment-position
    // TODO: enable?
    'line-comment-position': [ 'off', {
      position: 'above',
      ignorePattern: '',
      applyDefaultPatterns: true
    } ],

    // disallow mixed 'LF' and 'CRLF' as linebreaks
    // https://eslint.org/docs/rules/linebreak-style
    // This is commented out because on windows this is just noisy. Git is already set up to commit the appropriate line breaks.
    // 'linebreak-style': [ 'error', 'unix' ],

    // enforces empty lines around comments
    // PhET devs do not want this to be so strict in general
    // TODO: add to extra knit-picky rules in the future. https://github.com/phetsims/phet-info/issues/150
    // 'lines-around-comment': [ 'error', { beforeLineComment: true } ],

    // require or disallow newlines around directives
    // https://eslint.org/docs/rules/lines-around-directive
    'lines-around-directive': [ 'error', {
      before: 'always',
      after: 'always'
    } ],

    // specify the maximum depth that blocks can be nested
    'max-depth': [ 'off', 4 ],

    // specify the maximum length of a line in your program
    // https://eslint.org/docs/rules/max-len
    'max-len': [ 'off', 120, 2, {
      ignoreUrls: true,
      ignoreComments: false,
      ignoreRegExpLiterals: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true
    } ],

    // specify the max number of lines in a file
    // https://eslint.org/docs/rules/max-lines
    'max-lines': [ 'off', {
      max: 300,
      skipBlankLines: true,
      skipComments: true
    } ],

    // enforce a maximum function length
    // https://eslint.org/docs/rules/max-lines-per-function
    'max-lines-per-function': [ 'off', {
      max: 50,
      skipBlankLines: true,
      skipComments: true,
      IIFEs: true
    } ],

    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 'off',

    // limits the number of parameters that can be used in the function declaration.
    'max-params': [ 'off', 3 ],

    // specify the maximum number of statement allowed in a function
    'max-statements': [ 'off', 10 ],

    // restrict the number of statements per line
    // https://eslint.org/docs/rules/max-statements-per-line
    'max-statements-per-line': [ 'off', { max: 1 } ],

    // enforce a particular style for multiline comments
    // https://eslint.org/docs/rules/multiline-comment-style
    'multiline-comment-style': [ 'off', 'starred-block' ],

    // require multiline ternary
    // https://eslint.org/docs/rules/multiline-ternary
    // TODO: enable?
    'multiline-ternary': [ 'off', 'never' ],

    // allow/disallow an empty newline after var statement
    'newline-after-var': 'off',

    // https://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 'off',

    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // https://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': [ 'error', { ignoreChainWithDepth: 4 } ],

    // disallow use of bitwise operators
    // https://eslint.org/docs/rules/no-bitwise
    'no-bitwise': 'error', // SO MANY ERRORS IN SCENERY

    // disallow use of the continue statement
    // https://eslint.org/docs/rules/no-continue
    // 'no-continue': 'error',

    // disallow comments inline after code
    'no-inline-comments': 'off',

    // disallow if as the only statement in an else block
    // https://eslint.org/docs/rules/no-lonely-if
    // 'no-lonely-if': 'error',

    // disallow un-paren'd mixes of different operators
    // https://eslint.org/docs/rules/no-mixed-operators
    // 'no-mixed-operators': [ 'error', {
    //
    //   // the list of arithmetic groups disallows mixing `%` and `**`
    //   // with other arithmetic operators.
    //   groups: [
    //     [ '%', '**' ],
    //     [ '%', '+' ],
    //     [ '%', '-' ],
    //     [ '%', '*' ],
    //     [ '%', '/' ],
    //     [ '/', '*' ],
    //     [ '&', '|', '<<', '>>', '>>>' ],
    //     [ '==', '!=', '===', '!==' ],
    //     [ '&&', '||' ],
    //   ],
    //   allowSamePrecedence: false
    // } ],

    // disallow use of chained assignment expressions
    // https://eslint.org/docs/rules/no-multi-assign
    // 'no-multi-assign': [ 'error' ],

    // disallow multiple empty lines, only one newline at the end, and no new lines at the beginning
    // https://eslint.org/docs/rules/no-multiple-empty-lines
    'no-multiple-empty-lines': [ 'error', { max: 1, maxBOF: 0, maxEOF: 0 } ],

    // disallow negated conditions
    // https://eslint.org/docs/rules/no-negated-condition
    'no-negated-condition': 'off',

    // disallow nested ternary expressions
    // 'no-nested-ternary': 'error',

    // disallow the use of ternary operators
    'no-ternary': 'off',

    // disallow dangling underscores in identifiers
    // https://eslint.org/docs/rules/no-underscore-dangle
    // 'no-underscore-dangle': [ 'error', {
    //   allow: [],
    //   allowAfterThis: false,
    //   allowAfterSuper: false,
    //   enforceInMethodNames: true
    // } ],

    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // https://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [ 'error', { defaultAssignment: false } ],

    // get rid of extra spaces within lines of code
    // https://eslint.org/docs/rules/no-multi-spaces
    'no-multi-spaces': 'error',

    // enforce line breaks between braces
    // https://eslint.org/docs/rules/object-curly-newline
    'object-curly-newline': [ 'error', {
      ObjectExpression: { minProperties: 4, multiline: true, consistent: true },
      ObjectPattern: { minProperties: 4, multiline: true, consistent: true },
      ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
      ExportDeclaration: { minProperties: 4, multiline: true, consistent: true }
    } ],

    // enforce "same line" or "multiple line" on object properties.
    // https://eslint.org/docs/rules/object-property-newline
    // 'object-property-newline': [ 'error', {
    //   allowAllPropertiesOnSameLine: true,
    // } ],

    // require assignment operator shorthand where possible or prohibit it entirely
    // https://eslint.org/docs/rules/operator-assignment
    'operator-assignment': [ 'error', 'always' ],

    // Requires operator at the beginning of the line in multiline statements
    // https://eslint.org/docs/rules/operator-linebreak
    // 'operator-linebreak': [ 'error', 'after', { overrides: { '=': 'none' } } ],

    // disallow padding within blocks
    // 'padded-blocks': [ 'error', {
    //   blocks: 'never',
    //   classes: 'never',
    //   switches: 'never',
    // }, {
    //   allowSingleLineBlocks: true,
    // } ],

    // Require or disallow padding lines between statements
    // https://eslint.org/docs/rules/padding-line-between-statements
    'padding-line-between-statements': 'off',

    // Disallow the use of Math.pow in favor of the ** operator
    // https://eslint.org/docs/rules/prefer-exponentiation-operator
    // TODO: enable, semver-major when eslint 5 is dropped
    'prefer-exponentiation-operator': 'off',

    // Prefer use of an object spread over Object.assign
    // https://eslint.org/docs/rules/prefer-object-spread
    'prefer-object-spread': 'error',

    // do not require jsdoc
    // https://eslint.org/docs/rules/require-jsdoc
    'require-jsdoc': 'off',

    // requires object keys to be sorted
    'sort-keys': [ 'off', 'asc', { caseSensitive: false, natural: true } ],

    // sort variables within the same declaration block
    'sort-vars': 'off',

    // require or disallow space before function opening parenthesis
    // https://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': [ 'error', {
      anonymous: 'never',
      named: 'never',
      asyncArrow: 'always'
    } ],

    // require or disallow a space immediately following the // or /* in a comment
    // https://eslint.org/docs/rules/spaced-comment
    // TODO: https://github.com/phetsims/phet-info/issues/150
    // SR is opinionated about having a space after line comment slashes, but I saw places in fourier where that
    // isn't happening, so this is commented out for now.
    // 'spaced-comment': [ 'error', 'always', {
    //   line: {
    //     exceptions: [ '-', '+' ],
    //     markers: [ '=', '!', '/' ] // space here to support sprockets directives, slash for TS /// comments
    //   },
    //   block: {
    //     exceptions: [ '-', '+' ],
    //     markers: [ '=', '!', ':', '::' ], // space here to support sprockets directives and flow comment types
    //     balanced: true
    //   }
    // } ],

    // require regex literals to be wrapped in parentheses
    'wrap-regex': 'off'
  },
  env: {
    browser: true,
    es6: true
  },
  parserOptions: {
    ecmaVersion: 8,
    sourceType: 'module'
  }
};<commit_msg>Remove operator-assignment rule, see https://github.com/phetsims/phet-info/issues/156<commit_after>// Copyright 2021, University of Colorado Boulder

'use strict';

/**
 * The base eslint configuration for the project
 * values for rules:
 * 0 - no error
 * 1 - warn
 * 2 - error
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
module.exports = {

  // Use all of the default rules from eslint, unless overridden below.
  extends: 'eslint:recommended', // TODO: is this needed? https://github.com/phetsims/phet-info/issues/150

  // specify that this file is the root of the eslintrc tree, so eslint won't search past this file looking for a file
  // in a parent dir
  root: true, // TODO: is this needed? https://github.com/phetsims/phet-info/issues/150

  // The new rules, overrides, etc.
  rules: {

    // enforce line breaks after opening and before closing array brackets
    // https://eslint.org/docs/rules/array-bracket-newline
    // TODO: enable? semver-major
    'array-bracket-newline': [ 'off', 'consistent' ], // object option alternative: { multiline: true, minItems: 3 }

    // enforce line breaks between array elements
    // https://eslint.org/docs/rules/array-element-newline
    // TODO: enable? semver-major
    'array-element-newline': [ 'off', { multiline: true, minItems: 3 } ],

    // enforce spacing inside single-line blocks
    // https://eslint.org/docs/rules/block-spacing
    'block-spacing': [ 'error', 'always' ],

    // enforce one true brace style
    'brace-style': [ 'error', 'stroustrup', { allowSingleLine: true } ],

    // require camel case names
    camelcase: [ 'error', { properties: 'never', ignoreDestructuring: false } ],

    // enforce or disallow capitalization of the first letter of a comment
    // https://eslint.org/docs/rules/capitalized-comments
    'capitalized-comments': [ 'off', 'never', {
      line: {
        ignorePattern: '.*',
        ignoreInlineComments: true,
        ignoreConsecutiveComments: true
      },
      block: {
        ignorePattern: '.*',
        ignoreInlineComments: true,
        ignoreConsecutiveComments: true
      }
    } ],

    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': [ 'error', 'never' ],

    // requires function names to match the name of the variable or property to which they are
    // assigned
    // https://eslint.org/docs/rules/func-name-matching
    'func-name-matching': [ 'off', 'always', {
      includeCommonJSModuleExports: false,
      considerPropertyDescriptor: true
    } ],

    // require function expressions to have a name
    // https://eslint.org/docs/rules/func-names
    // 'func-names': 'warn',

    // enforces use of function declarations or expressions
    // https://eslint.org/docs/rules/func-style
    // TODO: enable
    'func-style': [ 'off', 'expression' ],

    // enforce consistent line breaks inside function parentheses
    // https://eslint.org/docs/rules/function-paren-newline
    // TODO: https://github.com/phetsims/phet-info/issues/150 should we turn this on?
    // I disabled it for compatibility with some formatting I saw in fourier
    // 'function-paren-newline': [ 'error', 'consistent' ],

    // Blacklist certain identifiers to prevent them being used
    // https://eslint.org/docs/rules/id-blacklist
    // TODO: semver-major, remove once eslint v7.4+ is required
    'id-blacklist': 'off',

    // disallow specified identifiers
    // https://eslint.org/docs/rules/id-denylist
    'id-denylist': 'off',

    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 'off',

    // require identifiers to match the provided regular expression
    'id-match': 'off',

    // Enforce the location of arrow function bodies with implicit returns
    // https://eslint.org/docs/rules/implicit-arrow-linebreak
    // 'implicit-arrow-linebreak': [ 'error', 'beside' ],

    // this option sets a specific tab width for your code
    // https://eslint.org/docs/rules/indent
    indent: [ 'error', 2, {
      SwitchCase: 1,
      VariableDeclarator: 'first',
      outerIIFEBody: 1,

      // MemberExpression: null,
      FunctionDeclaration: {
        parameters: 'first',
        body: 1
      },
      FunctionExpression: {
        parameters: 'first',
        body: 1
      },
      CallExpression: {
        arguments: 1
      },
      ArrayExpression: 'first',
      ObjectExpression: 'first',
      ImportDeclaration: 'first',
      flatTernaryExpressions: true,

      // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js
      ignoredNodes: [ 'ConditionalExpression' ],
      ignoreComments: false
    } ],

    // specify whether double or single quotes should be used in JSX attributes
    // https://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [ 'off', 'prefer-double' ],

    // require a space before & after certain keywords
    'keyword-spacing': [ 'error', {
      before: true,
      after: true,
      overrides: {
        return: { after: true },
        throw: { after: true },
        case: { after: true }
      }
    } ],

    // enforce position of line comments
    // https://eslint.org/docs/rules/line-comment-position
    // TODO: enable?
    'line-comment-position': [ 'off', {
      position: 'above',
      ignorePattern: '',
      applyDefaultPatterns: true
    } ],

    // disallow mixed 'LF' and 'CRLF' as linebreaks
    // https://eslint.org/docs/rules/linebreak-style
    // This is commented out because on windows this is just noisy. Git is already set up to commit the appropriate line breaks.
    // 'linebreak-style': [ 'error', 'unix' ],

    // enforces empty lines around comments
    // PhET devs do not want this to be so strict in general
    // TODO: add to extra knit-picky rules in the future. https://github.com/phetsims/phet-info/issues/150
    // 'lines-around-comment': [ 'error', { beforeLineComment: true } ],

    // require or disallow newlines around directives
    // https://eslint.org/docs/rules/lines-around-directive
    'lines-around-directive': [ 'error', {
      before: 'always',
      after: 'always'
    } ],

    // specify the maximum depth that blocks can be nested
    'max-depth': [ 'off', 4 ],

    // specify the maximum length of a line in your program
    // https://eslint.org/docs/rules/max-len
    'max-len': [ 'off', 120, 2, {
      ignoreUrls: true,
      ignoreComments: false,
      ignoreRegExpLiterals: true,
      ignoreStrings: true,
      ignoreTemplateLiterals: true
    } ],

    // specify the max number of lines in a file
    // https://eslint.org/docs/rules/max-lines
    'max-lines': [ 'off', {
      max: 300,
      skipBlankLines: true,
      skipComments: true
    } ],

    // enforce a maximum function length
    // https://eslint.org/docs/rules/max-lines-per-function
    'max-lines-per-function': [ 'off', {
      max: 50,
      skipBlankLines: true,
      skipComments: true,
      IIFEs: true
    } ],

    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 'off',

    // limits the number of parameters that can be used in the function declaration.
    'max-params': [ 'off', 3 ],

    // specify the maximum number of statement allowed in a function
    'max-statements': [ 'off', 10 ],

    // restrict the number of statements per line
    // https://eslint.org/docs/rules/max-statements-per-line
    'max-statements-per-line': [ 'off', { max: 1 } ],

    // enforce a particular style for multiline comments
    // https://eslint.org/docs/rules/multiline-comment-style
    'multiline-comment-style': [ 'off', 'starred-block' ],

    // require multiline ternary
    // https://eslint.org/docs/rules/multiline-ternary
    // TODO: enable?
    'multiline-ternary': [ 'off', 'never' ],

    // allow/disallow an empty newline after var statement
    'newline-after-var': 'off',

    // https://eslint.org/docs/rules/newline-before-return
    'newline-before-return': 'off',

    // enforces new line after each method call in the chain to make it
    // more readable and easy to maintain
    // https://eslint.org/docs/rules/newline-per-chained-call
    'newline-per-chained-call': [ 'error', { ignoreChainWithDepth: 4 } ],

    // disallow use of bitwise operators
    // https://eslint.org/docs/rules/no-bitwise
    'no-bitwise': 'error', // SO MANY ERRORS IN SCENERY

    // disallow use of the continue statement
    // https://eslint.org/docs/rules/no-continue
    // 'no-continue': 'error',

    // disallow comments inline after code
    'no-inline-comments': 'off',

    // disallow if as the only statement in an else block
    // https://eslint.org/docs/rules/no-lonely-if
    // 'no-lonely-if': 'error',

    // disallow un-paren'd mixes of different operators
    // https://eslint.org/docs/rules/no-mixed-operators
    // 'no-mixed-operators': [ 'error', {
    //
    //   // the list of arithmetic groups disallows mixing `%` and `**`
    //   // with other arithmetic operators.
    //   groups: [
    //     [ '%', '**' ],
    //     [ '%', '+' ],
    //     [ '%', '-' ],
    //     [ '%', '*' ],
    //     [ '%', '/' ],
    //     [ '/', '*' ],
    //     [ '&', '|', '<<', '>>', '>>>' ],
    //     [ '==', '!=', '===', '!==' ],
    //     [ '&&', '||' ],
    //   ],
    //   allowSamePrecedence: false
    // } ],

    // disallow use of chained assignment expressions
    // https://eslint.org/docs/rules/no-multi-assign
    // 'no-multi-assign': [ 'error' ],

    // disallow multiple empty lines, only one newline at the end, and no new lines at the beginning
    // https://eslint.org/docs/rules/no-multiple-empty-lines
    'no-multiple-empty-lines': [ 'error', { max: 1, maxBOF: 0, maxEOF: 0 } ],

    // disallow negated conditions
    // https://eslint.org/docs/rules/no-negated-condition
    'no-negated-condition': 'off',

    // disallow nested ternary expressions
    // 'no-nested-ternary': 'error',

    // disallow the use of ternary operators
    'no-ternary': 'off',

    // disallow dangling underscores in identifiers
    // https://eslint.org/docs/rules/no-underscore-dangle
    // 'no-underscore-dangle': [ 'error', {
    //   allow: [],
    //   allowAfterThis: false,
    //   allowAfterSuper: false,
    //   enforceInMethodNames: true
    // } ],

    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // https://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [ 'error', { defaultAssignment: false } ],

    // get rid of extra spaces within lines of code
    // https://eslint.org/docs/rules/no-multi-spaces
    'no-multi-spaces': 'error',

    // enforce line breaks between braces
    // https://eslint.org/docs/rules/object-curly-newline
    'object-curly-newline': [ 'error', {
      ObjectExpression: { minProperties: 4, multiline: true, consistent: true },
      ObjectPattern: { minProperties: 4, multiline: true, consistent: true },
      ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
      ExportDeclaration: { minProperties: 4, multiline: true, consistent: true }
    } ],

    // enforce "same line" or "multiple line" on object properties.
    // https://eslint.org/docs/rules/object-property-newline
    // 'object-property-newline': [ 'error', {
    //   allowAllPropertiesOnSameLine: true,
    // } ],

    // Requires operator at the beginning of the line in multiline statements
    // https://eslint.org/docs/rules/operator-linebreak
    // 'operator-linebreak': [ 'error', 'after', { overrides: { '=': 'none' } } ],

    // disallow padding within blocks
    // 'padded-blocks': [ 'error', {
    //   blocks: 'never',
    //   classes: 'never',
    //   switches: 'never',
    // }, {
    //   allowSingleLineBlocks: true,
    // } ],

    // Require or disallow padding lines between statements
    // https://eslint.org/docs/rules/padding-line-between-statements
    'padding-line-between-statements': 'off',

    // Disallow the use of Math.pow in favor of the ** operator
    // https://eslint.org/docs/rules/prefer-exponentiation-operator
    // TODO: enable, semver-major when eslint 5 is dropped
    'prefer-exponentiation-operator': 'off',

    // Prefer use of an object spread over Object.assign
    // https://eslint.org/docs/rules/prefer-object-spread
    'prefer-object-spread': 'error',

    // do not require jsdoc
    // https://eslint.org/docs/rules/require-jsdoc
    'require-jsdoc': 'off',

    // requires object keys to be sorted
    'sort-keys': [ 'off', 'asc', { caseSensitive: false, natural: true } ],

    // sort variables within the same declaration block
    'sort-vars': 'off',

    // require or disallow space before function opening parenthesis
    // https://eslint.org/docs/rules/space-before-function-paren
    'space-before-function-paren': [ 'error', {
      anonymous: 'never',
      named: 'never',
      asyncArrow: 'always'
    } ],

    // require or disallow a space immediately following the // or /* in a comment
    // https://eslint.org/docs/rules/spaced-comment
    // TODO: https://github.com/phetsims/phet-info/issues/150
    // SR is opinionated about having a space after line comment slashes, but I saw places in fourier where that
    // isn't happening, so this is commented out for now.
    // 'spaced-comment': [ 'error', 'always', {
    //   line: {
    //     exceptions: [ '-', '+' ],
    //     markers: [ '=', '!', '/' ] // space here to support sprockets directives, slash for TS /// comments
    //   },
    //   block: {
    //     exceptions: [ '-', '+' ],
    //     markers: [ '=', '!', ':', '::' ], // space here to support sprockets directives and flow comment types
    //     balanced: true
    //   }
    // } ],

    // require regex literals to be wrapped in parentheses
    'wrap-regex': 'off'
  },
  env: {
    browser: true,
    es6: true
  },
  parserOptions: {
    ecmaVersion: 8,
    sourceType: 'module'
  }
};<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014, B3log Team
 *
 * 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.
 */
/**
 * @fileoverview recite word.
 *
 * @author Liyuan Li
 * @author Liang Ding
 * @version 1.1.0.2, May 24, 2014
 */
function ReciteWordCtrl($scope, $routeParams) {
    $scope.reciteWords = [];
    $scope.inputWord = "";
    $scope.btnText = "清空";
    $scope.index = 0;
    $scope.hasStudy = false;

    $scope.mattch = function(event) {
        if (event.keyCode === 13) {
            this.studyNext();
            return false;
        }
        if ($scope.inputWord === $scope.letter) {
            $scope.btnText = '确定';
        } else {
            $scope.btnText = '清空';
        }
    };

    $scope.next = function() {
        $scope.index++;
        $scope.sounds = $scope.reciteWords[$scope.index].sounds;
        $scope.letter = $scope.reciteWords[$scope.index].letter;
        $scope.explain = $scope.reciteWords[$scope.index].explain;
        $scope.hasStudy = $scope.reciteWords[$scope.index].hasStudy;
        $scope.inputWord = "";
    };

    $scope.studyNext = function() {
        if ($scope.btnText === "清空") {
            $scope.inputWord = "";
        } else {
            if ($scope.index === $scope.reciteWords.length - 1) {
                alert('已完该课程的学习');

                clazz.finishLearn(reciteWord.currentClassId, reciteWord.currentPlanId);

                window.location = '#lexicon-list';
                return false;
            }
            $scope.index++;
            $scope.sounds = $scope.reciteWords[$scope.index].sounds;
            $scope.letter = $scope.reciteWords[$scope.index].letter;
            $scope.explain = $scope.reciteWords[$scope.index].explain;
            $scope.reciteWords[$scope.index - 1].hasStudy = true;
            $scope.hasStudy = false;
            $scope.inputWord = "";
        }
        $("#reciteInput").focus();
    };

    $scope.prev = function() {
        $scope.index--;
        $scope.sounds = $scope.reciteWords[$scope.index].sounds;
        $scope.letter = $scope.reciteWords[$scope.index].letter;
        $scope.explain = $scope.reciteWords[$scope.index].explain;
        $scope.hasStudy = true;
        $scope.inputWord = "";
    };

    $scope.back = function() {
        if (confirm("要重头开始吗?")) {
            window.location = '#lexicon-list';
        }
    };

    reciteWord.init($scope, $routeParams.classId);
}

var reciteWord = {
    currentPlanId: "",
    currentClassId: "",
    init: function($scope, classId) {
        reciteWord.currentClassId = classId;

        clazz.selectState(classId, function(result) {
            if (!result.selected) { // 没有“选定”该词库

                clazz.selectClass(classId);

                // 首次学习需要用户设置对该词库的学习词数
                tip.show('请设置每课学习的单词数',
                        '<input value="' + result.learnNum + '" />', function() {
                            if (/^[0-9]*[1-9][0-9]*$/.test($("#tipContent input").val())) {
                                var count = parseInt($("#tipContent input").val());
                                if (count < 20 || count > 100) {
                                    alert("请输入20~100的整数");
                                    return false;
                                }
                            } else {
                                 alert("请输入20~100的整数");
                                return false;
                            }

                            clazz.getLearnPlans(classId, count, function(result) {
                                reciteWord.currentPlanId = result.planId;

                                var words = result.words;
                                var reciteWords = [];

                                for (var i = 0, ii = words.length; i < ii; i++) {
                                    reciteWords.push({
                                        letter: words[i].word,
                                        explain: words[i].para,
                                        sounds: words[i].phon
                                    });
                                }

                                $scope.reciteWords = reciteWords;
                                $scope.sounds = $scope.reciteWords[0].sounds;
                                $scope.letter = $scope.reciteWords[0].letter;
                                $scope.explain = $scope.reciteWords[0].explain;
                                $scope.$apply();
                            });
                        });
            } else { // 已经“选定”该词库
                clazz.getLearnPlans(classId, parseInt($("#tipContent input").val()), function(result) {
                    reciteWord.currentPlanId = result.planId;

                    var words = result.words;
                    var reciteWords = [];

                    for (var i = 0, ii = words.length; i < ii; i++) {
                        reciteWords.push({
                            sounds: words[i].phon,
                            letter: words[i].word,
                            explain: words[i].para
                        });
                    }

                    $scope.reciteWords = reciteWords;
                    $scope.sounds = $scope.reciteWords[0].sounds;
                    $scope.letter = $scope.reciteWords[0].letter;
                    $scope.explain = $scope.reciteWords[0].explain;
                    $scope.$apply();
                });
            }
        });
    }
};
<commit_msg>选定词库事务<commit_after>/*
 * Copyright (c) 2014, B3log Team
 *
 * 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.
 */
/**
 * @fileoverview recite word.
 *
 * @author Liyuan Li
 * @author Liang Ding
 * @version 1.1.1.2, May 24, 2014
 */
function ReciteWordCtrl($scope, $routeParams) {
    $scope.reciteWords = [];
    $scope.inputWord = "";
    $scope.btnText = "清空";
    $scope.index = 0;
    $scope.hasStudy = false;

    $scope.mattch = function(event) {
        if (event.keyCode === 13) {
            this.studyNext();
            return false;
        }
        if ($scope.inputWord === $scope.letter) {
            $scope.btnText = '确定';
        } else {
            $scope.btnText = '清空';
        }
    };

    $scope.next = function() {
        $scope.index++;
        $scope.sounds = $scope.reciteWords[$scope.index].sounds;
        $scope.letter = $scope.reciteWords[$scope.index].letter;
        $scope.explain = $scope.reciteWords[$scope.index].explain;
        $scope.hasStudy = $scope.reciteWords[$scope.index].hasStudy;
        $scope.inputWord = "";
    };

    $scope.studyNext = function() {
        if ($scope.btnText === "清空") {
            $scope.inputWord = "";
        } else {
            if ($scope.index === $scope.reciteWords.length - 1) {
                alert('已完该课程的学习');

                clazz.finishLearn(reciteWord.currentClassId, reciteWord.currentPlanId);

                window.location = '#lexicon-list';
                return false;
            }
            $scope.index++;
            $scope.sounds = $scope.reciteWords[$scope.index].sounds;
            $scope.letter = $scope.reciteWords[$scope.index].letter;
            $scope.explain = $scope.reciteWords[$scope.index].explain;
            $scope.reciteWords[$scope.index - 1].hasStudy = true;
            $scope.hasStudy = false;
            $scope.inputWord = "";
        }
        $("#reciteInput").focus();
    };

    $scope.prev = function() {
        $scope.index--;
        $scope.sounds = $scope.reciteWords[$scope.index].sounds;
        $scope.letter = $scope.reciteWords[$scope.index].letter;
        $scope.explain = $scope.reciteWords[$scope.index].explain;
        $scope.hasStudy = true;
        $scope.inputWord = "";
    };

    $scope.back = function() {
        if (confirm("要重头开始吗?")) {
            window.location = '#lexicon-list';
        }
    };

    reciteWord.init($scope, $routeParams.classId);
}

var reciteWord = {
    currentPlanId: "",
    currentClassId: "",
    init: function($scope, classId) {
        reciteWord.currentClassId = classId;

        clazz.selectState(classId, function(result) {
            if (!result.selected) { // 没有“选定”该词库
                // 首次学习需要用户设置对该词库的学习词数
                tip.show('请设置每课学习的单词数',
                        '<input value="' + result.learnNum + '" />', function() {
                            if (/^[0-9]*[1-9][0-9]*$/.test($("#tipContent input").val())) {
                                var count = parseInt($("#tipContent input").val());
                                if (count < 20 || count > 100) {
                                    alert("请输入20~100的整数");
                                    return false;
                                }
                            } else {
                                alert("请输入20~100的整数");
                                return false;
                            }

                            clazz.getLearnPlans(classId, count, function(result) {
                                // 选定词库
                                clazz.selectClass(classId);

                                reciteWord.currentPlanId = result.planId;

                                var words = result.words;
                                var reciteWords = [];

                                for (var i = 0, ii = words.length; i < ii; i++) {
                                    reciteWords.push({
                                        letter: words[i].word,
                                        explain: words[i].para,
                                        sounds: words[i].phon
                                    });
                                }

                                $scope.reciteWords = reciteWords;
                                $scope.sounds = $scope.reciteWords[0].sounds;
                                $scope.letter = $scope.reciteWords[0].letter;
                                $scope.explain = $scope.reciteWords[0].explain;
                                $scope.$apply();
                            });
                        });
            } else { // 已经“选定”该词库
                clazz.getLearnPlans(classId, parseInt($("#tipContent input").val()), function(result) {
                    reciteWord.currentPlanId = result.planId;

                    var words = result.words;
                    var reciteWords = [];

                    for (var i = 0, ii = words.length; i < ii; i++) {
                        reciteWords.push({
                            sounds: words[i].phon,
                            letter: words[i].word,
                            explain: words[i].para
                        });
                    }

                    $scope.reciteWords = reciteWords;
                    $scope.sounds = $scope.reciteWords[0].sounds;
                    $scope.letter = $scope.reciteWords[0].letter;
                    $scope.explain = $scope.reciteWords[0].explain;
                    $scope.$apply();
                });
            }
        });
    }
};
<|endoftext|>


<commit_before>
module("About Asserts (topics/about_asserts.js)");

test("ok", function() {
    ok(__ === true, 'what will satisfy the ok assertion?');
});

test("not ok", function() {
    ok(__ === false, 'what is a false value?');
});

test("equal", function() {
    equal(__, 1 + 1, 'what will satisfy the equal assertion?');
});
<commit_msg>first commit<commit_after>
module("About Asserts (topics/about_asserts.js)");

test("ok", function() {
    ok(true === true, 'what will satisfy the ok assertion?');
});

test("not ok", function() {
    ok(false === false, 'what is a false value?');
});

test("equal", function() {
    equal(2, 1 + 1, 'what will satisfy the equal assertion?');
});
<|endoftext|>


<commit_before>'use strict'

const Sequelize = require('sequelize')
const {STRING, TEXT, INTEGER, DECIMAL} = require('sequelize')

module.exports = db => db.define('product', {
  title: {
    type: STRING,
    allowNull: false
  },

  description : TEXT,

  price: {
    type: DECIMAL(10,2),
    defaultValue: 0.00
  },

  inventory: INTEGER,

  photo: {
    type: STRING,
    allowNull: false
  },

  gender: STRING,

  clothingType: STRING

})

module.exports.associations = (Product, {Order, Review}) => {
  Product.hasMany(Review, {as: 'ProductReviews'})
  Product.belongsToMany(Order, {through: 'ProductOrders'})
}
<commit_msg>minor changes<commit_after>'use strict'

const Sequelize = require('sequelize')
const {STRING, TEXT, INTEGER, DECIMAL} = require('sequelize')

module.exports = db => db.define('products', {
  title: {
    type: STRING,
    allowNull: false
  },

  description : TEXT,

  price: {
    type: DECIMAL(10,2),
    defaultValue: 0.00
  },

  inventory: INTEGER,

  photo: {
    type: STRING,
    allowNull: false
  },

  gender: STRING,

  clothingType: STRING

})

module.exports.associations = (Product, {Order, Review}) => {
  Product.hasMany(Review, {as: 'ProductReviews'})
  Product.belongsToMany(Order, {through: 'ProductOrders'})
}
<|endoftext|>