PageSourceSearch

https://ta-tum.com/js/modules/teacher/classroom/TeacherClassroomGradebook.js

js ta-tum.com collected 2026-09-24 11:29:11 UTC 42,933 bytes, 1,203 lines download raw bytes

1/**
2 * Module Teacher Classroom Gradebook
3 * @description Módulo que representa el libro de calificaciones
4 * @author Alejandro Micheloud
5 */
6
7var TeacherClassroomGradebook = function (sco) {
8  var scope = sco;
9  var builded = false;
10  var gradebook = null;
11  var amount_projects = null;
12  var amount_students = null;
13  var classroom_total = null; // total of student points
14  var classroom_project_total = null;
15  var current_order_by = "orderNameValue"; // name || score
16  var current_order_by_mode = "asc"; // asc || desc
17  var isLoading = false;
18
19  /**
20   * Se invoca cuando el módulo recibe foco
21   * @returns {undefined}
22   */
23  function show() {
24    console.log("TeacherClassroomStudents:show();");
25    if (!builded) {
26      build(doStart);
27    } else {
28      doStart();
29    }
30    scope.find(".mStudents.d-students").show();
31  }
32  function doStart() {
33    scope.show();
34
35    if (isEnableGlobalEduca) {
36      scope.find(".btn-export").addClass("f-white-color");
37    }
38    
39    mDiscover.showLoading();
40    isLoading = true;
41    var intervalCheckLoading = setInterval(function () {
42      if(isLoading){
43        mDiscover.showLoading();
44      }else{
45        mDiscover.hideLoading();
46        clearInterval(intervalCheckLoading);
47      }
48    }, 1000);
49    mCom.ajax(
50      "classroom/gradebook/" + mTeacherHome.getClassroomGuid(),
51      "GET",
52      "",
53      function (data, status) {
54        console.log("doStart() result");
55        mDiscover.hideLoading();
56        isLoading = false;
57        gradebook = data;
58        gradebook.students.map((student) => {
59          student.orderNameValue = String(
60            (String(student.lastname).split(" ").join("") !== ""
61              ? student.lastname
62              : student.temp_student_lastname) +
63              " " +
64              (String(student.name).split(" ").join("") !== ""
65                ? student.name
66                : student.temp_student_name)
67          ).toLowerCase();
68          student.orderNameValue = student.orderNameValue
69            .split("á")
70            .join("a")
71            .split("á")
72            .join("a")
73            .split("é")
74            .join("e")
75            .split("í")
76            .join("i")
77            .split("ó")
78            .join("o")
79            .split("ú")
80            .join("u")
81            .split("ñ")
82            .join("n");
83        });
84        gradebook.students = sortByKey(
85          gradebook.students,
86          current_order_by,
87          current_order_by_mode
88        );
89        render();
90      },
91      function (err, status, msg) {
92        mDiscover.hideLoading();
93        mToast.toast("Problemas al recuperar resultados", 3000);
94      }
95    );
96  }
97  function calcTotal() {
98    classroom_total = 0;
99    amount_projects = gradebook.projects.length;
100    classroom_project_total = 0; // total de puntos que puede obtener un alumno
101    var projectTotalPoints = {};
102    for (var i = 0; i < amount_projects; i++) {
103      var project_score = 0;
104      var amount_tasks = 0;
105      if (gradebook.projects[i].hasOwnProperty("item_guid")) {
106        // gradebook classroom item
107        if (!isNaN(gradebook.projects[i].score)) {
108          project_score += gradebook.projects[i].score;
109        }
110      } else {
111        for (var j = 0; j < gradebook.projects[i].tasks.length; j++) {
112          if (gradebook.projects[i].tasks[j].hasOwnProperty("item_guid")) {
113            // gradebook project item
114            if (!isNaN(gradebook.projects[i].tasks[j].score)) {
115              project_score += gradebook.projects[i].tasks[j].score;
116            }
117          } else {
118            if (!isNaN(gradebook.projects[i].tasks[j].task_score)) {
119              project_score += gradebook.projects[i].tasks[j].task_score;
120            }
121            amount_tasks++;
122          }
123        }
124      }
125      gradebook.projects[i].project_score = project_score;
126      projectTotalPoints[i] = project_score;
127      classroom_project_total += project_score;
128      gradebook.projects[i].amount_tasks = amount_tasks;
129      // console.log("gradebook.projects[i].project_score: " + gradebook.projects[i].project_score);
130    }
131    for (var i = 0; i < gradebook.students.length; i++) {
132      var student_total = 0;
133      var student_without_access_points = 0;
134      for (var j = 0; j < amount_projects; j++) {
135        var project_total = 0;
136        var student_without_access_points_project = 0;
137        if (gradebook.students[i].projects[j].hasOwnProperty("item_guid")) {
138          // gradebook classroom item
139          if (!isNaN(gradebook.students[i].projects[j].score)) {
140            project_total += gradebook.students[i].projects[j].score;
141          }
142        } else {
143          for (
144            var k = 0;
145            k < gradebook.students[i].projects[j].tasks.length;
146            k++
147          ) {
148            if (
149              gradebook.students[i].projects[j].tasks[k].hasOwnProperty(
150                "item_guid"
151              )
152            ) {
153              // gradebook project item
154              if (!isNaN(gradebook.students[i].projects[j].tasks[k].score)) {
155                project_total +=
156                  gradebook.students[i].projects[j].tasks[k].score;
157              }
158            } else {
159              if (!gradebook.students[i].projects[j].tasks[k].with_access) {
160                if (
161                  !isNaN(gradebook.students[i].projects[j].tasks[k].task_score)
162                ) {
163                  student_without_access_points +=
164                    gradebook.students[i].projects[j].tasks[k].task_score;
165                }
166              }
167              if (
168                gradebook.students[i].projects[j].tasks[k].status ===
169                "corrected"
170              ) {
171                if (gradebook.students[i].projects[j].tasks[k].with_access) {
172                  if (
173                    !isNaN(
174                      gradebook.students[i].projects[j].tasks[k].student_score
175                    )
176                  ) {
177                    project_total +=
178                      gradebook.students[i].projects[j].tasks[k].student_score;
179                  }
180                }
181              } else {
182                // descontar tareas/test en estado distinto a corregido
183                if (gradebook.students[i].projects[j].tasks[k].with_access) {
184                  if (
185                    !isNaN(
186                      gradebook.students[i].projects[j].tasks[k].student_score
187                    )
188                  ) {
189                    student_without_access_points +=
190                      gradebook.students[i].projects[j].tasks[k].task_score;
191                    student_without_access_points_project +=
192                      gradebook.students[i].projects[j].tasks[k].task_score;
193                  }
194                }
195              }
196            }
197          }
198        }
199        gradebook.students[i].projects[j].project_total = project_total;
200        gradebook.students[i].projects[j].project_score = 0;
201        if (projectTotalPoints.hasOwnProperty(j)) {
202          gradebook.students[i].projects[j].project_score =
203            (project_total * 10) /
204            (projectTotalPoints[j] - student_without_access_points_project);
205          gradebook.students[i].projects[j].project_score =
206            Math.round(gradebook.students[i].projects[j].project_score * 100) /
207            100;
208        }
209        student_total += project_total;
210      }
211      gradebook.students[i].student_total = student_total;
212      gradebook.students[i].score =
213        (student_total * 10) /
214        (classroom_project_total - student_without_access_points);
215      gradebook.students[i].score =
216        Math.round(gradebook.students[i].score * 100) / 100;
217
218      classroom_total += student_total;
219    }
220  }
221
222  function render() {
223    calcTotal();
224
225    // classroom total
226    //scope.find(".d-left-fixed .d-header.h2 span").first().html(classroom_total);
227    scope
228      .find(".d-left-fixed .d-header.h2 span.d-total")
229      .html(classroom_project_total);
230
231    // students
232    amount_students = gradebook.students.length;
233    var html = "";
234    if (amount_students <= 0) {
235      html = getHtmlEmptyStudents();
236    } else {
237      for (var i = 0; i < amount_students; i++) {
238        html += getHtmlStudent(gradebook.students[i]);
239      }
240    }
241    scope.find(".d-col-students").html(html);
242
243    scope.find(".d-col-students .d-empty-gradebook span a").click(function () {
244      routie(
245        "teacher/classroom/" + mTeacherHome.getClassroomGuid() + "/students"
246      );
247    });
248
249    // project and responses
250    html = getHtmlHeaderTasks() + '<div class="d-col-tasks">';
251    for (var i = 0; i < amount_students; i++) {
252      html += getHtmlStudentTasks(gradebook.students[i]);
253    }
254    html += "</div>";
255    scope.find(".d-right").html(html);
256
257    for (var i = 0; i < amount_students; i++) {
258      var $student = scope.find(".d-right .d-col-project-student").eq(i);
259      $student.data(gradebook.students[i]);
260      for (var j = 0; j < gradebook.students[i].projects.length; j++) {
261        $student
262          .find(
263            ".d-col-project-student-tasks[guid='" +
264              gradebook.students[i].projects[j].guid +
265              "']"
266          )
267          .data(gradebook.students[i].projects[j]);
268        if (gradebook.students[i].projects[j].hasOwnProperty("item_guid")) {
269          // gradebook classroom item
270        } else {
271          for (
272            var k = 0;
273            k < gradebook.students[i].projects[j].tasks.length;
274            k++
275          ) {
276            $student
277              .find(
278                ".d-col-project-student-tasks[guid='" +
279                  gradebook.students[i].projects[j].guid +
280                  "'] .d-project-task"
281              )
282              .eq(k)
283              .data(gradebook.students[i].projects[j].tasks[k]);
284          }
285        }
286      }
287    }
288
289    scope
290      .find(".d-right  .d-col-project-student-tasks .d-project-task")
291      .click(function () {
292        if ($(this).hasClass("new-item") || $(this).hasAttr("item_guid")) {
293          return;
294        }
295        var task = $(this).data();
296        var project = $(this).closest(".d-col-project-student-tasks").data();
297        var student = $(this).closest(".d-col-project-student").data();
298
299        if (task.with_access) {
300          routie(
301            "teacher/classroom/" +
302              mTeacherHome.getClassroomGuid() +
303              "/" +
304              project.project_guid +
305              "/" +
306              project.guid +
307              "/post/" +
308              task.post_guid +
309              "/" +
310              student.guid
311          );
312        } else {
313          mToast.toast(lang.gradebook_project_student_without_permission, 3000);
314        }
315      });
316
317    // header projects data + hover
318    for (var i = 0; i < gradebook.projects.length; i++) {
319      var project = gradebook.projects[i];
320      var $cell = scope.find(
321        ".d-right .d-right-header-project[guid='" + project.guid + "']"
322      );
323      $cell.data(project);
324      $cell.find(".d-project-status").addClass(project.status);
325      mTooltip.setTooltip(
326        $cell,
327        '<b>"' +
328          project.title +
329          '"</b>' +
330          "<br>" +
331          lang.gradebook_project_status +
332          ": " +
333          getProjectStateLabel(project.status)
334      );
335      if (project.hasOwnProperty("tasks")) {
336        for (var j = 0; j < project.tasks.length; j++) {
337          if (!project.tasks[j].hasOwnProperty("item_guid")) {
338            var $cellTask = scope.find(
339              ".d-right .d-right-header-task[guid='" +
340                project.tasks[j].task_guid +
341                "']"
342            );
343            var title = stripTags(project.tasks[j].title);
344            if (title === "" || title === null)
345              title = lang.gradebook_project_no_title;
346            mTooltip.setTooltip(
347              $cellTask,
348              '<b>"' +
349                title +
350                '"</b>' +
351                "<br>" +
352                lang.gradebook_project_created_at +
353                " " +
354                String(
355                  getElapsedTimeMDM(project.tasks[j].created_at_mdm)
356                ).toLowerCase()
357            );
358            var route_classroom_project_post =
359              "teacher/classroom/" +
360              mTeacherHome.getClassroomGuid() +
361              "/" +
362              project.project_guid +
363              "/" +
364              project.guid +
365              "/post/" +
366              project.tasks[j].guid;
367            $cellTask.attr("route", route_classroom_project_post);
368            $cellTask.click(function () {
369              routie($(this).attr("route"));
370            });
371          }
372        }
373      }
374    }
375    // expand/collapse project
376    var $cell = scope
377      .find(".d-right .d-right-header-project")
378      .click(function () {
379        var project = $(this).data();
380        if (
381          scope
382            .find(".d-right-header-project-tasks[guid='" + project.guid + "']")
383            .is(":visible")
384        ) {
385          scope
386            .find(".d-right-header-project-tasks[guid='" + project.guid + "']")
387            .fadeOut();
388          scope
389            .find(".d-col-project-student-tasks[guid='" + project.guid + "']")
390            .fadeOut();
391
392          scope
393            .find(".d-project[guid='" + project.guid + "']")
394            .removeClass("d-task-open")
395            .addClass("d-task-close");
396
397          scope
398            .find(".d-right-header-project[guid='" + project.guid + "']")
399            .addClass("d-task-close")
400            .removeClass("d-task-open");
401
402          var totalWidth = scope
403            .find(".d-right-header-project[guid='" + project.guid + "']")
404            .width();
405
406          scope
407            .find(
408              ".d-right-header-project[guid='" + project.guid + "'] .d-title"
409            )
410            .width(totalWidth);
411
412          scope
413            .find(
414              ".d-right-header-project[guid='" +
415                project.guid +
416                "'] .d-title .d-title-text"
417            )
418            .width(147);
419        } else {
420          onProjectOpen(project);
421        }
422      });
423    //setTimeout(function () {
424    var $cell = scope
425      .find(".d-right .d-right-header-project")
426      .each(function () {
427        var project = $(this).data();
428        onProjectOpen(project);
429      });
430    //}, 100);
431
432    function onProjectOpen(project) {
433      scope
434        .find(".d-right-header-project-tasks[guid='" + project.guid + "']")
435        .fadeIn();
436      scope
437        .find(".d-col-project-student-tasks[guid='" + project.guid + "']")
438        .fadeIn();
439      scope
440        .find(".d-project[guid='" + project.guid + "']")
441        .removeClass("d-task-close")
442        .addClass("d-task-open");
443
444      scope
445        .find(".d-right-header-project[guid='" + project.guid + "']")
446        .addClass("d-task-open")
447        .removeClass("d-task-close");
448
449      var totalWidth =
450        scope
451          .find(".d-col-project-student-tasks[guid='" + project.guid + "']")
452          .width() +
453        scope
454          .find(".d-right-header-project[guid='" + project.guid + "']")
455          .width();
456
457      scope
458        .find(".d-right-header-project[guid='" + project.guid + "'] .d-title")
459        .width(totalWidth);
460
461      var titleText = scope.find(
462        ".d-right-header-project[guid='" +
463          project.guid +
464          "'] .d-title .d-title-text"
465      );
466      if (titleText && titleText.position())
467        titleText.width(totalWidth - titleText.position().left);
468    }
469    eventsAddGradebookItem();
470    eventsGradebookItems();
471  }
472
473  function eventsAddGradebookItem() {
474    // add gradebook item
475    scope
476      .find(".d-right .new-item .d-btn-add")
477      .off("click")
478      .on("click", function () {
479        var classroom_guid = mTeacherHome.getClassroomGuid();
480        var classroom_project_guid = null;
481        if ($(this).parent().hasAttr("project_guid")) {
482          classroom_project_guid = $(this).parent().attr("project_guid");
483        }
484        console.log(
485          "addGradebookItem() classroom_guid: " +
486            classroom_guid +
487            " classroom_project_guid: " +
488            classroom_project_guid
489        );
490        var data = {
491          classroom_guid: classroom_guid,
492          classroom_project_guid: classroom_project_guid,
493        };
494        var $tile = $(this).parent();
495        mDiscover.showLoading();
496        mCom.ajax(
497          "classroom/gradeb
497ook/item",
498          "POST",
499          data,
500          function (item, status) {
501            console.log("addGradebookItem() result");
502            mDiscover.hideLoading();
503            item.item_guid = item.guid;
504            afterAddGradebookItem(item, $tile);
505            mToast.toast(lang.gradebook_item_add_ok, 3000);
506          },
507          function (err, status, msg) {
508            mDiscover.hideLoading();
509            mToast.toast(lang.gradebook_item_add_fail, 3000);
510          }
511        );
512      });
513  }
514
515  function afterAddGradebookItem(item, $tile) {
516    // successfull item gradebook added
517    if (item.classroom_project_guid === null) {
518      // gradebook classroom item
519      gradebook.projects.push(item);
520      amount_projects = gradebook.projects.length;
521      for (var i = 0; i < amount_students; i++) {
522        item.score = null;
523        gradebook.students[i].projects.push(JSON.parse(JSON.stringify(item)));
524      }
525    } else {
526      // gradebook project item
527      for (var i = 0; i < amount_projects; i++) {
528        if (gradebook.projects[i].guid === item.classroom_project_guid) {
529          gradebook.projects[i].tasks.push(JSON.parse(JSON.stringify(item)));
530          for (var j = 0; j < amount_students; j++) {
531            item.score = null;
532            gradebook.students[j].projects[i].tasks.push(
533              JSON.parse(JSON.stringify(item))
534            );
535          }
536        }
537      }
538    }
539
540    // header new gradebook item
541    var $newtile = $tile.clone().appendTo($tile.parent());
542    $(this).hide();
543    $tile.addClass("active");
544    $tile.data({}).attr("item_guid", item.guid);
545
546    if ($tile.hasClass("item-project"))
547      $tile.find(".d-item-form").addClass("project");
548
549    // students new gradebook item
550    var tile_students = null;
551    if (item.classroom_project_guid !== null) {
552      tile_students = scope.find(
553        ".d-col-project-student-tasks .new-item[project_guid='" +
554          item.classroom_project_guid +
555          "']"
556      );
557    } else {
558      tile_students = scope.find(".d-project.new-item");
559    }
560    tile_students.each(function () {
561      var $tile = $(this);
562      var $newtile = $tile.clone().appendTo($tile.parent());
563      $tile
564        .addClass("active")
565        .removeClass("new-item")
566        .attr("item_guid", item.guid);
567    });
568
569    eventsAddGradebookItem();
570    eventsGradebookItems();
571  }
572
573  function eventsGradebookItems() {
574    // remove gradebook item
575    scope
576      .find(".d-right .d-right-header .d-item-remove")
577      .off("click")
578      .on("click", function () {
579        var tile = $(this).closest(".active");
580        var item_guid = tile.attr("item_guid");
581
582        var data = { guid: item_guid };
583        mDiscover.showLoading();
584        mCom.ajax(
585          "classroom/gradebook/item",
586          "DELETE",
587          data,
588          function (item, status) {
589            console.log("deleteGradebookItem() result");
590            mDiscover.hideLoading();
591            scope
592              .find(
593                ".d-col-project-student-tasks .active[item_guid='" +
594                  item_guid +
595                  "']"
596              )
597              .remove();
598            scope
599              .find(".d-project.active[item_guid='" + item_guid + "']")
600              .remove();
601            tile.remove();
602            mToast.toast(lang.gradebook_item_remove_ok, 3000);
603            removeGradebookItem(item_guid);
604          },
605          function (err, status, msg) {
606            mDiscover.hideLoading();
607            mToast.toast(lang.gradebook_item_remove_fail, 3000);
608          }
609        );
610      });
611
612    // update gradebook item
613    scope
614      .find(".d-right .d-right-header [contenteditable='true']")
615      .off("save_changes")
616      .on("save_changes", function () {
617        var tile = $(this).closest(".active");
618        var item_guid = tile.attr("item_guid");
619        var score = parseInt(tile.find(".d-item-points").text());
620        if (isNaN(score)) {
621          score = null;
622          //mToast.toast("La puntuación máxima debe ser un número entero", 3000);
623          //return;
624        }
625        var data = {
626          guid: item_guid,
627          item_guid: item_guid,
628          name: tile.find(".d-item-title").text(),
629          score: score,
630        };
631        //mDiscover.showLoading();
632        mCom.ajax(
633          "classroom/gradebook/item",
634          "put",
635          data,
636          function (item, status) {
637            mToast.toast(lang.gradebook_item_save_ok, 2000);
638            updateItemAndRender(data);
639          },
640          function (err, status, msg) {
641            //mDiscover.hideLoading();
642            mToast.toast(lang.gradebook_item_save_fail, 2000);
643          }
644        );
645      });
646    scope
647      .find(".d-right .d-right-header [contenteditable='true']")
648      .off("blur")
649      .on("blur", function () {
650        $(this).trigger("save_changes");
651      });
652    scope
653      .find(".d-right .d-right-header [contenteditable='true']")
654      .off("keypress")
655      .on("keypress", function (e) {
656        if (e.which === 13) {
657          $(this).trigger("save_changes");
658        }
659        return e.which !== 13;
660      });
661
662    // update gradebook item student
663    scope
664      .find(".d-right .d-col-project-student [contenteditable='true']")
665      .off("save_changes")
666      .on("save_changes", function () {
667        var tile = $(this).closest(".active");
668        var item_guid = tile.attr("item_guid");
669        var student_guid = $(this)
670          .closest(".d-col-project-student")
671          .attr("student_guid");
672        var score = parseInt($(this).text());
673        if (isNaN(score)) {
674          score = null;
675          //mToast.toast("La puntuación máxima debe ser un número entero", 3000);
676          //return;
677        }
678        var data = {
679          item_guid: item_guid,
680          student_guid: student_guid,
681          score: score,
682        };
683        //mDiscover.showLoading();
684        mCom.ajax(
685          "classroom/gradebook/item/student",
686          "put",
687          data,
688          function (item, status) {
689            mToast.toast(lang.gradebook_item_student_save_ok, 2000);
690            updateItemStudentAndRender(
691              data.item_guid,
692              data.student_guid,
693              data.score
694            );
695          },
696          function (err, status, msg) {
697            //mDiscover.hideLoading();
698            mToast.toast(lang.gradebook_item_student_save_fail, 2000);
699          }
700        );
701      });
702    scope
703      .find(".d-right .d-col-project-student [contenteditable='true']")
704      .off("blur")
705      .on("blur", function () {
706        $(this).trigger("save_changes");
707      });
708    scope
709      .find(".d-right .d-col-project-student [contenteditable='true']")
710      .off("keypress")
711      .on("keypress", function (e) {
712        if (e.which === 13) {
713          $(this).trigger("save_changes");
714        }
715        return e.which !== 13;
716      });
717  }
718
719  function removeGradebookItem(item_guid) {
720    for (var i = 0; i < amount_projects; i++) {
721      if (gradebook.projects[i].hasOwnProperty("item_guid")) {
722        // gradebook classroom item
723        if (gradebook.projects[i].item_guid === item_guid) {
724          gradebook.projects.splice(i, 1);
725          for (var j = 0; j < amount_students; j++) {
726            gradebook.students[j].projects.splice(i, 1);
727          }
728          return render();
729        }
730      } else {
731        for (var j = 0; j < gradebook.projects[i].tasks.length; j++) {
732          if (gradebook.projects[i].tasks[j].hasOwnProperty("item_guid")) {
733            // gradebook project item
734            if (gradebook.projects[i].tasks[j].item_guid === item_guid) {
735              gradebook.projects[i].tasks.splice(j, 1);
736              for (var k = 0; k < amount_students; k++) {
737                gradebook.students[k].projects[i].tasks.splice(j, 1);
738              }
739              return render();
740            }
741          }
742        }
743      }
744    }
745  }
746
747  function updateItemAndRender(item) {
748    for (var i = 0; i < amount_projects; i++) {
749      if (gradebook.projects[i].hasOwnProperty("item_guid")) {
750        // gradebook classroom item
751        if (gradebook.projects[i].item_guid === item.item_guid) {
752          gradebook.projects[i] = item;
753          console.log("UPDATED");
754          return render();
755        }
756      } else {
757        for (var j = 0; j < gradebook.projects[i].tasks.length; j++) {
758          if (gradebook.projects[i].tasks[j].hasOwnProperty("item_guid")) {
759            // gradebook project item
760            if (gradebook.projects[i].tasks[j].item_guid === item.item_guid) {
761              gradebook.projects[i].tasks[j] = item;
762              console.log("UPDATED");
763              return render();
764            }
765          }
766        }
767      }
768    }
769  }
770
771  function updateItemStudentAndRender(item_guid, student_guid, score) {
772    for (var i = 0; i < amount_students; i++) {
773      if (gradebook.students[i].guid === student_guid) {
774        for (var j = 0; j < amount_projects; j++) {
775          if (gradebook.students[i].projects[j].hasOwnProperty("item_guid")) {
776            // gradebook classroom item
777            if (gradebook.students[i].projects[j].item_guid === item_guid) {
778              gradebook.students[i].projects[j].score = score;
779              return render();
780            }
781          } else {
782            for (
783              var k = 0;
784              k < gradebook.students[i].projects[j].tasks.length;
785              k++
786            ) {
787              if (
788                gradebook.students[i].projects[j].tasks[k].hasOwnProperty(
789                  "item_guid"
790                )
791              ) {
792                // gradebook project item
793                if (
794                  gradebook.students[i].projects[j].tasks[k].item_guid ===
795                  item_guid
796                ) {
797                  gradebook.students[i].projects[j].tasks[k].score = score;
798                  return render();
799                }
800              }
801            }
802          }
803        }
804      }
805    }
806  }
807
808  function exportGradebook() {
809    window.open(
810      config.api +
811        "classroom/gradebook/export/" +
812        mDiscover.getSession() +
813        "/" +
814        mTeacherHome.getClassroomGuid(),
815      "_blank"
816    );
817  }
818  /**
819   * Constructor de módulo
820   * @returns {undefined}
821   */
822  function build(callback) {
823    builded = true;
824
825    scope.html(getHtmlGradebook());
826
827    scope.find(".d-gradebook").scroll(function (e) {
828      //console.log(e);
829      try {
830        scope
831          .find(".d-left-fixed")
832          .css("left", e.currentTarget.scrollLeft + "px");
833      } catch (err) {
834        console.log(err);
835      }
836    });
837
838    // order by
839    scope.find(".d-header.h1").click(function (e) {
840      current_order_by = "orderNameValue";
841      current_order_by_mode = current_order_by_mode === "asc" ? "desc" : "asc";
842      scope
843        .find(".d-header.h1 span")
844        .removeClass("asc desc")
845        .addClass(current_order_by_mode);
846      doRender("h2");
847    });
848    scope
849      .find(".d-header.h2 .on-score-ten, .d-header.h2 .orderby")
850      .click(function (e) {
851        current_order_by = "score";
852        current_order_by_mode =
853          current_order_by_mode === "asc" ? "desc" : "asc";
854        scope
855          .find(".d-header.h2 span")
856          .removeClass("asc desc")
857          .addClass(current_order_by_mode);
858        doRender("h1");
859      });
860    function doRender(tg) {
861      gradebook.students = sortByKey(
862        gradebook.students,
863        current_order_by,
864        current_order_by_mode
865      );
866      render();
867      scope.find(".d-header.h2").removeClass("no-active");
868      scope.find(".d-header.h1").removeClass("no-active");
869      scope.find(".d-header." + tg).addClass("no-active");
870    }
871
872    // button export
873    scope.find(".d-header-link .btn-export").click(exportGradebook);
874
875    scope.find(".d-header-tabs-nav-item").click(function(){
876      const route = $(this).attr("data-route");
877      routie("teacher/classroom/" + mTeacherHome.getClassroomGuid() + "/" + route);
878    });
879    const segments = window.location.href.split('/');
880    const lastUrlSegment = segments[segments.length - 1];
881    scope.find(".d-header-tabs-nav-item[data-route='"+lastUrlSegment+"']").addClass("active");
882
883    if (isFunction(callback)) callback();
884  }
885
886  /**
887   * Constructor
888   * @returns {undefined}
889   */
890  function init() {
891    // nada, latente hasta show
892  }
893  init();
894
895  // helpers
896  /* html of module */
897  function getHtmlEmptyStudents() {
898    return (
899      '<div class="d-empty-gradebook"><div class="d-icon"></div><span>' +
900      lang.gradebook_empty +
901      "</span></div>"
902    );
903  }
904  function getHtmlHeaderTabsNav() {
905    if (!isEnableGlobalEduca) {
906      return "";
907    }
908    return (
909      '       <div class="d-header-tabs-nav">' +
910      '           <div class="d-header-tabs-nav-items">' +
911      '             <div class="d-header-tabs-nav-item" data-route="gradebook">' +
912      '                 <div class="d-header-tabs-nav-item-text">'+lang.home_teacher_header_classroom_tab6+'</div>' +
913      "             </div>" +
914      '             <div class="d-header-tabs-nav-item" data-route="globaleduca">' +
915      '                 <div class="d-header-tabs-nav-item-icon">' +
916      '                     <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 512 512" class="icon icon--lib-core icon--size-big icon--color-label injected-svg" title=""><path fill="#7FBA26" d="M513 99c0 105.021 0 210.043-.38 315.344-.723.625-1.3.927-1.374 1.323-5.275 28.308-19.2 51.4-41.227 69.93-15.489 13.028-33.029 21.362-52.733 25.472-.859.18-1.528 1.267-2.286 1.931-105.021 0-210.043 0-315.344-.38-.625-.723-.927-1.3-1.323-1.374-28.308-5.275-51.4-19.2-69.93-41.227C15.376 454.53 7.042 436.99 2.932 417.286c-.18-.859-1.267-1.528-1.931-2.286 0-105.021 0-210.043.38-315.344.723-.625 1.3-.927 1.374-1.323 5.275-28.308 19.2-51.4 41.227-69.93C59.47 15.376 77.01 7.042 96.714 2.932 97.573 2.751 98.242 1.664 99 1c105.021 0 210.043 0 315.344.38.625.723.927 1.3 1.323 1.374 28.308 5.275 51.4 19.2 69.93 41.227 13.028 15.489 21.362 33.028 25.472 52.733.18.859 1.267 1.528 1.931 2.286m-93.72 154.217c-.54-3.116-.978-6.254-1.634-9.345-7.794-36.71-27.03-66.64-54.512-91.63-5.929-5.391-13.724-5.056-19.704.906a45849 45849 0 0 0-95.443 95.441c-6.445 6.465-6.374 14.188.014 20.787 5.33 5.507 10.837 10.844 16.266 16.254 8.752 8.721 15.524 8.698 24.368-.136 19.334-19.314 38.648-38.648 57.982-57.963 1.377-1.375 2.848-2.657 4.604-4.288 1.082 1.695 1.957 2.863 2.623 4.14 11.751 22.532 15.449 46.424 10.6 71.271-11.67 59.794-66.946 97.508-127.299 87.287-48.417-8.2-86.888-51.682-89.313-100.607-1.122-22.613 3.744-43.627 15.19-63.165 4.963-8.471 4.05-14.48-2.96-21.529-5.523-5.553-11.037-11.116-16.623-16.606-8.017-7.88-16.276-7.086-22.524 2.283-23.155 34.72-32.264 72.954-26.593 114.181 12.375 89.96 93.593 151.892 183.658 140.664 90.957-11.338 155.444-96.615 141.3-187.945m-229.912-136.35c-12.02 12.02-24.062 24.016-36.049 36.068-7.081 7.118-7.036 14.633.072 21.815 5.744 5.804 11.498 11.599 17.33 17.314 6.574 6.444 14.3 6.556 20.726.145a61750 61750 0 0 0 95.45-95.441c6.48-6.494 6.467-14.178.077-20.727-5.587-5.724-11.285-11.34-16.956-16.98-7.805-7.765-15.065-7.761-22.879.043-19.1 19.078-38.182 38.175-57.771 57.764m77.884 18.835c-21.903 21.903-43.832 43.78-65.695 65.725-6.824 6.85-6.787 14.613-.022 21.515a1162 1162 0 0 0 17.312 17.308c6.991 6.838 14.555 6.828 21.51-.104a32478 32478 0 0 0 65.36-65.351c7.172-7.19 7.189-14.68.141-21.817a2143 2143 0 0 0-16.954-16.96c-6.987-6.917-13.614-7.155-21.652-.316"></path><path fill="#FFFFFE" d="M189.618 116.618c19.339-19.34 38.42-38.436 57.521-57.514 7.814-7.804 15.074-7.808 22.88-.043 5.67 5.64 11.368 11.256 16.955 16.98 6.39 6.549 6.403 14.233-.077 20.727a61750 61750 0 0 1-95.45 95.441c-6.426 6.411-14.152 6.299-20.727-.145-5.831-5.715-11.585-11.51-17.329-17.314-7.108-7.182-7.153-14.697-.072-21.815 11.987-12.052 24.03-24.048 36.299-36.317"></path><path fill="#FEFEFD" d="M267.509 135.45c7.781-6.586 14.408-6.348 21.395.57a2143 2143 0 0 1 16.954 16.96c7.048 7.135 7.031 14.627-.14 21.816a32478 32478 0 0 1-65.36 65.35c-6.956 6.933-14.52 6.943-21.51.105-5.834-5.705-11.6-11.48-17.313-17.308-6.765-6.902-6.802-14.666.022-21.515 21.863-21.944 43.792-43.822 65.952-65.978"></path></svg>
916' +
917      '                 </div>' +
918      '                 <div class="d-header-tabs-nav-item-text">'+lang.home_teacher_header_classroom_tab7+'</div>' +
919      "             </div>" +
920      "         </div>" +
921      "       </div>"
922    );
923  }
924  function getHtmlGradebook() {
925    return (
926      getHtmlHeaderTabsNav() +
927      '       <div class="d-header-link">' +
928      '           <div class="btn btn-export standard-text-shadow-1">' +
929      lang.gradebook_export +
930      "</div>" +
931      "       </div>" +
932      '       <div class="d-gradebook standard-inner-shadow-1 standard-scrollbar-3">' +
933      '            <div class="d-left-fixed standard-inner-shadow-4">' +
934      '               <div class="d-headers">' +
935      '                   <div class="d-header h1">' +
936      lang.classroom_info_researchers +
937      ' <span class="orderby ' +
938      current_order_by_mode +
939      '"></span></div>' +
940      /*'                   <div class="d-header h2">Calificación <div class="d-order-box standard-inner-shadow-3"><div>' + lang.gradebook_current_order_by_name + '</div><span class="' + current_order_by_mode + '"></span></div></div>' +*/
941      '                   <div class="d-header h2">' +
942      lang.gradebook_score +
943      "/ " +
944      lang.gradebook_project_item_points +
945      '<span class="orderby ' +
946      current_order_by_mode +
947      '" ></span > <div class="on-score-ten">10</div><div><span class="d-total">-</span></div></div > ' +
948      /*'                   <div class="d-header h3"></div>' +*/
949      "               </div>" +
950      '               <div class="d-col-students">' +
951      "               </div>" +
952      "            </div>" +
953      '            <div class="d-right">' +
954      "            </div>" +
955      "       </div>"
956    );
957  }
958  function getHtmlStudent(student) {
959    return (
960      '<div class="d-student">' +
961      '   <div class="d-avatar">' +
962      getHtmlStudentAvatar(student, "x-small") +
963      "</div>" +
964      '   <div class="d-name">' +
965      getStudentName(student) +
966      "</div>" +
967      '   <div class="d-score">' +
968      (isNaN(student.score) ? "-" : student.score) +
969      "</div>" +
970      '   <div class="d-points">' +
971      student.student_total +
972      "</div>" +
973      "</div>"
974    );
975  }
976  function getHtmlStudentTasks(student) {
977    var html =
978      '<div class="d-col-project-student" student_guid="' + student.guid + '">';
979    for (var i = 0; i < amount_projects; i++) {
980      if (student.projects[i].hasOwnProperty("item_guid")) {
981        // gradebook classroom item
982        var score =
983          student.projects[i].score !== null ? student.projects[i].score : " ";
984        html +=
985          '<div class="d-project active" item_guid=' +
986          student.projects[i].item_guid +
987          ">" +
988          '   <div class="d-item-points" contenteditable="true">' +
989          score +
990          "</div>" +
991          "</div>";
992      } else {
993        html +=
994          '<div class="d-project" guid="' +
995          student.projects[i].guid +
996          '"><div class="score">' +
997          (student.projects[i].project_score || 0) +
998          '</div><div class="points">' +
999          student.projects[i].project_total +
1000          "</div></div>";
1001        html +=
1002          '<div class="d-col-project-student-tasks" guid="' +
1003          student.projects[i].guid +
1004          '">';
1005
1006        for (var j = 0; j < student.projects[i].tasks.length; j++) {
1007          if (student.projects[i].tasks[j].hasOwnProperty("item_guid")) {
1008            // gradebook project item
1009            var score =
1010              student.projects[i].tasks[j].score !== null
1011                ? student.projects[i].tasks[j].score
1012                : " ";
1013            html +=
1014              '<div class="d-project-task active" item_guid=' +
1015              student.projects[i].tasks[j].item_guid +
1016              ">" +
1017              '   <div class="d-item-points" contenteditable="true">' +
1018              score +
1019              "</div>" +
1020              "</div>";
1021          } else {
1022            var student_score = student.projects[i].tasks[j].student_score
1023              ? student.projects[i].tasks[j].student_score
1024              : "0";
1025            var score =
1026              student.projects[i].tasks[j].status === "corrected"
1027                ? student_score
1028                : "-";
1029            if (!student.projects[i].tasks[j].with_access) {
1030              score = "*";
1031            }
1032            html += '<div class="d-project-task">
1032' + score + "</div>";
1033          }
1034        }
1035
1036        html += getHtmlNewItemStudent(student.projects[i].guid);
1037        html += "</div>";
1038      }
1039    }
1040    html += getHtmlNewItemStudent();
1041    html += "</div>";
1042    return html;
1043  }
1044  function getHtmlHeaderTasks() {
1045    var amount = gradebook.projects.length;
1046    var html = '<div class="d-right-header">';
1047    for (var i = 0; i < amount; i++) {
1048      var project = gradebook.projects[i];
1049      var amount_tasks = 0;
1050      var amount_quizzes = 0;
1051      if (project.hasOwnProperty("item_guid")) {
1052        // gradebook classroom item
1053        html += getHtmlNewItem(null, project);
1054      } else {
1055        var title_project =
1056          userLang === "eu"
1057            ? i + 1 + ". " + lang.gradebook_project_title
1058            : lang.gradebook_project_title + " " + (i + 1);
1059        html +=
1060          '<div class="d-right-header-project" guid="' +
1061          gradebook.projects[i].guid +
1062          '">' +
1063          '    <div class="d-title"><div class="d-project-status"></div><div class="d-title-text">' +
1064          project.title +
1065          "</div></div>" +
1066          '    <div class="d-points">' +
1067          gradebook.projects[i].project_score +
1068          "</div>" +
1069          '    <div class="d-ten-score">10</div>' +
1070          "</div>" +
1071          '<div class="d-right-header-project-tasks" guid="' +
1072          gradebook.projects[i].guid +
1073          '" >';
1074
1075        for (var j = 0; j < project.tasks.length; j++) {
1076          var task = project.tasks[j];
1077          if (task.hasOwnProperty("item_guid")) {
1078            // gradebook project item
1079            html +=
1080              '<div class="d-right-header-task active" item_guid="' +
1081              task.item_guid +
1082              '">' +
1083              '   <div class="d-btn-add btn btn-bitmap"></div>' +
1084              '   <div class="d-item-form project">' +
1085              '       <div class="d-item-remove btn btn-bitmap"></div>' +
1086              '       <div class="d-item-title" contenteditable="true" placeholder="' +
1087              lang.gradebook_project_item_new +
1088              '">' +
1089              (task.name !== null && task.name ? task.name : "") +
1090              "</div>" +
1091              '       <div class="d-item-points" contenteditable="true" placeholder="' +
1092              lang.gradebook_project_item_points +
1093              '">' +
1094              (task.score !== null && task.score ? task.score : " ") +
1095              "</div>" +
1096              "   </div>" +
1097              "</div>";
1098          } else {
1099            var task_title = "";
1100
1101            if (task.type === "quizz") {
1102              amount_quizzes++;
1103              task_title =
1104                userLang === "eu"
1105                  ? amount_quizzes + ". " + lang.gradebook_project_quizz
1106                  : lang.gradebook_project_quizz + " " + amount_quizzes;
1107            } else {
1108              amount_tasks++;
1109              task_title =
1110                userLang === "eu"
1111                  ? amount_tasks + ". " + lang.gradebook_project_task
1112                  : lang.gradebook_project_task + " " + amount_tasks;
1113            }
1114            html +=
1115              '<div class="d-right-header-task" guid="' +
1116              task.task_guid +
1117              '">' +
1118              '    <div class="d-title">' +
1119              task_title +
1120              "</div>" +
1121              '    <div class="d-points">' +
1122              (task.task_score !== null ? task.task_score : "") +
1123              "</div>" +
1124              "</div>";
1125          }
1126        }
1127
1128        html += getHtmlNewItem(gradebook.projects[i].guid);
1129        html += "</div>";
1130      }
1131    }
1132    html += getHtmlNewItem();
1133    html += "</div>";
1134    return html;
1135  }
1136  function getHtmlNewItem(_project_guid, _gradebook_item) {
1137    var gradebook_item = _gradebook_item || null;
1138    var project_guid = _project_guid || null;
1139    var attr_project_guid =
1140      project_guid !== null ? ' project_guid="' + project_guid + '"' : "";
1141    var tile_class =
1142      project_guid !== null ? "d-right-header-task" : "d-right-header-project";
1143    var active_class = gradebook_item !== null ? "active" : "new-item";
1144    var attr_item =
1145      gradebook_item !== null
1146        ? 'item_guid="' + gradebook_item.item_guid + '"'
1147        : "";
1148    var general_class = project_guid !== null ? "item-project" : "item-general";
1149    return (
1150      '<div class="' +
1151      tile_class +
1152      " " +
1153      active_class +
1154      " " +
1155      general_class +
1156      ' "' +
1157      attr_project_guid +
1158      " " +
1159      attr_item +
1160      ">" +
1161      '   <div class="d-btn-add btn btn-bitmap" title="Agregar nuevo item"></div>' +
1162      '   <div class="d-item-form ' +
1163      general_class +
1164      '">' +
1165      '       <div class="d-item-remove btn btn-bitmap"></div>' +
1166      '       <div class="d-item-title" contenteditable="true" placeholder="' +
1167      lang.gradebook_project_item_new +
1168      '">' +
1169      (gradebook_item !== null && gradebook_item.name
1170        ? gradebook_item.name
1171        : "") +
1172      "</div>" +
1173      '       <div class="d-item-points" contenteditable="true" placeholder="' +
1174      lang.gradebook_project_item_points +
1175      '">' +
1176      (gradebook_item !== null &&
1177      gradebook_item.score !== null &&
1178      gradebook_item.score
1179        ? gradebook_item.score
1180        : " ") +
1181      "</div>" +
1182      "   </div>" +
1183      "</div>"
1184    );
1185  }
1186  function getHtmlNewItemStudent(_project_guid) {
1187    var project_guid = _project_guid || null;
1188    var attr_project_guid =
1189      project_guid !== null ? ' project_guid="' + project_guid + '"' : "";
1190    var tile_class = project_guid !== null ? "d-project-task" : "d-project";
1191    return (
1192      '<div class="' +
1193      tile_class +
1194      ' new-item" ' +
1195      attr_project_guid +
1196      ">" +
1197      '   <div class="d-item-points" contenteditable="true">&nbsp;</div>' +
1198      "</div>"
1199    );
1200  }
1201  // public methods
1202  this.show = show;
1203};

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.