<commit_before>'use strict';

class PopupText {
  constructor(options) {
    this.options = options;
    this.triggers = [];
    this.timers = [];
    this.inCombat = false;
    this.resetWhenOutOfCombat = true;
    this.infoText = document.getElementById('popup-text-info');
    this.alertText = document.getElementById('popup-text-alert');
    this.alarmText = document.getElementById('popup-text-alarm');

    this.kMaxRowsOfText = 2;

    this.Reset();
  }

  SetTimelineLoader(timelineLoader) {
    this.timelineLoader = timelineLoader;
  }

  OnPlayerChange(e) {
    if (this.job != e.detail.job || this.me != e.detail.name)
      this.OnJobChange(e);
    this.data.currentHP = e.detail.currentHP;
  }

  OnDataFilesRead(e) {
    this.triggerSets = Options.Triggers;
    for (let filename in e.detail.files) {
      // Reads from the data/triggers/ directory.
      if (!filename.startsWith('triggers/'))
        continue;

      let text = e.detail.files[filename];
      let json;
      try {
        json = eval(text);
      } catch (exception) {
        console.log('Error parsing JSON from ' + filename + ': ' + exception);
        continue;
      }
      if (typeof json != 'object' || !(json.length >= 0)) {
        console.log('Unexpected JSON from ' + filename + ', expected an array');
        continue;
      }
      for (let i = 0; i < json.length; ++i) {
        if (!('zoneRegex' in json[i])) {
          console.log('Unexpected JSON from ' + filename + ', expected a zoneRegex');
          continue;
        }
        if (!('triggers' in json[i])) {
          console.log('Unexpected JSON from ' + filename + ', expected a triggers');
          continue;
        }
        if (typeof json[i].triggers != 'object' || !(json[i].triggers.length >= 0)) {
          console.log('Unexpected JSON from ' + filename + ', expected triggers to be an array');
          continue;
        }
      }
      Array.prototype.push.apply(this.triggerSets, json);
    }
  }

  OnZoneChange(e) {
    this.zoneName = e.detail.zoneName;
    this.ReloadTimelines();
  }

  ReloadTimelines() {
    // Datafiles, job, and zone must be loaded.
    if (!this.triggerSets || !this.me || !this.zoneName)
      return;

    this.Reset();

    // Drop the triggers and timelines from the previous zone, so we can add new ones.
    this.triggers = [];
    let timelineFiles = [];
    let timelines = [];
    let replacements = [];
    let timelineTriggers = [];
    this.resetWhenOutOfCombat = true;

    // Recursively/iteratively process timeline entries for triggers.
    // Functions get called with data, arrays get iterated, strings get appended.
    let addTimeline = (function(obj) {
      if (Array.isArray(obj)) {
        for (let i = 0; i < obj.length; ++i)
          addTimeline(obj[i]);
      } else if (typeof(obj) == 'function') {
        addTimeline(obj(this.data));
      } else if (obj) {
        timelines.push(obj);
      }
    }).bind(this);

    let locale = this.options.Language || 'en';
    // construct something like regexEn or regexFr.
    let regexLocale = 'regex' + locale.charAt(0).toUpperCase() + locale.slice(1);

    for (let i = 0; i < this.triggerSets.length; ++i) {
      let set = this.triggerSets[i];
      if (this.zoneName.search(set.zoneRegex) >= 0) {
        // Adjust triggers for the locale.
        if (set.triggers) {
          for (let j = 0; j < set.triggers.length; ++j) {
            // Add an additional resolved regex here to save
            // time later.  This will clobber each time we
            // load this, but that's ok.
            let trigger = set.triggers[j];
            // Locale-based regex takes precedence.
            let regex = trigger[regexLocale] ? trigger[regexLocale] : trigger.regex;
            trigger.localRegex = Regexes.Parse(regex);
          }
        }
        // Save the triggers from each set that matches.
        Array.prototype.push.apply(this.triggers, set.triggers);
        // And set the timeline files/timelines from each set that matches.
        if (set.timelineFile)
          timelineFiles.push(set.timelineFile);
        if (set.timeline)
          addTimeline(set.timeline);
        if (set.timelineReplace)
          Array.prototype.push.apply(replacements, set.timelineReplace);
        if (set.timelineTriggers)
          Array.prototype.push.apply(timelineTriggers, set.timelineTriggers);
        if (set.resetWhenOutOfCombat !== undefined)
          this.resetWhenOutOfCombat &= set.resetWhenOutOfCombat;
      }
    }

    this.timelineLoader.SetTimelines(timelineFiles, timelines, replacements, timelineTriggers);
  }

