PageSourceSearch

https://lingolab.online/js/llo.js?version=1790901523

js lingolab.online collected 2026-10-02 00:39:06 UTC 37,391 bytes, 1,284 lines download raw bytes

1var app = new Vue({
2  mixins: [mainMixin],
3  el: "#app",
4  data: {
5    showStudentNames:true,
6    mode_shown: true,
7    mode_hidden: true,
8    mode_stellar: true,
9    audio_off: true,
10    audio_target: true,
11    audio_definition: true,
12    infoWasShown: false,
13    selectedTrackerStudent: "",
14    selectedTrackerClass: "",
15    selectedTrackerSet: "",
16    manageQuizzesFilter: "",
17    manageSetsFilter: "",
18    trackerSetsShown: false,
19    assignmentEditId: "",
20    checkAll: false,
21    customGridData: [],
22    termsTable: null,
23    trackerStudentsFilter: "",
24    trackerClassesFilter: "",
25    trackerSetsFilter: "",
26    sharesWithMe: [],
27    trackerview: "students",
28    importSetId: "",
29    deletingSet: false,
30    userSets: [],
31    toImport: "",
32    terms: [],
33    setName: "",
34    setLangs: {
35      definition: "en",
36      term: "en",
37    },
38    editId: "",
39    lang_filter: [
40      "ar",
41      "zh-CN",
42      "zh-pi",
43      "zh-TW",
44      "de",
45      "fr",
46      "it",
47      "ja",
48      "ko",
49      "ru",
50      "es",
51      "th",
52      "tr",
53      "en",
54      "vi",
55      "pt",
56    ],
57    savingSet: false,
58    voices: [
59      "Arabic Male",
60      "Australian Female",
61      "Chinese Female",
62      "Deutsch Female",
63      "French Female",
64      "Italian Female",
65      "Japanese Female",
66      "Korean Female",
67      "Russian Female",
68      "Spanish Female",
69      "Thai Female",
70      "Turkish Female",
71      "UK English Female",
72      "UK English Male",
73      "US English Female",
74      "Vietnamese Male",
75    ],
76    modes: {
77      full: "Full Words",
78      fandl: "Hidden Letters",
79      star: "Stellar Speller",
80    },
81    assignment: {
82      instructions:
83        "(Customize instructions here!) LingoLab: One point is deducted for each mistake made. If an answering word that you select is incorrect, the correct word is then shown to you by flashing yellow. You should click it to continue answering the question. Stellar Speller: Spell out the target word or phrase. 1 point is deducted for each mistake.",
84      show_score: false,
85      shuffle_order: false,
86      ppq: 3,
87      classes: "",
88      title: "",
89      set: {},
90      mode: "full",
91      tts: "off",
92      tts_voice: "US English Female",
93      elements: {
94        definition: true,
95        image: true,
96      },
97    },
98    customSetId: "",
99    defaultSets: [],
100    screen: "",
101    subscreen: "default_sets",
102    setType: "sentence",
103    languages: languages,
104    user: user,
105    assignments: assignments,
106  },
107  computed: {
108    uniqueSharedSets: function () {
109      return this.getSharedSets().filter((obj, pos, arr) => {
110        return arr.map((mapObj) => mapObj["name"]).indexOf(obj["name"]) === pos;
111      });
112    },
113  },
114  watch: {
115    infoWasShown: function () {
116      if (this.infoWasShown) {
117        localStorage.setItem("infoWasShown", "true");
118      }
119    },
120    trackerview: function () {
121      this.resetTracker();
122    },
123    trackerStudentsFilter: function () {
124      Vue.nextTick(function () {
125        $(".progress-tracker-students").trigger("update");
126      });
127    },
128    screen: function () {
129      var vm = this;
130
131      if (this.screen == "create_set") {
132        Vue.nextTick(function () {
133          var data = [];
134          for (var i = 0; i < 20; i++) {
135            data.push(["", "", ""]);
136          }
137
138          if (vm.customGridData.length) {
139            data = vm.customGridData;
140            vm.customGridData = [];
141          }
142          var container = document.getElementById("importTermsTable");
143
144          vm.termsTable = new Handsontable(container, {
145            minSpareRows: 5,
146            allowEmpty: false,
147            data: data,
148            colWidths: [240, 240, 240],
149            contextMenu: ["remove_row"],
150            rowHeaders: false,
151            colHeaders: ["Answer", "Question", "Image URL (Optional)"],
152            filters: false,
153            dropdownMenu: false,
154            licenseKey: "non-commercial-and-evaluation",
155            columnSorting: true,
156          });
157        });
158      }
159    },
160    "assignment.ppq": function () {
161      if (this.assignment.ppq < 1) {
162        this.assignment.ppq = 1;
163      }
164      if (this.assignment.ppq > 100) {
165        this.assignment.ppq = 100;
166      }
167    },
168    "assignment.set": function () {
169      if (!this.hasImages(this.assignment.set)) {
170        this.assignment.elements.image = false;
171      } else {
172        this.assignment.elements.image = true;
173      }
174    },
175  },
176  methods: {
177    downloadSetsTable(idx, filename) {
178      $("#progress-table-" + idx).table2csv("download", {
179        filename: filename + ".csv",
180      });
181    },
182    downloadStudentsTable() {
183      $("#students-table").table2csv("download", {
184        filename: this.trackerview + ".csv",
185      });
186    },
187    sort_tables() {
188      if (this.screen == "manage") {
189        $(".manage-tests").tablesorter({
190          theme: "bootstrap",
191          sortList: [[0, 0]],
192        });
193      }
194      if (this.screen == "manage_sets") {
195        $(".manage-sets").tablesorter({
196          theme: "bootstrap",
197          sortList: [[2, 1]],
198        });
199      }
200    },
201    get_url_param: function (name, url) {
202      if (!url) url = window.location.href;
203      name = name.replace(/[\[\]]/g, "\\$&");
204      var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
205        results = regex.exec(url);
206      if (!results) return null;
207      if (!results[2]) return "";
208      return decodeURIComponent(results[2].replace(/\+/g, " "));
209    },
210    duplicateAssignment: function (assignment_id) {
211      var vm = this;
212      axios
213        .post("/lingolab_online.class.php", {
214          action: "duplicateAssignment",
215          data: {
216            assignment_id: assignment_id,
217          },
218        })
219        .then(function (response) {
220          if (response.data.result == "success") {
221            Swal.fire({
222              reverseButtons: true,
223              title: "Success",
224              html:
225                "Assignment copied to:<br/><a target='_blank' href='https://lingolab.online/submit.php?id=" +
226                response.data.id +
227                "'>https://lingolab.online/submit.php?id=" +
228                response.data.id +
229                "</a>",
230              type: "success",
231            });
232            vm.assignments = response.data.assignments;
233            vm.screen = "manage";
234          } else {
235            Swal.fire({
236              reverseButtons: true,
237              title: "Error",
238              text: response.data.message,
239              type: "error",
240            });
241          }
242        })
243        .catch(function (error) {
244          console.error(error);
245        });
246    },
247    createNewAssignment: function () {
248      this.assignmentEditId = "";
249      this.assignment = {
250        instructions:
251          "(Customize instructions here!) LingoLab: One point is deducted for each mistake made. If an answering word that you select is incorrect, the correct word is then shown to you by flashing yellow. You should click it to continue answering the question. Stellar Speller: Spell out the target word or phrase. 1 point is deducted for each mistake.",
252        show_score: false,
253        shuffle_order: false,
254        ppq: 3,
255        classes: "",
256        title: "",
257        set: {},
258        mode: "full",
259        tts: "off",
260        tts_voice: "US English Female",
261        elements: {
262          definition: true,
263          image: true,
264        },
265      };
266      this.screen = "create";
267    },
268    editAssignment: function (assignment) {
269      this.assignmentEditId = assignment.id;
270      this.assignment = assignment.json;
271      this.screen = "create";
272    },
273    createNewSet: function () {
274      this.reset();
275      this.screen = "create_set";
276    },
277    resetChecked: function () {
278      var vm = this;
279      Swal.fire({
280        title:
281          "Are you sure you wish to reset the Current Total for " +
282          vm.getChecked().length +
283          " checked students?",
284        showCancelButton: true,
285        reverseButtons: true,
286      }).then((result) => {
287        if (result.value) {
288          Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
289          axios
290            .post("/lingolab_online.class.php", {
291              action: "resetChecked",
292              data: {
293                subs: vm.getChecked(),
294              },
295            })
296            .then(function (response) {
297              $("#checkAll").prop("checked", false);
298              $(".share-check").prop("checked", false);
299              if (response.data.result == "success") {
300                vm.loadTracker();
301              } else {
302                Swal.fire({
303                  reverseButtons: true,
304                  title: "Error",
305                  text: response.data.message,
306                  type: "error",
307                });
308              }
309            })
310            .catch(function (error) {
311              console.error(error);
312            });
313        }
314      });
315    },
316    removeChecked: function () {
317      var vm = this;
318      Swal.fire({
319        title:
320          "Are you sure you wish to remove " +
321          vm.getChecked().length +
322          " checked students from your tracker?",
323        showCancelButton: true,
324        reverseButtons: true,
325      }).then((result) => {
326        if (result.value) {
327          Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
328          axios
329            .post("/lingolab_online.class.php", {
330              action: "removeChecked",
331              data: {
332                subs: vm.getChecked(),
333              },
334            })
335            .then(function (response) {
336              $("#checkAll").prop("checked", false);
337              $(".share-check").prop("checked", false);
338              if (response.data.result == "success") {
339                vm.loadTracker();
340              } else {
341                Swal.fire({
342                  reverseButtons: true,
343                  title: "Error",
344                  text: response.data.message,
345                  type: "error",
346                });
347              }
348            })
349            .catch(function (error) {
350              console.error(error);
351            });
352        }
353      });
354    },
355    getChecked: function () {
356      return this.sharesWithMe
357        .filter(function (e) {
358          return e.checked;
359        })
360        .map(function (e) {
361          return e.sub;
362        });
363    },
364    hasChecked: function () {
365      return this.sharesWithMe.filter(function (e) {
366        return e.checked;
367      }).length;
368    },
369    toggleCheck: function (idx) {
370      this.sharesWithMe[idx].checked = !this.sharesWithMe[idx].checked;
371    },
372    toggleCheckAll: function () {
373      var vm = this;
374      vm.checkAll = !vm.checkAll;
375      this.sharesWithMe.forEach(function (obj, idx) {
376        if ($("#check_" + idx).length) {
377          $("#check_" + idx).prop("checked", vm.checkAll);
378          obj.checked = vm.checkAll;
379        }
380      });
381    },
382    importCustomSet: function () {
383      var vm = this;
384      axios
385        .get(
386          "https://study.lingolab.online/lingolabco.class.php?action=getSet&set=" +
387            this.importSetId
388        )
389        .then(function (response) {
390          vm.importSetId = "";
391          if (response.data.set) {
392            vm.customGridData = [];
393
394            response.data.set.terms.forEach(function (obj) {
395              vm.customGridData.push([
396                obj.term,
397                obj.definition,
398                obj.image ? obj.image : "",
399              ]);
400            });
401
402            vm.screen = "create_set";
403            vm.setName = response.data.set.name;
404            vm.setLangs = response.data.set.languages;
405          } else {
406            Swal.fire({
407              title: "Error",
408              text: "Invalid set!",
409              type: "error",
410            });
411          }
412        })
413        .catch(function (error) {
414          console.log(error);
415        });
416    },
417    removeProgressRecord: function (set, student) {
418      var vm = this;
419      Swal.fire({
420        reverseButtons: true,
421        title: "Warning",
422        text:
423          "Are you sure you wish to remove progress on '" +
424          set.name +
425          "' for '" +
426          student.name +
427          "'?",
428        showCancelButton: true,
429      }).then(function (value) {
430        if (value.value) {
431          Swal.fire({
432            onOpen: () => {
433              Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
434            },
435          });
436          axios
437            .post("/lingolab_online.class.php", {
438              action: "removeProgressRecord",
439              data: {
440                set: set,
441                student: student,
442              },
443            })
444            .then(function (response) {
445              Swal.close();
446              if (response.data.result == "success") {
447                vm.sharesWithMe = response.data.sharesWithMe;
448              } else {
449                Swal.fire({
450                  reverseButtons: true,
451                  title: "Error",
452                  text: response.data.message,
453                  type: "error",
454                });
455              }
456            })
457            .catch(function (error) {
458              console.error(error);
459            });
460        }
461      });
462    },
463    getSetsByStudent: function (student) {
464      return this.sharesWithMe.filter(function (e) {
465        return e.id == student.id;
466      })[0].progress;
467    },
468    showStudentRecord: function (record) {
469      var vm = this;
470      var progress = this.getSetsByStudent(record);
471      var summary = this.totalProgressSummary(progress);
472      var html =
473        "<div><div style='font-size:20px;'>Current Total: " +
474        record.current_score +
475        " Running Total: " +
476        record.total_score +
477        "<br/>Last Active: " +
478        this.UTCToLocal(summary.last_active).substring(0, 10) +
479        "</div>";
480      html +=
481        "<div class='studentRecordDiv'><table class='table table-sm table-striped student-record-table'>";
482      html +=
483        "<thead class='thead-dark'><th>Set</th><th>Letters</th><th>Audio</th><th>Mastered</th><th>Errors</th><th>Last Studied</th></thead>";
484      html += "<tbody>";
485      var progressSummary;
486      progress.forEach(function (p) {
487        progressSummary = vm.progressSummary(p.progress_data);
488        html +=
489          "<tr><td style='width:30%'>" +
490          p.set_name +
491          "</td><td style='width:10%'>" +
492          p.display.toUpperCase() +
493          "</td><td style='width:10%'>" +
494          p.audio.toUpperCase() +
495          "</td><td style='width:10%'>" +
496          progressSummary.mastered +
497          "</td><td style='width:10%'>" +
498          progressSummary.mistakes +
499          "</td><td style='width:20%'>" +
500          vm.UTCToLocal(p.modified).substring(0, 10) +
501          "</td></tr>";
502      });
503      html += "</tbody>";
504      html += "</table></div></div>";
505
506      Swal.fire({
507        onOpen: function () {
508          $(".student-record-table").tablesorter({
509            theme: "bootstrap",
510            sortList: [[0, 0]],
511          });
512          $("#myContainer").addClass("blurred");
513        },
514        reverseButtons: true,
515        title: record.name + "<br/>" + record.class_name,
516        html: html,
517        showCancelButton: false,
518      }).then(function (value) {
519        $("#myContainer").removeClass("blurred");
520      });
521    },
522    progressSummaryBySet: function (set, sub) {
523      var vm = this;
524      var thisSummary = null;
525      this.sharesWithMe.forEach(function (record) {
526        if (record.sub == sub) {
527          record.progress.forEach(function (p) {
528            if (
529              p.set_id == set.id &&
530              p.text == set.text &&
531              p.audio == set.audio &&
532              p.display == set.display
533            ) {
534              thisSummary = vm.progressSummary(p.progress_data);
535            }
536          });
537        }
538      });
539      if (thisSummary === null) {
540        thisSummary = {
541          mastered: "-",
542          attempted: "-",
543          mistakes: "-",
544          last_active: "-",
545          av_mistakes_per_term: "-",
546        };
547      }
548      return thisSummary;
549    },
550    getStudentsBySet: function (set) {
551      var vm = this;
552      var students = [];
553      var setProgress = null;
554      this.sharesWithMe.forEach(function (record) {
555        setProgress = record.progress.find(function (e) {
556          return (
557            e.text == set.text &&
558            e.audio == set.audio &&
559            e.display == set.display &&
560            e.set_id == set.id
561          );
562        });
563
564        students.push({
565          modified: setProgress ? setProgress.modified : 0,
566          class_name: record.class_name,
567          name: record.name,
568          sub: record.sub,
569          text: set.text,
570          audio: set.audio,
571          display: set.display,
572          resets: setProgress ? setProgress.resets : 0,
573        });
574      });
575      return students;
576    },
577    numResultsBySet: function (set_id) {
578      var vm = this;
579      var results = 0;
580      this.sharesWithMe.forEach(function (user) {
581        results += user.progress.filter(function (p) {
582          return p.set_id == set_id;
583        }).length;
584      });
585      return results;
586    },
587    resetTracker() {
588      this.selectedTrackerStudent = "";
589      this.selectedTrackerClass = "";
590      this.selectedTrackerSet = "";
591      this.sharesWithMe = [];
592      this.checkAll = false;
593      $("#checkAll").prop("checked", false);
594    },
595    loadTracker: function () {
596      var vm = this;
597      Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
598      axios
599        .get(
600          "/lingolab_online.class.php?action=loadTracker&name=" +
601            this.selectedTrackerStudent +
602            "&class_name=" +
603            this.selectedTrackerClass +
604            "&set_name=" +
605            this.selectedTrackerSet
606        )
607        .then(function (response) {
608          Swal.close();
609          vm.sharesWithMe = response.data;
610          vm.$forceUpdate();
611          Vue.nextTick(function () {
612            $(".progress-tracker-table").tablesorter({
613              theme: "bootstrap",
614              sortList: [[2, 1]],
615            });
616            $(".progress-tracker-students:not(.tablesorter)").tablesorter({
617              theme: "bootstrap",
618              sortList: [[3, 1]],
619            });
620          });
621        })
622        .catch(function (error) {
623          console.log(error);
624        });
625    },
626    getFilteredSharedSets: function () {
627      var vm = this;
628      var sharedSets = this.getSharedSets();
629      return sharedSets.filter(function (e) {
630        return (
631          ((e.display == "full" && vm.mode_shown) ||
632            (e.display == "hidden" && vm.mode_hidden) ||
633            (e.display == "stellar" && vm.mode_stellar)) &&
634          ((e.audio == "question" && vm.audio_definition) ||
635            (e.audio == "answer" && vm.audio_target) ||
636            (e.audio == "off" && vm.audio_off))
637        );
638      });
639    },
640    getSharedSets: function () {
641      var sharedSets = [];
642      this.sharesWithMe.forEach(function (record) {
643        record.progress.forEach(function (p) {
644          if (
645            !sharedSets
646              .map(function (e) {
647                return e.id + "_" + e.text + "_" + e.audio + "_" + e.display;
648              })
649              .includes(
650                p.set_id + "_" + p.text + "_" + p.audio + "_" + p.display
651              )
652          ) {
653            sharedSets.push({
654              name: p.set_name,
655              id: p.set_id,
656              text: p.text,
657              audio: p.audio,
658              display: p.display,
659            });
660          }
661        });
662      });
663      sharedSets.sort(function (a, b) {
664        if (a.name < b.name) {
665          return -1;
666        }
667        if (a.name > b.name) {
668          return 1;
669        }
670        return 0;
671      });
672      return sharedSets;
673    },
674    totalProgressSummary: function (progress) {
675      var vm = this;
676      var mastered = 0,
677        attempted = 0,
678        mistakes = 0,
679        totalTerms = 0;
680      var dates = [];
681      progress.forEach(function (data) {
682        dates.push(data.modified);
683        data.progress_data.forEach(function (item) {
684          totalTerms++;
685          mistakes += item.mistakes;
686          if (item.rank == 3) {
687            mastered++;
688          }
689          if (item.attempted) {
690            attempted++;
691          }
692        });
693      });
694      dates.sort(function (a, b) {
695        return new Date(b) - new Date(a);
696      });
697      var last_active = "0";
698      if (dates.length) {
699        last_active = dates[0];
700      }
701      var av_mistakes_per_term = (mistakes / attempted).toFixed(1);
702      return {
703        mastered: mastered,
704        attempted: attempted,
705        mistakes: mistakes,
706        last_active: last_active,
707        av_mistakes_per_term: isNaN(av_mistakes_per_term)
708          ? 0
709          : av_mistakes_per_term,
710      };
711    },
712    progressSummary: function (data) {
713      var mastered = 0,
714        attempted = 0,
715        mistakes = 0;
716      data.forEach(function (item) {
717        mistakes += item.mistakes;
718        if (item.rank == 3) {
719          mastered++;
720        }
721        if (item.attempted) {
722          attempted++;
723        }
724      });
725      var av_mistakes_per_term = (mistakes / attempted).toFixed(1);
726      return {
727        mastered: mastered,
728        attempted: attempted,
729        mistakes: mistakes,
730        av_mistakes_per_term: isNaN(av_mistakes_per_term)
731          ? 0
732          : av_mistakes_per_term,
733      };
734    },
735    changeImage: function (obj) {
736      Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
737      axios
738        .post("https://study.lingolab.online/lingolabco.class.php", {
739          action: "imageSearch",
740          data: {
741            term: obj.term,
742          },
743        })
744        .then(function (response) {
745          var code = "<div class='scroll-list'>";
746          response.data.matches.hits.forEach(function (hit) {
747            code +=
748              "<img data-term='" +
749              obj.id +
750              "' class='imgSearchResult' src='" +
751              hit.previewURL +
752              "'>";
753          });
754          code += "</div>";
755          Swal.fire({
756            reverseButtons: true,
757            title: "Choose image:",
758            html: code,
759            showCancelButton: true,
760            showConfirmButton: false,
761          });
762        })
763        .catch(function (error) {
764          console.error(error);
765        });
766    },
767    uniqueID: function () {
768      return Math.random().toString(36).substr(2, 9);
769    },
770    deleteSet: function (id) {
771      var self = this;
772      self.deletingSet = true;
773      Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
774      axios
775        .post("/lingolab_online.class.php", {
776          action: "deleteSet",
777          data: {
778            id: id,
779          },
780        })
781        .then(function (response) {
782          self.deletingSet = false;
783          if (response.data.result == "success") {
784            Swal.fire("Success", "Set deleted!", "success");
785            self.userSets = response.data.userSets;
786            self.screen = "manage_sets";
787          } else {
788            Swal.fire("Error", response.data.message, "error");
789          }
790        })
791        .catch(function (error) {
792          console.error(error);
793        });
794    },
795    reset: function () {
796      this.editId = "";
797      this.savingSet = false;
798      this.deletingSet = false;
799      this.toImport = "";
800      this.terms = [];
801      this.setName = "";
802      this.setLangs = {
803        definition: "en",
804        term: "en",
805      };
806      this.screen = "manage_sets";
807    },
808    saveSet: function () {
809      var self = this;
810
811      if (this.editId == "") {
812        this.importSet();
813      }
814
815      if (!this.terms.length) {
816        Swal.fire(
817          "Error",
818          "Please input at least one term / definition pair",
819          "error"
820        );
821      } else {
822        var maxWords = 16;
823        var maxWordsExceeded = false;
824        var words;
825        this.terms.forEach(function (obj) {
826          words = obj.term.split(" ");
827          if (words.length > maxWords) {
828            maxWordsExceeded = true;
829          }
830        });
831
832        if (maxWordsExceeded) {
833          Swal.fire(
834            "Warning",
835            "Set contains terms that exceed the maximum of 16 words.",
836            "error"
837          );
838        } else {
839          this.savingSet = true;
840          Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
841          axios
842            .post("/lingolab_online.class.php", {
843              action: "saveSet",
844              data: {
845                editId: this.editId,
846                name: this.setName,
847                set: {
848                  terms: this.terms,
849                  languages: this.setLangs,
850                },
851              },
852            })
853            .then(function (response) {
854              self.savingSet = false;
855              if (response.data.result == "success") {
856                Swal.fire(
857                  "Success",
858                  "Set " + (self.editId == "" ? "created" : "updated"),
859                  "success"
860                );
861                self.userSets = response.data.userSets;
862                self.screen = "manage_sets";
863                self.setName = "";
864                self.setLangs = {
865                  definition: "en",
866                  term: "en",
867                };
868                self.terms = [];
869                //self.editSet(response.data.shortid);
870              } else {
871                Swal.fire("Error", "Error saving set!", "error");
872              }
873            })
874            .catch(function (error) {
875              console.error(error);
876            });
877        }
878      }
879    },
880    importSet: function () {
881      var self = this;
882      var gridData = self.termsTable.getData();
883      gridData.forEach(function (elements) {
884        if (
885          elements.slice(0, 2).every(function (e) {
886            return e && e.trim() !== "";
887          })
888        ) {
889          self.terms.push({
890            id: self.uniqueID(),
891            term: elements[0].trim(),
892            definition: elements[1].trim(),
893            image: elements[2] ? elements[2].trim() : null,
894          });
895        }
896      });
897    },
898    editStudent: function (record) {
899      var vm = this;
900      var html = `
901      
902      <div class="form-group">
903        <label>Name:</label>
904        <input type="text" class="form-control" id="newName">
905      </div>
906
907      <div class="form-group">
908        <label>Class:</label>
909        <input type="text" class="form-control" id="newClass">
910      </div>
911
912      `;
913      Swal.fire({
914        onOpen: function () {
915          $("#newName").val(record.name);
916          $("#newClass").val(record.class_name);
917        },
918        title: "Edit Student",
919        showCancelButton: true,
920        html: html,
921        reverseButtons: true,
922      }).then((result) => {
923        if (result.value) {
924          axios
925            .post("/lingolab_online.class.php", {
926              action: "editStudent",
927              data: {
928                name: $("#newName").val(),
929                class_name: $("#newClass").val(),
930                sub: record.sub,
931              },
932            })
933            .then(function (response) {
934              if (response.data.result == "success") {
935                vm.loadTracker();
936              } else {
937                Swal.fire({
938                  title: "Error",
939                  text: response.data.message,
940                  type: "error",
941                });
942              }
943            })
944            .catch(function (error) {
945              console.error(error);
946            });
947        }
948      });
949    },
950    editSet: function (setId) {
951      var thisSet = this.userSets.filter(function (e) {
952        return e.shortid == setId;
953      })[0];
954      this.editId = setId;
955      this.terms = thisSet.terms;
956      this.setName = thisSet.name;
957      this.setLangs = thisSet.languages;
958      this.screen = "create_set";
959    },
960    get_set: function (id) {
961      var vm = this;
962      axios
963        .get(
964          "https://study.lingolab.online/lingolabco.class.php?action=getSet&set=" +
965            id
966        )
967        .then(function (response) {
968          if (response.data.set) {
969            vm.selectSet(response.data.set);
970          } else {
971            Swal.fire({
972              title: "Error",
973              text: "Invalid set!",
974              type: "error",
975            });
976          }
977        })
978        .catch(function (error) {
979          console.log(error);
980        });
981    },
982    mobile_user: function () {
983      if (
984        /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
985          navigator.userAgent
986        )
987      ) {
988        return true;
989      } else {
990        return false;
991      }
992    },
993    getQRCode: function (assignment_id) {
994      var url = "https://www.lingolab.online/submit.php?id=" + assignment_id;
995      axios
996        .post("/lingolab_online.class.php", {
997          action: "generateQRCode",
998          data: {
999            assignment_id: assignment_id,
1000            url: url,
1001          },
1002        })
1003        .then(function (response) {
1004          if (response.data.result == "success") {
1005            Swal.fire({
1006              reverseButtons: true,
1007              html:
1008                "<h5 style='margin-top:40px;'>" +
1009                url +
1010                " <i style='cursor:pointer;' data-clipboard-text='https://lingolab.online/submit.php?id=" +
1011                assignment_id +
1012                "' class='link-btn fas fa-paste'></i></h5><img src='data:image/png;base64," +
1013                response.data.b64 +
1014                "'><h3>Students Joined: " +
1015                response.data.num_submissions +
1016                "</h3><h2><a href='javascript:void(0)' onClick='app.getQRCode(" +
1017                assignment_id +
1018                ")'>Reload</a></h2>",
1019              showCloseButton: true,
1020              showCancelButton: false,
1021              showConfirmButton: false,
1022            });
1023          }
1024        })
1025        .catch(function (error) {
1026          console.error(error);
1027        });
1028    },
1029    selectAll: function () {
1030      this.assignment.set.terms.forEach(function (item) {
1031        item.selected = true;
1032      });
1033      this.$forceUpdate();
1034    },
1035    selectNone: function () {
1036      this.assignment.set.terms.forEach(function (item) {
1037        item.selected = false;
1038      });
1039      this.$forceUpdate();
1040    },
1041    numSelected: function () {
1042      if (this.assignment.set.terms) {
1043        return this.assignment.set.terms.filter(function (e) {
1044          return e.selected;
1045        }).length;
1046      } else {
1047        return 0;
1048      }
1049    },
1050    hasImages: function (set) {
1051      var result = false;
1052      if (set.terms) {
1053        set.terms.forEach(function (object) {
1054          if (object.image) {
1055            result = true;
1056          }
1057        });
1058      }
1059      return result;
1060    },
1061    selectItem: function (item) {
1062      item.selected = !item.selected;
1063      this.$forceUpdate();
1064    },
1065    deleteAssignment: function (assignment_id) {
1066      var vm = this;
1067      Swal.fire({
1068        reverseButtons: true,
1069        title: "Warning",
1070        text: "Are you sure you wish to delete this assignment and ALL related submissions??",
1071        showCancelButton: true,
1072      }).then(function (value) {
1073        if (value.value) {
1074          Swal.fire({
1075            onOpen: () => {
1076              Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
1077            },
1078          });
1079          axios
1080            .post("/lingolab_online.class.php", {
1081              action: "deleteAssignment",
1082              data: {
1083                assignment_id: assignment_id,
1084              },
1085            })
1086            .then(function (response) {
1087              if (response.data.result == "success") {
1088                vm.assignments = response.data.assignments;
1089              }
1090            })
1091            .catch(function (error) {
1092              console.error(error);
1093            });
1094        }
1095      });
1096    },
1097    UTCToLocal: function (date) {
1098      var local = moment(moment.utc(date).toDate())
1099        .local()
1100        .format("YYYY-MM-DD HH:mm:ss");
1101      return local;
1102    },
1103    timeElapsedHuman: function (then) {
1104      var now = moment().utc().format();
1105      return moment.duration(moment.utc(then).diff(moment(now))).humanize();
1106    },
1107    createAssignment: function () {
1108      var vm = this;
1109      axios
1110        .post("/lingolab_online.class.php", {
1111          action: "createAssignment",
1112          data: {
1113            assignmentEditId: this.assignmentEditId,
1114            assignment: this.assignment,
1115          },
1116        })
1117        .then(function (response) {
1118          if (response.data.result == "success") {
1119            Swal.fire({
1120              reverseButtons: true,
1121              title: "Success",
1122              html:
1123                "Assignment created at:<br/><a target='_blank' href='https://lingolab.online/submit.php?id=" +
1124                response.data.id +
1125                "'>https://lingolab.online/submit.php?id=" +
1126                response.data.id +
1127                "</a>",
1128              type: "success",
1129            });
1130            vm.assignments = response.data.assignments;
1131            vm.screen = "manage";
1132          } else {
1133            Swal.fire({
1134              reverseButtons: true,
1135              title: "Error",
1136              text: response.data.message,
1137              type: "error",
1138            });
1139          }
1140        })
1141        .catch(function (error) {
1142          console.error(error);
1143        });
1144    },
1145    ucfirst: function (str) {
1146      str += "";
1147      var f = str.charAt(0).toUpperCase();
1148      return f + str.substr(1);
1149    },
1150    selectSet: function (set) {
1151      this.assignment.set = set;
1152      this.assignment.set.terms.forEach(function (item) {
1153        item.mistakes = 0;
1154        item.selected = true;
1155      });
1156    },
1157    signIn: function (googleUser) {
1158      var vm = this;
1159
1160      axios
1161        .post("/lingolab_online.class.php", {
1162          action: "saveSignIn",
1163          data: {
1164            userToken: googleUser.credential,
1165          },
1166        })
1167        .then(function (response) {
1168          if (vm.user.role == "guest") {
1169            location.reload();
1170          }
1171        })
1172        .catch(function (error) {
1173          console.error(error);
1174        });
1175    },
1176
1177    signOut: function () {
1178      axios
1179        .post("/lingolab_online.class.php", {
1180          action: "signOut",
1181          data: {},
1182        })
1183        .then(function (response) {
1184          location.reload();
1185        })
1186        .catch(function (error) {
1187          console.error(error);
1188        });
1189    },
1190  },
1191  created: function () {
1192    if (localStorage.getItem("infoWasShown")) {
1193      this.infoWasShown = true;
1194    }
1195  },
1196  mounted: function () {
1197    var vm = this;
1198
1199    var clipboard = new ClipboardJS(".link-btn");
1200    clipboard.on("success", function (e) {
1201      alertify.success("Copied!");
1202    });
1203
1204    $("body").on("click", ".imgSearchResult", function () {
1205      var imgUrl = $(this).attr("src");
1206      var termId = $(this).data("term");
1207
1208      Swal.fire({ title: 'Loading...', onBeforeOpen: () => Swal.showLoading(), allowOutsideClick: false });
1209
1210      axios
1211        .post("https://study.lingolab.online/lingolabco.class.php", {
1212          action: "saveImage",
1213          data: {
1214            termId: termId,
1215            imgUrl: imgUrl,
1216          },
1217        })
1218        .then(function (response) {
1219          if (response.data.result == "success") {
1220            var term = app.terms.filter(function (term) {
1221              return term.id == termId;
1222            })[0];
1223            term.image =
1224              "https://study.lingolab.online/set_images/" + termId + ".jpg";
1225            Swal.close();
1226          } else {
1227            Swal.fire("Error", response.data.message, "error");
1228          }
1229        })
1230        .catch(function (error) {
1231          console.error(error);
1232        });
1233    });
1234
1235    axios
1236      .get(
1237        "https://study.lingolab.online/lingolabco.class.php?action=getDefaultSets&list=lingolab"
1238      )
1239      .then(function (response) {
1240        response.data.sort(function (a, b) {
1241          if (a.name < b.name) {
1242            return -1;
1243          }
1244          if (a.name > b.name) {
1245            return 1;
1246          }
1247          return 0;
1248        });
1249
1250        vm.defaultSets = response.data;
1251      })
1252      .catch(function (error) {
1253        console.log(error);
1254      });
1255
1256    axios
1257      .get("/lingolab_online.class.php?action=getUserSets")
1258      .then(function (response) {
1259        if (response.data.result == "success") {
1260          vm.userSets = response.data.sets;
1261          Vue.nextTick(function () {
1262            vm.sort_tables();
1263          });
1264        }
1265      })
1266      .catch(function (error) {
1267        console.log(error);
1268      });
1269
1270    $(window).resize(function () {
1271      if (vm.screen == "create_set") {
1272        Vue.nextTick(function () {
1273          vm.termsTable.render();
1274        });
1275      }
1276    });
1277
1278    if (this.get_url_param("screen")) {
1279      this.screen = this.get_url_param("screen");
1280    } else {
1281      this.screen = "manage";
1282    }
1283  },
1284});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.