<commit_before>/* global ko */
import ClientTabViewModel from './client-tab-view-model';
import VerbEditorTabViewModel from './verb-editor-tab-view-model';
import FunctionEditorTabViewModel from './function-editor-tab-view-model';
import SearchViewModel from './search-view-model';

export class TabsViewModel {
  constructor() {
    // Observables

    this.activeTab = ko.observable();
    this.tabs = ko.observableArray([new ClientTabViewModel(this)]);
    this.searchViewModel = ko.observable();

    // Computeds

    this.activeViewModel = ko.computed(this.computeActiveViewModel.bind(this));
    this.hidden = ko.computed(this.computeHidden.bind(this));
    this.visible = ko.computed(this.computeVisible.bind(this));
    this.tabPaneClasses = ko.computed(this.computeTabPaneClasses.bind(this));
    this.templateBinding = ko.computed(this.computeTemplateBinding.bind(this));
    this.activeSocket = ko.computed(this.computeActiveSocket.bind(this));
    this.anyTabLoggedIn = ko.computed(this.computedAnyTabLoggedIn.bind(this));

    // Initialization

    this.activeTab(this.tabs()[0]);
  }

  computeActiveViewModel() {
    const searchViewModel = this.searchViewModel();
    if (searchViewModel) {
      return searchViewModel;
    }
    const activeTab = this.activeTab();
    return activeTab ? activeTab.viewModel : this;
  }

  computeHidden() {
    const l = this.tabs().length;
    const t = this.tabs()[0];

    return l === 0 || (l === 1 && t.hideIfOnlyMe());
  }

  computeVisible() {
    return !this.hidden();
  }

  computeTabPaneClasses() {
    const activeTab = this.activeTab();
    let classes;

    if (activeTab) {
      classes = activeTab.tabPaneClasses();
    } else {
      classes = [];
    }

    if (this.visible()) {
      classes.push('tabs-visible');
    }

    return classes.join(' ');
  }

  computeTemplateBinding() {
    const activeTab = this.activeTab();
    if (activeTab) {
      return activeTab.templateBinding();
    }
    return { name: 'no-tabs', data: this };
  }

  computeActiveSocket() {
    const activeViewModel = this.activeViewModel();
    return activeViewModel.socket;
  }

  computedAnyTabLoggedIn() {
    return this.tabs().reduce((result, tab) =>
      result || (tab.viewModel && tab.viewModel.loggedIn && tab.viewModel.loggedIn())
    , false);
  }

  tabPaneClasses() {
    return [];
  }

  newClientTab() {
    const newTab = new ClientTabViewModel(this);
    this.tabs.push(newTab);
    newTab.select();
  }

  newEditVerbTab(socket, data) {
    const newTab = new VerbEditorTabViewModel(this, socket, data);
    this.tabs.push(newTab);
    newTab.select();
  }

  newEditFunctionTab(socket, data) {
    const newTab = new FunctionEditorTabViewModel(this, socket, data);
    this.tabs.push(newTab);
    newTab.select();
  }

  closeTab(tab) {
    const l = this.tabs().length - 1;
    let i = this.tabs.indexOf(tab);

    if (i === l) { i--; }
    this.tabs.remove(tab);
    if (l > 0) {
      this.tabs()[i].select();
    } else {
      this.activeTab(null);
    }
  }