  OnJobChange(e) {<commit_msg>Add error when regex not specified in a trigger<commit_after>'use strict';

class PopupText {
  constructor(options) {
    this.options = options;
    this.triggers = [];
    this.timers = [];
    this.inCombat = false;
    this.resetWhenOutOfCombat = true;
    this.infoText = document.getElementById('popup-text-info');
    this.alertText = document.getElementById('popup-text-alert');
    this.alarmText = document.getElementById('popup-text-alarm');

    this.kMaxRowsOfText = 2;

    this.Reset();
  }

  SetTimelineLoader(timelineLoader) {
    this.timelineLoader = timelineLoader;
  }

  OnPlayerChange(e) {
    if (this.job != e.detail.job || this.me != e.detail.name)
      this.OnJobChange(e);
    this.data.currentHP = e.detail.currentHP;
  }

  OnDataFilesRead(e) {
    this.triggerSets = Options.Triggers;
    for (let filename in e.detail.files) {
      // Reads from the data/triggers/ directory.
      if (!filename.startsWith('triggers/'))
        continue;

      let text = e.detail.files[filename];
      let json;
      try {
        json = eval(text);
      } catch (exception) {
        console.log('Error parsing JSON from ' + filename + ': ' + exception);
        continue;
      }
      if (typeof json != 'object' || !(json.length >= 0)) {
        console.log('Unexpected JSON from ' + filename + ', expected an array');
        continue;
      }
      for (let i = 0; i < json.length; ++i) {
        if (!('zoneRegex' in json[i])) {
          console.log('Unexpected JSON from ' + filename + ', expected a zoneRegex');
          continue;
        }
        if (!('triggers' in json[i])) {
          console.log('Unexpected JSON from ' + filename + ', expected a triggers');
          continue;
        }
        if (typeof json[i].triggers != 'object' || !(json[i].triggers.length >= 0)) {
          console.log('Unexpected JSON from ' + filename + ', expected triggers to be an array');
          continue;
        }
      }
      Array.prototype.push.apply(this.triggerSets, json);
    }
  }

  OnZoneChange(e) {
    this.zoneName = e.detail.zoneName;
    this.ReloadTimelines();
  }

  ReloadTimelines() {
    // Datafiles, job, and zone must be loaded.
    if (!this.triggerSets || !this.me || !this.zoneName)
      return;

    this.Reset();

    // Drop the triggers and timelines from the previous zone, so we can add new ones.
    this.triggers = [];
    let timelineFiles = [];
    let timelines = [];
    let replacements = [];
    let timelineTriggers = [];
    this.resetWhenOutOfCombat = true;

    // Recursively/iteratively process timeline entries for triggers.
    // Functions get called with data, arrays get iterated, strings get appended.
    let addTimeline = (function(obj) {
      if (Array.isArray(obj)) {
        for (let i = 0; i < obj.length; ++i)
          addTimeline(obj[i]);
      } else if (typeof(obj) == 'function') {
        addTimeline(obj(this.data));
      } else if (obj) {
        timelines.push(obj);
      }
    }).bind(this);

    let locale = this.options.Language || 'en';
    // construct something like regexEn or regexFr.
    let regexLocale = 'regex' + locale.charAt(0).toUpperCase() + locale.slice(1);

    for (let i = 0; i < this.triggerSets.length; ++i) {
      let set = this.triggerSets[i];
      if (this.zoneName.search(set.zoneRegex) >= 0) {
        // Adjust triggers for the locale.
        if (set.triggers) {
          for (let j = 0; j < set.triggers.length; ++j) {
            // Add an additional resolved regex here to save
            // time later.  This will clobber each time we
            // load this, but that's ok.
            let trigger = set.triggers[j];

            if (!trigger.regex)
              console.error('Trigger ' + trigger.id + ': has no regex property specified');

            // Locale-based regex takes precedence.
            let regex = trigger[regexLocale] ? trigger[regexLocale] : trigger.regex;
            if (!regex) {
              console.error('Trigger ' + trigger.id + ': undefined ' + regexLocale);
              continue;
            }
            trigger.localRegex = Regexes.Parse(regex);
          }
        }
        // Save the triggers from each set that matches.
        Array.prototype.push.apply(this.triggers, set.triggers);
        // And set the timeline files/timelines from each set that matches.
        if (set.timelineFile)
          timelineFiles.push(set.timelineFile);
        if (set.timeline)
          addTimeline(set.timeline);
        if (set.timelineReplace)
          Array.prototype.push.apply(replacements, set.timelineReplace);
        if (set.timelineTriggers)
          Array.prototype.push.apply(timelineTriggers, set.timelineTriggers);
        if (set.resetWhenOutOfCombat !== undefined)
          this.resetWhenOutOfCombat &= set.resetWhenOutOfCombat;
      }
    }

    this.timelineLoader.SetTimelines(timelineFiles, timelines, replacements, timelineTriggers);
  }

  OnJobChange(e) {
{'fraction_non_alphanumeric': 0.12914975543761056, 'fraction_numerical': 0.002081382037673015, 'mean_word_length': 2.322959889349931, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, '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': '5771235', 'n_tokens_mistral': 2911, 'n_tokens_neox': 2834, 'n_words': 696}