  onKeyDown(...args) {
    const event = args[1];
    const activeViewModel = this.activeViewModel();
    const key = typeof event.which === 'undefined' ? event.keyCode : event.which;
    const meta = event.metaKey;
    const ctrl = event.ctrlKey;
    const pKey = key === 80;

    // open the search box on cmd+p (or ctrl+p) if it's not already open
    if ((meta && pKey) || (ctrl && pKey)) {
      if (!this.searchViewModel()) {
        this.searchViewModel(new SearchViewModel(this, this.activeSocket()));<commit_msg>Fix #5 : close tabs with same name, unless they are marked dirty<commit_after>/* global ko */
import ClientTabViewModel from './client-tab-view-model';
import VerbEditorTabViewModel from './verb-editor-tab-view-model';
import FunctionEditorTabViewModel from './function-editor-tab-view-model';
import SearchViewModel from './search-view-model';

export class TabsViewModel {
  constructor() {
    // Observables

    this.activeTab = ko.observable();
    this.tabs = ko.observableArray([new ClientTabViewModel(this)]);
    this.searchViewModel = ko.observable();

    // Computeds

    this.activeViewModel = ko.computed(this.computeActiveViewModel.bind(this));
    this.hidden = ko.computed(this.computeHidden.bind(this));
    this.visible = ko.computed(this.computeVisible.bind(this));
    this.tabPaneClasses = ko.computed(this.computeTabPaneClasses.bind(this));
    this.templateBinding = ko.computed(this.computeTemplateBinding.bind(this));
    this.activeSocket = ko.computed(this.computeActiveSocket.bind(this));
    this.anyTabLoggedIn = ko.computed(this.computedAnyTabLoggedIn.bind(this));

    // Initialization

    this.activeTab(this.tabs()[0]);
  }

  computeActiveViewModel() {
    const searchViewModel = this.searchViewModel();
    if (searchViewModel) {
      return searchViewModel;
    }
    const activeTab = this.activeTab();
    return activeTab ? activeTab.viewModel : this;
  }

  computeHidden() {
    const l = this.tabs().length;
    const t = this.tabs()[0];

    return l === 0 || (l === 1 && t.hideIfOnlyMe());
  }

  computeVisible() {
    return !this.hidden();
  }

  computeTabPaneClasses() {
    const activeTab = this.activeTab();
    let classes;

    if (activeTab) {
      classes = activeTab.tabPaneClasses();
    } else {
      classes = [];
    }

    if (this.visible()) {
      classes.push('tabs-visible');
    }

    return classes.join(' ');
  }

  computeTemplateBinding() {
    const activeTab = this.activeTab();
    if (activeTab) {
      return activeTab.templateBinding();
    }
    return { name: 'no-tabs', data: this };
  }

  computeActiveSocket() {
    const activeViewModel = this.activeViewModel();
    return activeViewModel.socket;
  }

  computedAnyTabLoggedIn() {
    return this.tabs().reduce((result, tab) =>
      result || (tab.viewModel && tab.viewModel.loggedIn && tab.viewModel.loggedIn())
    , false);
  }

  tabPaneClasses() {
    return [];
  }

  newClientTab() {
    const newTab = new ClientTabViewModel(this);
    this.tabs.push(newTab);
    newTab.select();
  }

  newEditVerbTab(socket, data) {
    const newTab = new VerbEditorTabViewModel(this, socket, data);
    this.closeSameTabs(newTab);
    this.tabs.push(newTab);
    newTab.select();
  }

  newEditFunctionTab(socket, data) {
    const newTab = new FunctionEditorTabViewModel(this, socket, data);
    this.closeSameTabs(newTab);
    this.tabs.push(newTab);
    newTab.select();
  }

  closeTab(tab) {
    const l = this.tabs().length - 1;
    let i = this.tabs.indexOf(tab);

    if (i === l) { i--; }
    this.tabs.remove(tab);
    if (l > 0) {
      this.tabs()[i].select();
    } else {
      this.activeTab(null);
    }
  }

  closeSameTabs(tab) {
    // Close other tabs with same names, only if they are not edited
    this.tabs().forEach(t => {
      if (t.name() === tab.name() && !t.dirty()) {
        this.closeTab(t);
      }
    });
  }

  onKeyDown(...args) {
    const event = args[1];
    const activeViewModel = this.activeViewModel();
    const key = typeof event.which === 'undefined' ? event.keyCode : event.which;
    const meta = event.metaKey;
    const ctrl = event.ctrlKey;
    const pKey = key === 80;

    // open the search box on cmd+p (or ctrl+p) if it's not already open
    if ((meta && pKey) || (ctrl && pKey)) {
      if (!this.searchViewModel()) {
        this.searchViewModel(new SearchViewModel(this, this.activeSocket()));
{'fraction_non_alphanumeric': 0.14683815648445875, 'fraction_numerical': 0.002545551982851018, 'mean_word_length': 3.1587743732590527, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5610092', 'n_tokens_mistral': 2415, 'n_tokens_neox': 2301, 'n_words': 469}