<commit_before>define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) {

  return DataTablePage.extend({

    el: '.js-page-jobs',

    template:"#tpl-page-jobs",

    events:{
      "click .js-datatable-filters-submit": "filterschanged",
      "click .js-datatable .js-actions button": "row_jobaction",
      "click button.js-jobs-groupaction": "groupaction"
    },

    initFilters: function() {
      this.filters = {
        "worker": this.options.params.worker||"",
        "queue": this.options.params.queue||"",
        "path": this.options.params.path||"",
        "status": this.options.params.status||"",
        "exceptiontype": this.options.params.exceptiontype||"",
        "params": this.options.params.params||"",
        "id": this.options.params.id||"",
      };
    },

    setOptions:function(options) {
      this.options = options;
      this.initFilters();
      this.flush();
    },

    refresh_logs:function(job_id) {

      var self = this;

      $.ajax("/api/logs?job="+job_id+"&last_log_id="+self.last_log_id, {
        "type": "GET",
        "success": function(data) {
          if (!self.last_log_id) {
            self.$(".js-jobs-modal .js-jobs-modal-content").html("");
          }
          self.$(".js-jobs-modal .js-jobs-modal-content")[0].innerHTML += _.escape(data.logs);
          self.last_log_id = data.last_log_id;
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        }
      });

    },

    refreshStackTrace: function(jobId)  {
      var self = this;

      $.ajax("/api/job/"+jobId+"/traceback", {
        "type": "GET",
        "success": function(data) {
          var stack = self.format_traceback(data["traceback"]);
          self.$(".js-jobs-modal .js-jobs-modal-content").html(stack);
          self.$(".js-jobs-modal h4").html("Stack Trace");
          self.$(".js-jobs-modal").modal({});
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        }
      });
    },

    refreshCallStack: function(jobId)  {
      var self = this;
      self.app.getJobsDataFromWorkers(function(err, jobs) {

        if (err) {
          self.$(".js-jobs-modal-callstack-outdated").show();
        }

        var job_data = _.find(jobs, function(job) {
          return job.id == jobId;
        });

        if (job_data && job_data["stack"]) {
          var stack = self.format_traceback((job_data["stack"] || []).join(""));
          self.$(".js-jobs-modal .js-jobs-modal-content").html(stack);
        } else {
          self.$(".js-jobs-modal-callstack-outdated").show();
        }

      });

    },
    groupaction: function(evt) {
      evt.preventDefault();
      evt.stopPropagation();

      var self = this;

      var action = $(evt.target).data("action");
      var data = _.clone(this.filters);

      data["action"] = action;
      self.jobaction(evt, data);

    },

    format_traceback: function(stack) {

      // Escape it to avoid XSS
      stack = _.escape(stack.replace(/\\n/g, "
"));

      // Try to insert links to source code
      var online_repositories = window.MRQ_CONFIG.dashboard_autolink_repositories || [];

      if (online_repositories.length) {

        stack = stack.replace(/File "(.+?)", line ([0-9]+)/gm, function(m, file_path, line) {
          for (var i=0;i<online_repositories.length;i++) {
            var regex = new RegExp(online_repositories[i][0]);
            if (file_path.match(regex)) {
              var file_url = file_path.replace(regex, online_repositories[i][1]);
              return "File ""+file_path+"", line "+line+"";
            }
          }
          return m;
        });

      }

      return stack;
    },

    row_jobaction:function(evt) {
      evt.preventDefault();
      evt.stopPropagation();

      var self = this;

      var job_id = $(evt.currentTarget).closest(".js-actions").data("jobid");
      var action = $(evt.currentTarget).data("action");

      self.$(".js-jobs-modal").unbind();

      if (action == "viewresult") {

        $.ajax("/api/job/"+job_id+"/result", {
          "type": "GET",
          "success": function(data) {
            self.$(".js-jobs-modal .js-jobs-modal-content").html(_.escape(JSON.stringify(data, null, 2)));
            self.$(".js-jobs-modal h4").html("Job result");
            self.$(".js-jobs-modal").modal({});
          },
          "error": function(xhr, status, error) {
            alert("Error: "+error);
          }
        });

      } else if (action == "viewlogs") {

        self.last_log_id = "";

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Job logs");
        self.$(".js-jobs-modal").modal({});

        // These poll events are sent by the generic datatable refresh() method
        self.$(".js-jobs-modal").on("poll", function() {
          self.refresh_logs(job_id);
        });
        self.refresh_logs(job_id);

      } else if (action == "copycommand") {

        var html = "<textarea style='width:100%; height:400px;'>Loading...</textarea>";

        var job_data = self.jobData[job_id];

        self.$(".js-jobs-modal .js-jobs-modal-content").html(html);
        self.$(".js-jobs-modal h4").html("Command-line for this job");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal textarea")[0].value = "mrq-run " + job_data.path + " '" + JSON.stringify(job_data.params).replace("'","\\'") + "'";

      } else if (action == "viewtraceback") {

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Stack Trace");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal").on("poll", function() {
          self.refreshStackTrace(job_id);
        });
        self.refreshStackTrace(job_id);

      } else if (action == "viewcallstack") {

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Current call stack");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal").on("poll", function() {
          self.refreshCallStack(job_id);
        });
        self.refreshCallStack(job_id);

      } else {

        self.jobaction(evt, {
          "id": job_id,
          "action": action
        });

      }

    },

    jobaction:function(evt, data) {

      $(evt.target).find(".glyphicon").addClass("spin");

      $.ajax("/api/jobaction", {
        "type": "POST",
        "data": data,
        "success": function(data) {
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        },
        "complete": function() {
          setTimeout(function() {
            $(evt.target).find(".glyphicon").removeClass("spin");
          }, 500);
        }
      });
    },

    renderDatatable:function() {

      var self = this;

      var datatableConfig = self.getCommonDatatableConfig("jobs");

      _.extend(datatableConfig, {
        "aoColumns": [

          {
            "sTitle": "Path & ID",
            "sClass": "col-jobs-path",
            "sWidth":"35%",
            "mDataProp": "path",
            "mData": function ( source /*, val */) {
              return ""+source.path+""+
                "

"+source._id+"";
            }
          },
          {
            "sTitle": "Params",
            "sWidth":"65%",
            "sClass": "col-jobs-params",
            "mDataProp": "params",
            "mData": function ( source /*, val */) {
              return "
"+_.escape(JSON.stringify(source.params, null, 2))+"
";
            }
          },
          {
            "sTitle": "Status",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-status",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {

                var status_classes = {
                  'started': "label-success",
                  'success': "label-success",
                  'timeout': "label-danger",
                  'failed': "label-danger",
                  'maxretries': "label-danger",
                  'interrupt': "label-danger",
                  'cancel': "label-warning",
                  'abort': "label-warning",
                  'retry': "label-warning"
                };
                var css_class = status_classes[source.status] || "label-info";

                html = "
" + "" + (source.status || "queued") + "";
                html += "

";

                if (source.progress) {
                  var progress = (Math.round(source.progress*10000)/100);
                  html += '

'+progress+'%

';
                }

                html += "<button class='btn btn-xs btn-default' data-action='viewtraceback'> Trace</button>";
                html += "

<button class='btn btn-xs btn-default' data-action='viewcallstack'> Stack</button>";
                html += "
";
                return (html);
              } else {
                return source.status || "queued";
              }
            }
          },
          {
            "sTitle": "Time",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-time",
            "mData":function(source, type/*, val*/) {

              if (type == "display") {
                var display = [
                  "queued "+moment.utc(1000 * parseInt(source._id.substring(0, 8), 16)).fromNow()
                  //"updated "+moment.utc(source.dateupdated).fromNow()
                ];

                if (source.datestarted) {
                  display.push("started "+moment.utc(source.datestarted).fromNow());
                }
                if (source.totaltime) {
                  display.push("totaltime "+String(source.totaltime).substring(0,6)+"s");
                }
                if (source.time) {
                  display.push("cputime "+String(source.time).substring(0,6)+"s ("+source.switches+" switches)");
                }

                return "" + display.join("
") + "";

              } else {
                return source.datestarted || "";
              }
            }
          },
          {
            "sTitle": "Queue",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-queue",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {
                return source.queue?(""+source.queue+""):"";
              } else {
                return source.queue || "";
              }

            }
          },
          {
            "sTitle": "Worker",
            "sType":"string",
            "sWidth":"140px",
            "sClass": "col-jobs-worker",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {
                return source.worker?(""+source.worker+""):"";
              } else {
                return source.worker || "";
              }
            }
          },
          {
            "sTitle": "Actions",
            "sType":"string",
            "sWidth":"200px",
            "sClass": "col-jobs-action",
            "mData":function(source, type) {
              if (type == "display") {
                return "
"+
                  "<button class='btn btn-xs btn-default' data-action='viewlogs'> Logs</button>"+
                  "<button class='pull-right btn btn-xs btn-default' data-action='viewresult'> Result</button>"+
                  "

"+
                  "<button class='pull-right btn btn-xs btn-default' data-action='copycommand'> Command</button>"+
                  "

"+
                  "<button class='btn btn-xs btn-danger pull-right' data-action='cancel'> Cancel</button>"+
                  "<button class='btn btn-xs btn-warning' data-action='requeue'> Requeue</button>"+
                "
";
              }
              return "";
            }
          }

        ],
        "aaSorting":[ [0,'asc'] ],

        "fnDrawCallback": function (oSettings) {

          self.jobData = {};

          _.each(oSettings.aoData,function(row) {
            var oData = row._aData;
            self.jobData[oData["_id"]] = oData;
          });
        }
      });

      this.initDataTable(datatableConfig);

      if (!_.any(this.filters, function(v, k) {
        return v;
      })) {
        this.$(".js-jobs-groupactions").hide();
      }

    },

    filterschanged:function(evt) {

      var self = this;

      if (evt) {
        evt.preventDefault();
        evt.stopPropagation();
      }

      _.each(self.filters, function(v, k) {
        self.filters[k] = self.$(".js-datatable-filters-"+k).val();
      });

      window.location = "/#jobs?"+$.param(self.filters, true).replace(/\+/g, "%20");
    },

  });

});
<commit_msg>Front update for traceback histories<commit_after>define(["jquery", "underscore", "views/generic/datatablepage", "models"],function($, _, DataTablePage, Models) {

  return DataTablePage.extend({

    el: '.js-page-jobs',

    template:"#tpl-page-jobs",

    events:{
      "click .js-datatable-filters-submit": "filterschanged",
      "click .js-datatable .js-actions button": "row_jobaction",
      "click button.js-jobs-groupaction": "groupaction"
    },

    initFilters: function() {
      this.filters = {
        "worker": this.options.params.worker||"",
        "queue": this.options.params.queue||"",
        "path": this.options.params.path||"",
        "status": this.options.params.status||"",
        "exceptiontype": this.options.params.exceptiontype||"",
        "params": this.options.params.params||"",
        "id": this.options.params.id||"",
      };
    },

    setOptions:function(options) {
      this.options = options;
      this.initFilters();
      this.flush();
    },

    refresh_logs:function(job_id) {

      var self = this;

      $.ajax("/api/logs?job="+job_id+"&last_log_id="+self.last_log_id, {
        "type": "GET",
        "success": function(data) {
          if (!self.last_log_id) {
            self.$(".js-jobs-modal .js-jobs-modal-content").html("");
          }
          self.$(".js-jobs-modal .js-jobs-modal-content")[0].innerHTML += _.escape(data.logs);
          self.last_log_id = data.last_log_id;
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        }
      });

    },

    refreshStackTrace: function(jobId)  {
      var self = this;

      $.ajax("/api/job/"+jobId+"/traceback", {
        "type": "GET",
        "success": function(data) {
          if (data["traceback"]) {
            var stack = self.format_traceback(data["traceback"]);
            self.$(".js-jobs-modal .js-jobs-modal-content").html(stack);
          }
          else {
            var stack = self.format_traceback_history(data["traceback_history"]);
            self.$(".js-jobs-modal .js-jobs-modal-content").html(stack);
          }
          self.$(".js-jobs-modal h4").html("Stack Trace");
          self.$(".js-jobs-modal").modal({});
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        }
      });
    },

    refreshCallStack: function(jobId)  {
      var self = this;
      self.app.getJobsDataFromWorkers(function(err, jobs) {

        if (err) {
          self.$(".js-jobs-modal-callstack-outdated").show();
        }

        var job_data = _.find(jobs, function(job) {
          return job.id == jobId;
        });

        if (job_data && job_data["stack"]) {
          var stack = self.format_traceback((job_data["stack"] || []).join(""));
          self.$(".js-jobs-modal .js-jobs-modal-content").html(stack);
        } else {
          self.$(".js-jobs-modal-callstack-outdated").show();
        }

      });

    },
    groupaction: function(evt) {
      evt.preventDefault();
      evt.stopPropagation();

      var self = this;

      var action = $(evt.target).data("action");
      var data = _.clone(this.filters);

      data["action"] = action;
      self.jobaction(evt, data);

    },

    format_traceback: function(stack) {

      // Escape it to avoid XSS
      stack = _.escape(stack.replace(/\\n/g, "
"));

      // Try to insert links to source code
      var online_repositories = window.MRQ_CONFIG.dashboard_autolink_repositories || [];

      if (online_repositories.length) {

        stack = stack.replace(/File "(.+?)", line ([0-9]+)/gm, function(m, file_path, line) {
          for (var i=0;i<online_repositories.length;i++) {
            var regex = new RegExp(online_repositories[i][0]);
            if (file_path.match(regex)) {
              var file_url = file_path.replace(regex, online_repositories[i][1]);
              return "File ""+file_path+"", line "+line+"";
            }
          }
          return m;
        });

      }

      return stack;
    },
    format_traceback_history: function(stacks) {
      var self = this;
      var full_history = "";
      _.each(stacks, function(stack) {
        full_history += "
" + stack["date"] + "
";
        if (stack["original_traceback"]) {
          full_history += "Original trace";
          full_history += self.format_traceback(stack["original_traceback"] || "");
        }
        full_history += "Trace" + "
";
        full_history += self.format_traceback(stack["traceback"] || "");
        full_history += "---------------------------------------------------------------------" + "
";
      });
      return full_history;
    },
    row_jobaction:function(evt) {
      evt.preventDefault();
      evt.stopPropagation();

      var self = this;

      var job_id = $(evt.currentTarget).closest(".js-actions").data("jobid");
      var action = $(evt.currentTarget).data("action");

      self.$(".js-jobs-modal").unbind();

      if (action == "viewresult") {

        $.ajax("/api/job/"+job_id+"/result", {
          "type": "GET",
          "success": function(data) {
            self.$(".js-jobs-modal .js-jobs-modal-content").html(_.escape(JSON.stringify(data, null, 2)));
            self.$(".js-jobs-modal h4").html("Job result");
            self.$(".js-jobs-modal").modal({});
          },
          "error": function(xhr, status, error) {
            alert("Error: "+error);
          }
        });

      } else if (action == "viewlogs") {

        self.last_log_id = "";

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Job logs");
        self.$(".js-jobs-modal").modal({});

        // These poll events are sent by the generic datatable refresh() method
        self.$(".js-jobs-modal").on("poll", function() {
          self.refresh_logs(job_id);
        });
        self.refresh_logs(job_id);

      } else if (action == "copycommand") {

        var html = "<textarea style='width:100%; height:400px;'>Loading...</textarea>";

        var job_data = self.jobData[job_id];

        self.$(".js-jobs-modal .js-jobs-modal-content").html(html);
        self.$(".js-jobs-modal h4").html("Command-line for this job");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal textarea")[0].value = "mrq-run " + job_data.path + " '" + JSON.stringify(job_data.params).replace("'","\\'") + "'";

      } else if (action == "viewtraceback") {

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Stack Trace");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal").on("poll", function() {
          self.refreshStackTrace(job_id);
        });
        self.refreshStackTrace(job_id);

      } else if (action == "viewcallstack") {

        self.$(".js-jobs-modal .js-jobs-modal-content").html("Loading...");
        self.$(".js-jobs-modal h4").html("Current call stack");
        self.$(".js-jobs-modal").modal({});

        self.$(".js-jobs-modal").on("poll", function() {
          self.refreshCallStack(job_id);
        });
        self.refreshCallStack(job_id);

      } else {

        self.jobaction(evt, {
          "id": job_id,
          "action": action
        });

      }

    },

    jobaction:function(evt, data) {

      $(evt.target).find(".glyphicon").addClass("spin");

      $.ajax("/api/jobaction", {
        "type": "POST",
        "data": data,
        "success": function(data) {
        },
        "error": function(xhr, status, error) {
          alert("Error: "+error);
        },
        "complete": function() {
          setTimeout(function() {
            $(evt.target).find(".glyphicon").removeClass("spin");
          }, 500);
        }
      });
    },

    renderDatatable:function() {

      var self = this;

      var datatableConfig = self.getCommonDatatableConfig("jobs");

      _.extend(datatableConfig, {
        "aoColumns": [

          {
            "sTitle": "Path & ID",
            "sClass": "col-jobs-path",
            "sWidth":"35%",
            "mDataProp": "path",
            "mData": function ( source /*, val */) {
              return ""+source.path+""+
                "

"+source._id+"";
            }
          },
          {
            "sTitle": "Params",
            "sWidth":"65%",
            "sClass": "col-jobs-params",
            "mDataProp": "params",
            "mData": function ( source /*, val */) {
              return "
"+_.escape(JSON.stringify(source.params, null, 2))+"
";
            }
          },
          {
            "sTitle": "Status",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-status",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {

                var status_classes = {
                  'started': "label-success",
                  'success': "label-success",
                  'timeout': "label-danger",
                  'failed': "label-danger",
                  'maxretries': "label-danger",
                  'interrupt': "label-danger",
                  'cancel': "label-warning",
                  'abort': "label-warning",
                  'retry': "label-warning"
                };
                var css_class = status_classes[source.status] || "label-info";

                html = "
" + "" + (source.status || "queued") + "";
                html += "

";

                if (source.progress) {
                  var progress = (Math.round(source.progress*10000)/100);
                  html += '

'+progress+'%

';
                }

                html += "<button class='btn btn-xs btn-default' data-action='viewtraceback'> Trace</button>";
                html += "

<button class='btn btn-xs btn-default' data-action='viewcallstack'> Stack</button>";
                html += "
";
                return (html);
              } else {
                return source.status || "queued";
              }
            }
          },
          {
            "sTitle": "Time",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-time",
            "mData":function(source, type/*, val*/) {

              if (type == "display") {
                var display = [
                  "queued "+moment.utc(1000 * parseInt(source._id.substring(0, 8), 16)).fromNow()
                  //"updated "+moment.utc(source.dateupdated).fromNow()
                ];

                if (source.datestarted) {
                  display.push("started "+moment.utc(source.datestarted).fromNow());
                }
                if (source.totaltime) {
                  display.push("totaltime "+String(source.totaltime).substring(0,6)+"s");
                }
                if (source.time) {
                  display.push("cputime "+String(source.time).substring(0,6)+"s ("+source.switches+" switches)");
                }

                return "" + display.join("
") + "";

              } else {
                return source.datestarted || "";
              }
            }
          },
          {
            "sTitle": "Queue",
            "sType":"string",
            "sWidth":"100px",
            "sClass": "col-jobs-queue",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {
                return source.queue?(""+source.queue+""):"";
              } else {
                return source.queue || "";
              }

            }
          },
          {
            "sTitle": "Worker",
            "sType":"string",
            "sWidth":"140px",
            "sClass": "col-jobs-worker",
            "mData":function(source, type/*, val*/) {
              if (type == "display") {
                return source.worker?(""+source.worker+""):"";
              } else {
                return source.worker || "";
              }
            }
          },
          {
            "sTitle": "Actions",
            "sType":"string",
            "sWidth":"200px",
            "sClass": "col-jobs-action",
            "mData":function(source, type) {
              if (type == "display") {
                return "
"+
                  "<button class='btn btn-xs btn-default' data-action='viewlogs'> Logs</button>"+
                  "<button class='pull-right btn btn-xs btn-default' data-action='viewresult'> Result</button>"+
                  "

"+
                  "<button class='pull-right btn btn-xs btn-default' data-action='copycommand'> Command</button>"+
                  "

"+
                  "<button class='btn btn-xs btn-danger pull-right' data-action='cancel'> Cancel</button>"+
                  "<button class='btn btn-xs btn-warning' data-action='requeue'> Requeue</button>"+
                "
";
              }
              return "";
            }
          }

        ],
        "aaSorting":[ [0,'asc'] ],

        "fnDrawCallback": function (oSettings) {

          self.jobData = {};

          _.each(oSettings.aoData,function(row) {
            var oData = row._aData;
            self.jobData[oData["_id"]] = oData;
          });
        }
      });

      this.initDataTable(datatableConfig);

      if (!_.any(this.filters, function(v, k) {
        return v;
      })) {
        this.$(".js-jobs-groupactions").hide();
      }

    },

    filterschanged:function(evt) {

      var self = this;

      if (evt) {
        evt.preventDefault();
        evt.stopPropagation();
      }

      _.each(self.filters, function(v, k) {
        self.filters[k] = self.$(".js-datatable-filters-"+k).val();
      });

      window.location = "/#jobs?"+$.param(self.filters, true).replace(/\+/g, "%20");
    },

  });

});
<|endoftext|>


<commit_before>var PhotoClassList = {
    is_desktop: undefined,

    hide_card: function() {
        if (window.user.instructor) {
            return false;
        }
        return true;
    },

    render: function() {
        if (PhotoClassList.hide_card()) {
            return;
        }
        showLoading();

        if (window.hasOwnProperty('section_data') &&
            window.section_data.hasOwnProperty('section')) {

            WSData.fetch_instructed_section_details(window.section_data.section,
                                                    PhotoClassList.render_upon_data,
                                                    PhotoClassList.render_error);
        }
        //PhotoClassList.make_html();
    },

    render_upon_data: function() {
        // build class list content
        var source = $("#photo_class_list").html();
        var template = Handlebars.compile(source);
        var data = WSData.instructed_section_details();
        var registrations = data.sections[0].registrations;
        data.sections[0].registrations = PhotoClassList.sort_students(registrations,
                                                                      'surname,name');

        if (window.section_data.hasOwnProperty('available_sections') &&
                window.section_data.available_sections.length > 1) {
            data.available_sections = window.section_data.available_sections;
        }

        $("#app_content").html(template(data));

        // add event handlers
        $("#download_class_list").on("click", PhotoClassList.download_list);

        $("#sort_list").on("change", function() {
            var data = WSData.instructed_section_details();
            var registrations = data.sections[0].registrations;
            var sorted = PhotoClassList.sort_students(registrations,
                                                      this.value);

            var new_body = $("<div>");
            for (var i = 0; i < sorted.length; i++) {
                var regid = sorted[i].regid;
                var student_row = $("#student_"+regid);
                new_body.append(student_row);
            }

            $("#student_sort").html(new_body.html());
        });

        $("#list_view").on("click", function(e) {
            e.preventDefault();
            $("#student_sort").removeClass("grid-view");
        });

        $("#grid_view").on("click", function(e) {
            e.preventDefault();
            $("#student_sort").addClass("grid-view");
        });

        $("#available_sections").on("change", function() {
            window.section_data.section = this.value;
            PhotoClassList.render();
        });

    },

    sort_students: function(registrations, key) {
        var fields = key.split(',');
        if (!registrations || !registrations.length) {
            return null;
        }
        for (var i = fields.length-1; i >= 0; i--) {
            registrations = PhotoClassList.sort_registrations(registrations,
                                                              fields[i]);
        }
        return registrations;
    },

    sort_registrations: function(registrations, key) {

        var sorted = registrations.sort(function(a, b) {
            var av = a[key];
            var bv = b[key];

            if (av === undefined && bv !== undefined) {
                return 1;
            }
            if (av !== undefined && bv === undefined) {
                return -1;
            }
            // A guess at what sorting majors looks like
            try {
                av = av.map(function(x) { return x.full_name; }).join(",");
                bv = bv.map(function(x) { return x.full_name; }).join(",");
            }
            catch(exception) {
            }

            if (av > bv) {
                return 1;
            }
            if (av < bv) {
                return -1;
            }
            return 0;
        });

        return sorted;
    },

    download_name: function(section, year, quarter) {
        var base_name = section.curriculum_abbr+"_"+section.course_number+"_"+section.section_id+"_"+quarter+"_"+year+"_students.csv";

        return base_name.replace(/[^a-z0-9\._]/ig, '_');
    },

    quote_field: function(x) {
        if (x) {
            x = x.replace(/"/g, '\\"');
            return '"'+x+'"';
        }
        else {
            return '""';
        }
    },

    combine_majors: function(list) {
        if (list) {
            return list.map(function(x) { return x.full_name; }).join(", ");
        }
        return "";
    },

    build_download: function(data) {
        var registrations = PhotoClassList.sort_students(data.registrations,
                                                         'surname,name');
        var lines = [],
            header = ["Student Number", "UW NetID", "Name", "Last Name"],
            type;
        for (var i = 0; i < data.linked_types.length; i++) {
            type = data.linked_types[i];
            var upper = type.replace(/^([a-z])/, function(match) { return match.toUpperCase(); });
            header.push(upper+" Section");
        }

        header.push("Credits", "Class", "Majors", "Email");

        lines.push(header.join(","));

        for (i = 0; i < registrations.length; i++) {
            var reg = registrations[i];

            var linked = reg.linked_sections;

            var by_type = {};
            for (var j = 0; j < linked.length; j++) {
                type = linked[j].type;
                var section_string = linked[j].sections.join(",");
                by_type[type] = section_string;
            }

            var fields = [reg.student_number,
                          reg.netid,
                          reg.name,
                          reg.surname];

            for (j = 0; j < data.linked_types.length; j++) {
                type = data.linked_types[j];
                if (by_type[type]) {
                    fields.push(by_type[type]);
                }
                else {
                    fields.push("");
                }
            }

            fields.push(reg.credits,
                        reg.class,
                        PhotoClassList.combine_majors(reg.majors),
                        reg.email);

            lines.push(fields.map(PhotoClassList.quote_field).join(","));
        }
        return lines.join("\n");
    },

    download_list: function() {
        var data = WSData.instructed_section_details();
        var download = PhotoClassList.build_download(data.sections[0]);
        var blob = new Blob([download], {type: "text/csv" });

        var section = data.sections[0];
        var filename = PhotoClassList.download_name(section, data.year, data.quarter);
        // from http://stackoverflow.com/questions/3665115/create-a-file-in-memory-for-user-to-download-not-through-server
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        }
        else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);

            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }

        return false;
    },

    render_error: function() {
        var error_code = WSData.instructed_section_details_error_code(),
            source,
            course_template;

        if (error_code == 410) {
            Error410.render();
            return;
        }

        if (error_code === 403) {
            source = $("#instructor_section_card_not_instructor").html();
            courses_template = Handlebars.compile(source);
            $("#app_content").html(courses_template());
        } else if (error_code === 404) {
            source = $("#instructor_section_card_no_course").html();
            courses_template = Handlebars.compile(source);
            $("#app_content").html(courses_template());
        } else {
            source = CardWithError.render("Teaching Section");
            $("#app_content").html(source);
        }

    }

};

/* node.js exports */
if (typeof exports == "undefined") {
    var exports = {};
}
exports.PhotoClassList = PhotoClassList;
<commit_msg>fix section selector for class lists<commit_after>var PhotoClassList = {
    is_desktop: undefined,

    hide_card: function() {
        if (window.user.instructor) {
            return false;
        }
        return true;
    },

    render: function() {
        if (PhotoClassList.hide_card()) {
            return;
        }
        showLoading();

        if (window.hasOwnProperty('section_data') &&
            window.section_data.hasOwnProperty('section')) {

            WSData.fetch_instructed_section_details(window.section_data.section,
                                                    PhotoClassList.render_upon_data,
                                                    PhotoClassList.render_error);
        }
        //PhotoClassList.make_html();
    },

    render_upon_data: function() {
        // build class list content
        var source = $("#photo_class_list").html();
        var template = Handlebars.compile(source);
        var data = WSData.instructed_section_details();
        var registrations = data.sections[0].registrations;
        data.sections[0].registrations = PhotoClassList.sort_students(registrations,
                                                                      'surname,name');

        if (window.section_data.hasOwnProperty('available_sections') &&
                window.section_data.available_sections.length > 1) {
            data.available_sections = window.section_data.available_sections;
        }

        $("#main-content").html(template(data));

        // add event handlers
        $("#download_class_list").on("click", PhotoClassList.download_list);

        $("#sort_list").on("change", function() {
            var data = WSData.instructed_section_details();
            var registrations = data.sections[0].registrations;
            var sorted = PhotoClassList.sort_students(registrations,
                                                      this.value);

            var new_body = $("<div>");
            for (var i = 0; i < sorted.length; i++) {
                var regid = sorted[i].regid;
                var student_row = $("#student_"+regid);
                new_body.append(student_row);
            }

            $("#student_sort").html(new_body.html());
        });

        $("#list_view").on("click", function(e) {
            e.preventDefault();
            $("#student_sort").removeClass("grid-view");
        });

        $("#grid_view").on("click", function(e) {
            e.preventDefault();
            $("#student_sort").addClass("grid-view");
        });

        $("#available_sections").on("change", function() {
            window.section_data.section = $(this).val();
            PhotoClassList.render();
        }).val(window.section_data.section);

    },

    sort_students: function(registrations, key) {
        var fields = key.split(',');
        if (!registrations || !registrations.length) {
            return null;
        }
        for (var i = fields.length-1; i >= 0; i--) {
            registrations = PhotoClassList.sort_registrations(registrations,
                                                              fields[i]);
        }
        return registrations;
    },

    sort_registrations: function(registrations, key) {

        var sorted = registrations.sort(function(a, b) {
            var av = a[key];
            var bv = b[key];

            if (av === undefined && bv !== undefined) {
                return 1;
            }
            if (av !== undefined && bv === undefined) {
                return -1;
            }
            // A guess at what sorting majors looks like
            try {
                av = av.map(function(x) { return x.full_name; }).join(",");
                bv = bv.map(function(x) { return x.full_name; }).join(",");
            }
            catch(exception) {
            }

            if (av > bv) {
                return 1;
            }
            if (av < bv) {
                return -1;
            }
            return 0;
        });

        return sorted;
    },

    download_name: function(section, year, quarter) {
        var base_name = section.curriculum_abbr+"_"+section.course_number+"_"+section.section_id+"_"+quarter+"_"+year+"_students.csv";

        return base_name.replace(/[^a-z0-9\._]/ig, '_');
    },

    quote_field: function(x) {
        if (x) {
            x = x.replace(/"/g, '\\"');
            return '"'+x+'"';
        }
        else {
            return '""';
        }
    },

    combine_majors: function(list) {
        if (list) {
            return list.map(function(x) { return x.full_name; }).join(", ");
        }
        return "";
    },

    build_download: function(data) {
        var registrations = PhotoClassList.sort_students(data.registrations,
                                                         'surname,name');
        var lines = [],
            header = ["Student Number", "UW NetID", "Name", "Last Name"],
            type;
        for (var i = 0; i < data.linked_types.length; i++) {
            type = data.linked_types[i];
            var upper = type.replace(/^([a-z])/, function(match) { return match.toUpperCase(); });
            header.push(upper+" Section");
        }

        header.push("Credits", "Class", "Majors", "Email");

        lines.push(header.join(","));

        for (i = 0; i < registrations.length; i++) {
            var reg = registrations[i];

            var linked = reg.linked_sections;

            var by_type = {};
            for (var j = 0; j < linked.length; j++) {
                type = linked[j].type;
                var section_string = linked[j].sections.join(",");
                by_type[type] = section_string;
            }

            var fields = [reg.student_number,
                          reg.netid,
                          reg.name,
                          reg.surname];

            for (j = 0; j < data.linked_types.length; j++) {
                type = data.linked_types[j];
                if (by_type[type]) {
                    fields.push(by_type[type]);
                }
                else {
                    fields.push("");
                }
            }

            fields.push(reg.credits,
                        reg.class,
                        PhotoClassList.combine_majors(reg.majors),
                        reg.email);

            lines.push(fields.map(PhotoClassList.quote_field).join(","));
        }
        return lines.join("\n");
    },

    download_list: function() {
        var data = WSData.instructed_section_details();
        var download = PhotoClassList.build_download(data.sections[0]);
        var blob = new Blob([download], {type: "text/csv" });

        var section = data.sections[0];
        var filename = PhotoClassList.download_name(section, data.year, data.quarter);
        // from http://stackoverflow.com/questions/3665115/create-a-file-in-memory-for-user-to-download-not-through-server
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        }
        else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);

            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }

        return false;
    },

    render_error: function() {
        var error_code = WSData.instructed_section_details_error_code(),
            source,
            course_template;

        if (error_code == 410) {
            Error410.render();
            return;
        }

        if (error_code === 403) {
            source = $("#instructor_section_card_not_instructor").html();
            courses_template = Handlebars.compile(source);
            $("#app_content").html(courses_template());
        } else if (error_code === 404) {
            source = $("#instructor_section_card_no_course").html();
            courses_template = Handlebars.compile(source);
            $("#app_content").html(courses_template());
        } else {
            source = CardWithError.render("Teaching Section");
            $("#app_content").html(source);
        }

    }

};

/* node.js exports */
if (typeof exports == "undefined") {
    var exports = {};
}
exports.PhotoClassList = PhotoClassList;
<|endoftext|>


<commit_before>var AWS = require('aws-sdk');
var s3 = new AWS.S3();

exports.handler = function(event, context) {
    console.log(JSON.stringify(event, null, 2));

    var stackName = context.functionName.split("-")[0];
    var accountId = event.Records[0].eventSourceARN.split(":")[4];
    var s3Bucket = stackName + "-eventarchive-" + accountId;
    var date = new Date();
    var s3Key = date.toISOString().split("T")[0] + "/" + event.Records[0].kinesis.sequenceNumber + ".json";

    var body = "{ \"Records\" : [\n";

    event.Records.forEach(function(record, index) {
        // Kinesis data is base64 encoded so decode here
        var payload = new Buffer(record.kinesis.data, 'base64').toString('ascii');

        console.log('Decoded payload:', payload);

        body = body + (index == 0 ? "" : ",\n") + payload;
    });

    body = body + "\n]}";

    var params = {
        Bucket: s3Bucket,
        Key: s3Key,
        Body: body
    };

    s3.putObject(params, function(err, data) {
        if (err) {
            console.log(err, err.stack); // an error occurred
            context.fail(err);
        } else {
            console.log(data);           // successful response
            context.succeed();
        }
    });
};
<commit_msg>Add check for no records.<commit_after>var AWS = require('aws-sdk');
var s3 = new AWS.S3();

exports.handler = function(event, context) {
    console.log(JSON.stringify(event, null, 2));

    if(event.Records.length == 0) {
        //Nothing to do
        console.log('Got no records.');
        context.succeed();
        return;
    }

    var firstRecord = event.Records[0];
    var stackName = context.functionName.split("-")[0];
    var accountId = firstRecord.eventSourceARN.split(":")[4];
    var s3Bucket = stackName + "-eventarchive-" + accountId;
    var date = new Date();
    var s3Key = date.toISOString().split("T")[0] + "/" + firstRecord.kinesis.sequenceNumber + ".json";

    var body = "{ \"Records\" : [\n";

    event.Records.forEach(function(record, index) {
        // Kinesis data is base64 encoded so decode here
        var payload = new Buffer(record.kinesis.data, 'base64').toString('ascii');

        console.log('Decoded payload:', payload);

        body = body + (index == 0 ? "" : ",\n") + payload;
    });

    body = body + "\n]}";

    var params = {
        Bucket: s3Bucket,
        Key: s3Key,
        Body: body
    };

    s3.putObject(params, function(err, data) {
        if (err) {
            console.log(err, err.stack); // an error occurred
            context.fail(err);
        } else {
            console.log(data);           // successful response
            context.succeed();
        }
    });
};
<|endoftext|>


<commit_before>import * as action from './actions';
import ng from 'core/services/ng';
import {countOccurrences, forEachMatch} from './reducers/find-replace';
import {toHTML} from './html';
import {clearHighlights} from './reducers/find-replace';

/**
 * @type {Object} Redux store
 * @description Holds the store of the currently active body editor of the open article.
 * @private
 */
let store = null;

/**
 * @ngdoc service
 * @module superdesk.core.editor3
 * @name editor3
 * @description editor3 is the service that allows interacting with the editor from
 * the outside. It uses the same interface as the editor service of core/editor2/editor.js
 * to allow plugging one or the other based on the editor of the item being edited.
 */
export class EditorService {
    /**
     * @ngdoc method
     * @name editor3#setStore
     * @param {Object} redux store
     * @description Registers the passed redux store with the service.
     */
    setStore(s) {
        if (store !== null) {
            console.warn('You\'ve overwritten the find & replace target.');
        }

        store = s;
    }

    /**
     * @ngdoc method
     * @name editor3#version
     * @description Returns the editor version (this is for when using editorResolver).
     * @returns {string}
     */
    version() {
        return '3';
    }

    /**
     * @ngdoc method
     * @name editor3#unsetStore
     * @description Clears the store.
     */
    unsetStore() {
        store = null;
    }

    /**
     * @ngdoc method
     * @name editor3#selectNext
     * @description Triggers the action to select the next occurence of the search
     * criteria in the editor.
     */
    selectNext() {
        ok() && store.dispatch(action.findNext());
    }

    /**
     * @ngdoc method
     * @name editor3#selectPrev
     * @description Triggers the action to select the previous occurence of the search
     * criteria in the editor.
     */
    selectPrev() {
        ok() && store.dispatch(action.findPrev());
    }

    /**
     * @ngdoc method
     * @name editor3#replace
     * @param {string} txt
     * @description Replaces the currently highlighted search criteria with the given text.
     */
    replace(txt) {
        ok() && store.dispatch(action.replace(txt));
    }

    /**
     * @ngdoc method
     * @name editor3#replace
     * @param {string} txt
     * @description Replaces all the search criteria with the given text.
     */
    replaceAll(txt) {
        ok() && store.dispatch(action.replaceAll(txt));
    }

    /**
     * @ngdoc method
     * @name editor3#setSettings
     * @param {Object} findReplace The new search criteria. An object containing the keys
     * caseSensitive (boolean) and diff (object having one key that is the pattern).
     * @description Updates the search criteria.
     */
    setSettings({findreplace}) {
        if (!ok()) {
            return;
        }

        if (findreplace === null) {
            store.dispatch(action.setHighlightCriteria({pattern: ''}));

            return;
        }

        const {diff, caseSensitive} = findreplace;
        const pattern = Object.keys(diff || {})[0] || '';

        store.dispatch(action.setHighlightCriteria({pattern, caseSensitive}));
    }

    /**
     * @ngdoc method
     * @name editor3#render
     * @description Highlights the current search criteria in the editor.
     */
    render() {
        ok() && store.dispatch(action.renderHighlights());
    }

    /**
     * @ngdoc method
     * @name editor3#countErrors
     * @description Not sure what this method is supposed to do. It's needed by
     * the interface and it's copied from `core/editor2/editor.js#93`.
     */
    countErrors() {
        return ng.get('$q').when(countOccurrences(store.getState()));
    }

    /**
     * @ngdoc method
     * @name editor3#getActiveText
     * @description Gets the text under the current selection.
     */
    getActiveText() {
        if (!ok()) {
            return;
        }

        const state = store.getState();
        const {editorState, searchTerm} = state;
        const {index, pattern, caseSensitive} = searchTerm;
        const content = editorState.getCurrentContent();

        let txt = pattern;

        // find the active match
        forEachMatch(content, pattern, caseSensitive, (i, selection, block) => {
            if (i === index) {
                const start = selection.getStartOffset();
                const end = selection.getEndOffset();

                txt = block.getText().slice(start, end);
            }
        });

        return txt;
    }

    /**
     * @ngdoc method
     * @name editor3#getHTML
     * @description Gets the content of the editor as HTML.
     * @returns {string} HTML
     */
    getHTML() {
        if (!ok()) {
            return '';
        }

        const state = store.getState();
        const content = state.editorState.getCurrentContent();
        const cleanedContent = clearHighlights(content).content;

        return toHTML(cleanedContent);
    }

    /**
     * @ngdoc method
     * @name editor3#setHTML
     * @param {string} html
     * @description Replaces the content of the editor with the given HTML.
     */
    setHTML(html) {
        ok() && store.dispatch(action.setHTML(html));
    }
}

function ok() {
    if (store === null) {
        console.error('No editor set as target in service.');
    }

    return store !== null;
}
<commit_msg>fix(unspike): fix unspike when editor3 is used<commit_after>import * as action from './actions';
import ng from 'core/services/ng';
import {countOccurrences, forEachMatch} from './reducers/find-replace';
import {toHTML} from './html';
import {clearHighlights} from './reducers/find-replace';

/**
 * @type {Object} Redux store
 * @description Holds the store of the currently active body editor of the open article.
 * @private
 */
let store = null;

/**
 * @ngdoc service
 * @module superdesk.core.editor3
 * @name editor3
 * @description editor3 is the service that allows interacting with the editor from
 * the outside. It uses the same interface as the editor service of core/editor2/editor.js
 * to allow plugging one or the other based on the editor of the item being edited.
 */
export class EditorService {
    /**
     * @ngdoc method
     * @name editor3#setStore
     * @param {Object} redux store
     * @description Registers the passed redux store with the service.
     */
    setStore(s) {
        if (store !== null) {
            console.warn('You\'ve overwritten the find & replace target.');
        }

        store = s;
    }

    /**
     * @ngdoc method
     * @name editor3#version
     * @description Returns the editor version (this is for when using editorResolver).
     * @returns {string}
     */
    version() {
        return '3';
    }

    /**
     * @ngdoc method
     * @name editor3#unsetStore
     * @description Clears the store.
     */
    unsetStore() {
        store = null;
    }

    /**
     * @ngdoc method
     * @name editor3#selectNext
     * @description Triggers the action to select the next occurence of the search
     * criteria in the editor.
     */
    selectNext() {
        ok() && store.dispatch(action.findNext());
    }

    /**
     * @ngdoc method
     * @name editor3#selectPrev
     * @description Triggers the action to select the previous occurence of the search
     * criteria in the editor.
     */
    selectPrev() {
        ok() && store.dispatch(action.findPrev());
    }

    /**
     * @ngdoc method
     * @name editor3#replace
     * @param {string} txt
     * @description Replaces the currently highlighted search criteria with the given text.
     */
    replace(txt) {
        ok() && store.dispatch(action.replace(txt));
    }

    /**
     * @ngdoc method
     * @name editor3#replace
     * @param {string} txt
     * @description Replaces all the search criteria with the given text.
     */
    replaceAll(txt) {
        ok() && store.dispatch(action.replaceAll(txt));
    }

    /**
     * @ngdoc method
     * @name editor3#setSettings
     * @param {Object} findReplace The new search criteria. An object containing the keys
     * caseSensitive (boolean) and diff (object having one key that is the pattern).
     * @description Updates the search criteria.
     */
    setSettings({findreplace}) {
        if (!ok()) {
            return;
        }

        if (findreplace === null) {
            store.dispatch(action.setHighlightCriteria({pattern: ''}));

            return;
        }

        const {diff, caseSensitive} = findreplace;
        const pattern = Object.keys(diff || {})[0] || '';

        store.dispatch(action.setHighlightCriteria({pattern, caseSensitive}));
    }

    /**
     * @ngdoc method
     * @name editor3#render
     * @description Highlights the current search criteria in the editor.
     */
    render() {
        ok() && store.dispatch(action.renderHighlights());
    }

    /**
     * @ngdoc method
     * @name editor3#countErrors
     * @description Not sure what this method is supposed to do. It's needed by
     * the interface and it's copied from `core/editor2/editor.js#93`.
     */
    countErrors() {
        return ng.get('$q').when(store ? countOccurrences(store.getState()) : null);
    }

    /**
     * @ngdoc method
     * @name editor3#getActiveText
     * @description Gets the text under the current selection.
     */
    getActiveText() {
        if (!ok()) {
            return;
        }

        const state = store.getState();
        const {editorState, searchTerm} = state;
        const {index, pattern, caseSensitive} = searchTerm;
        const content = editorState.getCurrentContent();

        let txt = pattern;

        // find the active match
        forEachMatch(content, pattern, caseSensitive, (i, selection, block) => {
            if (i === index) {
                const start = selection.getStartOffset();
                const end = selection.getEndOffset();

                txt = block.getText().slice(start, end);
            }
        });

        return txt;
    }

    /**
     * @ngdoc method
     * @name editor3#getHTML
     * @description Gets the content of the editor as HTML.
     * @returns {string} HTML
     */
    getHTML() {
        if (!ok()) {
            return '';
        }

        const state = store.getState();
        const content = state.editorState.getCurrentContent();
        const cleanedContent = clearHighlights(content).content;

        return toHTML(cleanedContent);
    }

    /**
     * @ngdoc method
     * @name editor3#setHTML
     * @param {string} html
     * @description Replaces the content of the editor with the given HTML.
     */
    setHTML(html) {
        ok() && store.dispatch(action.setHTML(html));
    }
}

function ok() {
    if (store === null) {
        console.error('No editor set as target in service.');
    }

    return store !== null;
}
<|endoftext|>


<commit_before>import { confirm } from 'src/util/ui';

import Roc from '../../rest-on-couch/Roc';

import Status from './Status';
import processAction from './processAction';

const muteSuccess = { muteSuccess: true };

export default class RequestManager {
  constructor(couchDB, options) {
    options = options || {};
    this.roc = new Roc({
      url: couchDB.url,
      database: couchDB.database
    });
    this.sampleRoc = options.sampleRoc || null;
    this.servicesRoc = options.servicesRoc || null;
    this.servicesView = null;
  }

  getStatus(request) {
    return request.$content.status[0];
  }

  getStatusCode(request) {
    return Number(this.getStatus(request).status);
  }

  async cancel(request) {
    if (this.getStatusCode(request) === 0) {
      return null;
    }
    if (await confirm('cancel request?')) {
      return this.setStatus(request, 0);
    }
    return null;
  }

  async setStatus(request, status) {
    if (typeof status !== 'number') {
      status = Status.getStatusCode(status);
    }
    if (this.getStatusCode(request) === status) {
      return;
    }
    request.$content.status.unshift({
      date: Date.now(),
      status: status
    });
    await this.roc.update(request, muteSuccess);
  }
  async createCustomRequest(sample, options = {}) {
    let groups = options.groups || [];
    if (typeof groups === 'string') groups = [groups];

    const kind = options.kind || '';
    const data = options.data || {};
    const disableNotification = options.disableNotification;
    // allow each lab analytical lab to edit the entry
    for (const group of groups) {
      await this.sampleRoc.addGroup(sample, group, disableNotification);
    }
    // we can only add one request
    const requestObject = {
      $content: {
        productUuid: String(sample._id),
        productId: sample.$id,
        analysis: {
          kind,
          data
        },
        status: [
          {
            date: Date.now(),
            status: 10
          }
        ]
      },
      $owners: groups
    };

    return this.roc.create(requestObject, disableNotification);
  }

  async createRequests(sample, list) {
    const groups = Array.from(new Set(list.map((el) => el.kind)));
    // allow each lab to edit the entry
    for (const group of groups) {
      await this.sampleRoc.addGroup(sample, group, muteSuccess);
    }
    for (const req of list) {
      const requestObject = {
        $content: {
          productUuid: String(sample._id),
          productId: sample.$id,
          analysis: req,
          status: [
            {
              date: Date.now(),
              status: 10
            }
          ]
        },
        $owners: [req.kind]
      };
      await this.roc.create(requestObject, muteSuccess);
    }
  }

  async view(name, options) {
    const viewData = await this.roc.view(name, options);
    viewData.sort(sortRequestsByStatus);
    addChangeListener(viewData);
    return viewData;
  }

  async initServices(name, options) {
    if (this.servicesView) {
      return this.servicesView;
    } else {
      this.servicesView = this.servicesRoc.view(name, options);
      return this.servicesView;
    }
  }

  /**
   *
   * @param {string} scan : either the result of a scan or an uuid
   */
  async getRequest(scan) {
    let uuid = scan.replace(/[^a-z0-9:_]/g, '');
    if (uuid.includes('_')) {
      uuid = uuid.replace(/.*_r:([a-z0-9]*).*/, '$1');
    }
    if (uuid.length === 0) return undefined;
    if (uuid.length !== 32) {
      return undefined;
    }
    return this.roc.document(uuid);
  }

  async getSample(request) {
    return this.sampleRoc.document(request.$content.productUuid);
  }

  async getPrintTemplate(analysis) {
    const experiment = await this.getExperiment(analysis);
    return experiment.twig;
  }

  async getExperiment(analysis) {
    const services = await this.initServices();
    const serviceId = analysis.kind;
    const instrument = analysis.instrument;
    const configName = analysis.configuration;
    return services
      .find((s) => String(s.$id) === String(serviceId))
      .$content.instruments.find((i) => String(i.name) === String(instrument))
      .experiments.find((e) => String(e.name) === String(configName));
  }

  async getUserInfo() {
    return this.roc.getUserInfo();
  }
}

RequestManager.prototype.processAction = processAction;

function addChangeListener(view) {
  const id = view.onChange((event, triggerId) => {
    if (triggerId !== id) {
      updateView(view, id);
    }
  });
  updateView(view, id);
}

function updateView(view, id) {
  view.forEach(updateRequest);
  view.triggerChange(false, id);
}

function updateRequest(request) {
  const lastStatus = request.$content.status[0];
  const status = Status.getStatus(lastStatus.status);
  request.statusText = status[0];
  request.statusColor = status[1];
}

function sortRequestsByStatus(req1, req2) {
  return req1.$content.status[0].status - req2.$content.status[0].status;
}
<commit_msg>fix local roc of RequestManager<commit_after>import { confirm } from 'src/util/ui';

import Roc from '../../rest-on-couch/Roc';

import Status from './Status';
import processAction from './processAction';

const muteSuccess = { muteSuccess: true };

export default class RequestManager {
  constructor(couchDB, options) {
    options = options || {};
    this.roc = new Roc({
      url: couchDB.url,
      database: couchDB.database,
      kind: 'analysisRequest'
    });
    this.sampleRoc = options.sampleRoc || null;
    this.servicesRoc = options.servicesRoc || null;
    this.servicesView = null;
  }

  getStatus(request) {
    return request.$content.status[0];
  }

  getStatusCode(request) {
    return Number(this.getStatus(request).status);
  }

  async cancel(request) {
    if (this.getStatusCode(request) === 0) {
      return null;
    }
    if (await confirm('cancel request?')) {
      return this.setStatus(request, 0);
    }
    return null;
  }

  async setStatus(request, status) {
    if (typeof status !== 'number') {
      status = Status.getStatusCode(status);
    }
    if (this.getStatusCode(request) === status) {
      return;
    }
    request.$content.status.unshift({
      date: Date.now(),
      status: status
    });
    await this.roc.update(request, muteSuccess);
  }
  async createCustomRequest(sample, options = {}) {
    let groups = options.groups || [];
    if (typeof groups === 'string') groups = [groups];

    const kind = options.kind || '';
    const data = options.data || {};
    const disableNotification = options.disableNotification;
    // allow each lab analytical lab to edit the entry
    for (const group of groups) {
      await this.sampleRoc.addGroup(sample, group, disableNotification);
    }
    // we can only add one request
    const requestObject = {
      $content: {
        productUuid: String(sample._id),
        productId: sample.$id,
        analysis: {
          kind,
          data
        },
        status: [
          {
            date: Date.now(),
            status: 10
          }
        ]
      },
      $owners: groups
    };

    return this.roc.create(requestObject, disableNotification);
  }

  async createRequests(sample, list) {
    const groups = Array.from(new Set(list.map((el) => el.kind)));
    // allow each lab to edit the entry
    for (const group of groups) {
      await this.sampleRoc.addGroup(sample, group, muteSuccess);
    }
    for (const req of list) {
      const requestObject = {
        $content: {
          productUuid: String(sample._id),
          productId: sample.$id,
          analysis: req,
          status: [
            {
              date: Date.now(),
              status: 10
            }
          ]
        },
        $owners: [req.kind]
      };
      await this.roc.create(requestObject, muteSuccess);
    }
  }

  async view(name, options) {
    const viewData = await this.roc.view(name, options);
    viewData.sort(sortRequestsByStatus);
    addChangeListener(viewData);
    return viewData;
  }

  async initServices(name, options) {
    if (this.servicesView) {
      return this.servicesView;
    } else {
      this.servicesView = this.servicesRoc.view(name, options);
      return this.servicesView;
    }
  }

  /**
   *
   * @param {string} scan : either the result of a scan or an uuid
   */
  async getRequest(scan) {
    let uuid = scan.replace(/[^a-z0-9:_]/g, '');
    if (uuid.includes('_')) {
      uuid = uuid.replace(/.*_r:([a-z0-9]*).*/, '$1');
    }
    if (uuid.length === 0) return undefined;
    if (uuid.length !== 32) {
      return undefined;
    }
    return this.roc.document(uuid);
  }

  async getSample(request) {
    return this.sampleRoc.document(request.$content.productUuid);
  }

  async getPrintTemplate(analysis) {
    const experiment = await this.getExperiment(analysis);
    return experiment.twig;
  }

  async getExperiment(analysis) {
    const services = await this.initServices();
    const serviceId = analysis.kind;
    const instrument = analysis.instrument;
    const configName = analysis.configuration;
    return services
      .find((s) => String(s.$id) === String(serviceId))
      .$content.instruments.find((i) => String(i.name) === String(instrument))
      .experiments.find((e) => String(e.name) === String(configName));
  }

  async getUserInfo() {
    return this.roc.getUserInfo();
  }
}

RequestManager.prototype.processAction = processAction;

function addChangeListener(view) {
  const id = view.onChange((event, triggerId) => {
    if (triggerId !== id) {
      updateView(view, id);
    }
  });
  updateView(view, id);
}

function updateView(view, id) {
  view.forEach(updateRequest);
  view.triggerChange(false, id);
}

function updateRequest(request) {
  const lastStatus = request.$content.status[0];
  const status = Status.getStatus(lastStatus.status);
  request.statusText = status[0];
  request.statusColor = status[1];
}

function sortRequestsByStatus(req1, req2) {
  return req1.$content.status[0].status - req2.$content.status[0].status;
}
<|endoftext|>


<commit_before>var clone = require('clone');

function TransactionStore(store) {
  this._store = store;
}

TransactionStore.prototype.serializeClient = function(fn) {
  this._serializeClientFunc = fn;
}

TransactionStore.prototype.deserializeClient = function(fn) {
  this._deserializeClientFunc = fn;
}

TransactionStore.prototype.load = function(req, cb) {
  var self = this;

  function loaded(err, state) {
    if (err) { return cb(err); }
    if (!state) { return cb(null); }

    state.transactionID = state.handle;
    state.locals = {};
    if (state.authN) {
      state.locals.authN = state.authN;
      delete state.authN;
    }
    if (state.deviceCode) {
      state.locals.deviceCode = state.deviceCode;
      delete state.deviceCode;
    }
    if (state.consent) {
      state.locals.consent = state.consent;
      delete state.consent;
    }

    self._deserializeClientFunc(state.client, function(err, client) {
      if (err) { return cb(err); }
      if (!client) {
        return cb(new Error('Failed to deserialize client'));
      }

      state.client = client;
      return cb(null, state);
    });
  }

  if (req.state) {
    loaded(null, clone(req.state));
  } else {
    // FIXME: The following line is a workaround for a misleading exception thrown when /authorize
    //        is missing a required parameter
    /// AuthorizationError: Missing required parameter: response_type
    if (!req.body) { return loaded(null); }

    this._store.load(req, req.body.state, loaded);
  }
}

TransactionStore.prototype.store = function(req, txn, cb) {
  console.log('@@@@@ OAuth 2.0 store state');
  console.log(req.state);
  console.log(txn)
  console.log('@@@@@-----');

  var self = this;
  this._serializeClientFunc(txn.client, function(err, client) {
    if (err) { return cb(err); }

    req.state.client = client;
    req.state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { req.state.webOrigin = txn.webOrigin; }
    req.state.req = txn.req;
    return cb();

    /*
    var state = {};
    state.name = 'oauth2-authorize';
    state.client = client;
    state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { state.webOrigin = txn.webOrigin; }
    state.req = txn.req;

    self._store.save(req, state, function(err, h) {
      if (err) { return cb(err); }
      cb(null, h);
    });
    */
  });
}

TransactionStore.prototype.update = function(req, h, txn, cb) {
  var self = this;
  this._serializeClientFunc(txn.client, function(err, client) {
    if (err) { return cb(err); }

    var state = {};
    state.name = 'oauth2-authorize';
    state.initiatedAt = txn.initiatedAt;
    state.client = client;
    state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { state.webOrigin = txn.webOrigin; }
    state.req = txn.req;
    if (txn.locals) {
      if (txn.locals.authN) {
        state.authN = txn.locals.authN;
      }
      if (txn.locals.deviceCode) {
        state.deviceCode = txn.locals.deviceCode;
      }
    }

    self._store.update(req, h, state, function(err, h) {
      if (err) { return cb(err); }
      cb(null, h);
    });
  });
}

TransactionStore.prototype.remove = function(req, h, cb) {
  this._store.destroy(req, h, cb);
}

module.exports = TransactionStore;
<commit_msg>Save state directly.<commit_after>var clone = require('clone');

function TransactionStore(store) {
  this._store = store;
}

TransactionStore.prototype.serializeClient = function(fn) {
  this._serializeClientFunc = fn;
}

TransactionStore.prototype.deserializeClient = function(fn) {
  this._deserializeClientFunc = fn;
}

TransactionStore.prototype.load = function(req, cb) {
  var self = this;

  function loaded(err, state) {
    if (err) { return cb(err); }
    if (!state) { return cb(null); }

    state.transactionID = state.handle;
    state.locals = {};
    if (state.authN) {
      state.locals.authN = state.authN;
      delete state.authN;
    }
    if (state.deviceCode) {
      state.locals.deviceCode = state.deviceCode;
      delete state.deviceCode;
    }
    if (state.consent) {
      state.locals.consent = state.consent;
      delete state.consent;
    }

    self._deserializeClientFunc(state.client, function(err, client) {
      if (err) { return cb(err); }
      if (!client) {
        return cb(new Error('Failed to deserialize client'));
      }

      state.client = client;
      return cb(null, state);
    });
  }

  if (req.state) {
    loaded(null, clone(req.state));
  } else {
    // FIXME: The following line is a workaround for a misleading exception thrown when /authorize
    //        is missing a required parameter
    /// AuthorizationError: Missing required parameter: response_type
    if (!req.body) { return loaded(null); }

    this._store.load(req, req.body.state, loaded);
  }
}

TransactionStore.prototype.store = function(req, txn, cb) {
  console.log('@@@@@ OAuth 2.0 store state');
  console.log(req.state);
  console.log(txn)
  console.log('@@@@@-----');

  var self = this;
  this._serializeClientFunc(txn.client, function(err, client) {
    if (err) { return cb(err); }

    /*
    req.state.client = client;
    req.state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { req.state.webOrigin = txn.webOrigin; }
    req.state.req = txn.req;
    return cb();
    */

    req.state.client = client;
    req.state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { req.state.webOrigin = txn.webOrigin; }
    req.state.req = txn.req;

    req.state.save(function(err) {
      console.log('STATE SAVED!');
      console.log(err);

      return cb(err);
    });

    /*
    var state = {};
    state.name = 'oauth2-authorize';
    state.client = client;
    state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { state.webOrigin = txn.webOrigin; }
    state.req = txn.req;

    self._store.save(req, state, function(err, h) {
      if (err) { return cb(err); }
      cb(null, h);
    });
    */
  });
}

TransactionStore.prototype.update = function(req, h, txn, cb) {
  var self = this;
  this._serializeClientFunc(txn.client, function(err, client) {
    if (err) { return cb(err); }

    var state = {};
    state.name = 'oauth2-authorize';
    state.initiatedAt = txn.initiatedAt;
    state.client = client;
    state.redirectURI = txn.redirectURI;
    if (txn.webOrigin) { state.webOrigin = txn.webOrigin; }
    state.req = txn.req;
    if (txn.locals) {
      if (txn.locals.authN) {
        state.authN = txn.locals.authN;
      }
      if (txn.locals.deviceCode) {
        state.deviceCode = txn.locals.deviceCode;
      }
    }

    self._store.update(req, h, state, function(err, h) {
      if (err) { return cb(err); }
      cb(null, h);
    });
  });
}

TransactionStore.prototype.remove = function(req, h, cb) {
  this._store.destroy(req, h, cb);
}

module.exports = TransactionStore;
<|endoftext|>


<commit_before>/*global namespace*/
(function () {
    var shim = namespace.shim;
    var forEach = shim.forEach;
    var getKeys = shim.getKeys;

    var utils = {
        // https://gist.github.com/1044128/
        getOuterHTML: function (element) {
            // jshint browser:true
            if ('outerHTML' in element) return element.outerHTML;
            var ns = "http://www.w3.org/1999/xhtml";
            var container = document.createElementNS(ns, '_');
            var xmlSerializer = new XMLSerializer();
            var html;
            if (document.xmlVersion) {
                return xmlSerializer.serializeToString(element);
            } else {
                container.appendChild(element.cloneNode(false));
                html = container.innerHTML.replace('><', '>' + element.innerHTML + '<');
                container.innerHTML = '';
                return html;
            }
        },

        // Returns true if object is a DOM element.
        isDOMElement: function (object) {
            if (typeof HTMLElement === 'object') {
                return object instanceof HTMLElement;
            } else {
                return object &&
                    typeof object === 'object' &&
                    object.nodeType === 1 &&
                    typeof object.nodeName === 'string';
            }
        },

        isArray: function (ar) {
            return Object.prototype.toString.call(ar) === '[object Array]';
        },

        isRegExp: function (re) {
            var s;
            try {
                s = '' + re;
            } catch (e) {
                return false;
            }

            return re instanceof RegExp || // easy case
            // duck-type for context-switching evalcx case
            typeof(re) === 'function' &&
                re.constructor.name === 'RegExp' &&
                re.compile &&
                re.test &&
                re.exec &&
                s.match(/^\/.*\/[gim]{0,3}$/);
        },

        isError: function (err) {
            return typeof err === 'object' && Object.prototype.toString.call(err) === '[object Error]';
        },

        isDate: function (d) {
            if (d instanceof Date) return true;
            return false;
        },

        extend: function (target) {
            var sources = Array.prototype.slice.call(arguments, 1);
            forEach(sources, function (source) {
                forEach(getKeys(source), function (key) {
                    target[key] = source[key];
                });
            });
            return target;
        },

        isUndefinedOrNull: function  (value) {
            return value === null || value === undefined;
        },

        isArguments: function  (object) {
            return Object.prototype.toString.call(object) === '[object Arguments]';
        },

        /**
         * Levenshtein distance algorithm from wikipedia
         * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
         */
        levenshteinDistance: function (a, b) {
            if (a.length === 0) return b.length;
            if (b.length === 0) return a.length;

            var matrix = [];

            // increment along the first column of each row
            var i;
            for (i = 0; i <= b.length; i += 1) {
                matrix[i] = [i];
            }

            // increment each column in the first row
            var j;
            for (j = 0; j <= a.length; j += 1) {
                matrix[0][j] = j;
            }

            // Fill in the rest of the matrix
            for (i = 1; i <= b.length; i += 1) {
                for (j = 1; j <= a.length; j += 1) {
                    if (b.charAt(i - 1) === a.charAt(j - 1)) {
                        matrix[i][j] = matrix[i - 1][j - 1];
                    } else {
                        matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
                                                Math.min(matrix[i][j - 1] + 1, // insertion
                                                         matrix[i - 1][j] + 1)); // deletion
                    }
                }
            }

            return matrix[b.length][a.length];
        },

        truncateStack: function (err, fn) {
            if (Error.captureStackTrace) {
                Error.captureStackTrace(err, fn);
            } else if ('stack' in err) {
                // Excludes IE<10, and fn cannot be anonymous for this backup plan to work:
                var stackEntries = err.stack.split(/\r\n?|\n\r?/),
                needle = 'at ' + fn.name + ' ';
                for (var i = 0 ; i < stackEntries.length ; i += 1) {
                    if (stackEntries[i].indexOf(needle) !== -1) {
                        stackEntries.splice(1, i);
                        err.stack = stackEntries.join("\n");
                    }
                }
            }
        },

        findFirst: function (arr, predicate, thisObj) {
            var scope = thisObj || null;
            for (var i = 0 ; i < arr.length ; i += 1) {
                if (predicate.call(scope, arr[i], i, arr)) {
                    return arr[i];
                }
            }
            return null;
        },

        leftPad: function (str, width, ch) {
            ch = ch || ' ';
            while (str.length < width) {
                str = ch + str;
            }
            return str;
        }
    };

    namespace.utils = utils;
}());
<commit_msg>utils: Whitespace fixes.<commit_after>/*global namespace*/
(function () {
    var shim = namespace.shim;
    var forEach = shim.forEach;
    var getKeys = shim.getKeys;

    var utils = {
        // https://gist.github.com/1044128/
        getOuterHTML: function (element) {
            // jshint browser:true
            if ('outerHTML' in element) return element.outerHTML;
            var ns = "http://www.w3.org/1999/xhtml";
            var container = document.createElementNS(ns, '_');
            var xmlSerializer = new XMLSerializer();
            var html;
            if (document.xmlVersion) {
                return xmlSerializer.serializeToString(element);
            } else {
                container.appendChild(element.cloneNode(false));
                html = container.innerHTML.replace('><', '>' + element.innerHTML + '<');
                container.innerHTML = '';
                return html;
            }
        },

        // Returns true if object is a DOM element.
        isDOMElement: function (object) {
            if (typeof HTMLElement === 'object') {
                return object instanceof HTMLElement;
            } else {
                return object &&
                    typeof object === 'object' &&
                    object.nodeType === 1 &&
                    typeof object.nodeName === 'string';
            }
        },

        isArray: function (ar) {
            return Object.prototype.toString.call(ar) === '[object Array]';
        },

        isRegExp: function (re) {
            var s;
            try {
                s = '' + re;
            } catch (e) {
                return false;
            }

            return re instanceof RegExp || // easy case
            // duck-type for context-switching evalcx case
            typeof(re) === 'function' &&
                re.constructor.name === 'RegExp' &&
                re.compile &&
                re.test &&
                re.exec &&
                s.match(/^\/.*\/[gim]{0,3}$/);
        },

        isError: function (err) {
            return typeof err === 'object' && Object.prototype.toString.call(err) === '[object Error]';
        },

        isDate: function (d) {
            if (d instanceof Date) return true;
            return false;
        },

        extend: function (target) {
            var sources = Array.prototype.slice.call(arguments, 1);
            forEach(sources, function (source) {
                forEach(getKeys(source), function (key) {
                    target[key] = source[key];
                });
            });
            return target;
        },

        isUndefinedOrNull: function (value) {
            return value === null || value === undefined;
        },

        isArguments: function (object) {
            return Object.prototype.toString.call(object) === '[object Arguments]';
        },

        /**
         * Levenshtein distance algorithm from wikipedia
         * http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
         */
        levenshteinDistance: function (a, b) {
            if (a.length === 0) return b.length;
            if (b.length === 0) return a.length;

            var matrix = [];

            // increment along the first column of each row
            var i;
            for (i = 0; i <= b.length; i += 1) {
                matrix[i] = [i];
            }

            // increment each column in the first row
            var j;
            for (j = 0; j <= a.length; j += 1) {
                matrix[0][j] = j;
            }

            // Fill in the rest of the matrix
            for (i = 1; i <= b.length; i += 1) {
                for (j = 1; j <= a.length; j += 1) {
                    if (b.charAt(i - 1) === a.charAt(j - 1)) {
                        matrix[i][j] = matrix[i - 1][j - 1];
                    } else {
                        matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
                                                Math.min(matrix[i][j - 1] + 1, // insertion
                                                         matrix[i - 1][j] + 1)); // deletion
                    }
                }
            }

            return matrix[b.length][a.length];
        },

        truncateStack: function (err, fn) {
            if (Error.captureStackTrace) {
                Error.captureStackTrace(err, fn);
            } else if ('stack' in err) {
                // Excludes IE<10, and fn cannot be anonymous for this backup plan to work:
                var stackEntries = err.stack.split(/\r\n?|\n\r?/),
                needle = 'at ' + fn.name + ' ';
                for (var i = 0 ; i < stackEntries.length ; i += 1) {
                    if (stackEntries[i].indexOf(needle) !== -1) {
                        stackEntries.splice(1, i);
                        err.stack = stackEntries.join("\n");
                    }
                }
            }
        },

        findFirst: function (arr, predicate, thisObj) {
            var scope = thisObj || null;
            for (var i = 0 ; i < arr.length ; i += 1) {
                if (predicate.call(scope, arr[i], i, arr)) {
                    return arr[i];
                }
            }
            return null;
        },

        leftPad: function (str, width, ch) {
            ch = ch || ' ';
            while (str.length < width) {
                str = ch + str;
            }
            return str;
        }
    };

    namespace.utils = utils;
}());
<|endoftext|>


<commit_before>/* eslint-disable */
var Modus = (function() {
    //Variables
    let _os = _getParameterByName("os");

    //Helpers
    function _getParameterByName(name, url) {
        if (!url) url = window.location.href;
        name = name.replace(/[\[\]]/g, '\\$&');
        var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }

    //Break this in to a different file
    let _createExampleResult = function (request) {
        //TODO: should this be somewhere else?
        var name = request.methodName;
        var result = null;

        switch (name) {
            case "getCurrentUserName":
                result = "Obi Won Kenobi"
                break;
            case "getCurrentUserEmail":
                result = "maul_killer@jedicouncil.crst"
                break;
            case "getAccessToken":
                result = "exampleToken"
                break;
            case "getCurrentUserRegions":
                result = ["Tatooine", "Stewjon", "Coruscant"]
                break;

            //Storage
            case "getItem":
            case "getGlobalItem":
                result = localStorage.getItem(request.data.key);
                break;
            case "setItem":
            case "setGlobalItem":
                localStorage.setItem(request.data.key, request.data.value);
                break;

            //Email
            case "sendAgenda":
            case "sendEmail":
            case "sendEmailHtml":
                var e = request.data;
                var body = e.body ? e.body : e.html ? e.html : "";
                var to = e.to ? e.to : e.emailAddress;
                var mailto = "mailto:" + to + "?subject=" + e.subject + "&body=" + body + "&cc=" + e.cc;
                window.open(mailto);
                break;
            //Agendas
            case "getAgendas":
                result = JSON.stringify([{agendaId: "1", agendaTitle: "Bespin Meeting"}, {agendaId: "2", agendaTitle: "Endor Visit"}, {agendaId: "3", agendaTitle: "Hoth Beach Vacation"}])
                break;
        }

        window[request.successMethodId](result);
    }

    //Marshall
    let _callNativeFunction = function (methodName, methodData) {
        var id = Math.floor(Math.random() * 10000000);
        var successId = methodName + "_success_" + id;
        var errorId = methodName + "_error_" + id;

        return new Promise(function (resolve, reject) {
            //build success function
            window[successId] = function (data) {
                resolve(data);
                window[successId] = null;
                window[errorId] = null;
                delete window[successId];
                delete window[errorId];
            };

            //build error function
            window[errorId] = function (data) {
                reject(data);
                window[successId] = null;
                window[errorId] = null;
                delete window[successId];
                delete window[errorId];
            };

            //create request
            let request = {
                methodName: methodName,
                data: methodData,
                successMethodId: successId,
                errorMethodId: errorId
            };

            let os = _getParameterByName("os");

            //For Windows builds that don't pass in the os param
            let userAgent = navigator.userAgent;

            //  Windows
            if (os === "windows" || userAgent.includes("Windows.Desktop")) {
                return window.external.notify(JSON.stringify(request));
            }

            //  iOS
            if (os === "ios" || (window.webkit && window.webkit.messageHandlers.modus != undefined)) {
                return window.webkit.messageHandlers.modus.postMessage(request);
            }
            //  Android
            if (os === "android" || window.appInterface != undefined) {
                return window.appInterface.postMessage(JSON.stringify(request));
            }

            //Defaults and Stubs
            return _createExampleResult(request);
        });
    }

    //Public
    return {
         //User
        getCurrentUserName: _callNativeFunction.bind(null, "getCurrentUserName", null),
        getCurrentUserEmail: _callNativeFunction.bind(null, "getCurrentUserEmail", null),
        getCurrentUserRegions: _callNativeFunction.bind(null, "getCurrentUserRegions", null),
        getAccessToken: _callNativeFunction.bind(null, "getAccessToken", null),

        //Storage
        getItem: function (key) { return _callNativeFunction("getItem", { key: key }) },
        setItem: function (key, value) { return _callNativeFunction("setItem", { key: key, value: value }) },
        getGlobalItem: function (key) { return _callNativeFunction("getGlobalItem", { key: key }) },
        setGlobalItem: function (key, value) { return _callNativeFunction("setGlobalItem", { key: key, value: value }) },

        //Emails
        sendEmail: function (to, cc, subject, body) { return _callNativeFunction("sendEmail", { to: to, cc: cc, subject: subject, body: body }) },
        sendEmailHtml: function (to, cc, subject, html) { return _callNativeFunction("sendEmailHtml", { to: to, cc: cc, subject: subject, html: html }) },
        sendEmailWithFileAttachmentFromBase64: function (data) { return _callNativeFunction("sendEmailWithFileAttachmentFromBase64", { data: data }) },

        //Agendas
        getAgendas: _callNativeFunction.bind(null, "getAgendas", null),
        sendAgenda: function (agendaId, emailAddress) { return _callNativeFunction("sendAgenda", { agendaId: agendaId, emailAddress: emailAddress }) },

        //Other
        scanBarcode: _callNativeFunction.bind(null, "scanPDF417Barcode", null),
        asyncHttpRequest: function (url, verb, headers, body) { return _callNativeFunction("asyncHttpRequest", { url: url, verb: verb, headers: headers, body: body }) },
        sendFollowup: function (step, bundleName, link) { return _callNativeFunction("sendFollowup", {step: step, bundle: bundleName, link : link }) },
        previewNextFollowupLink: _callNativeFunction.bind(null, "previewNextFollowupLink", null),
        getFollowupGuid: function (followupLink) { return _callNativeFunction("getFollowupGuid", { link: followupLink }) },
        promptShareMenuWithData: function (fileName, base64) { return _callNativeFunction("promptShareMenuWithData", { nane: fileName, fileAsBase64: base64 }) },
        getMediaWithPicker: function (currentMedias) { return _callNativeFunction("getMediaWithPicker", {medias: currentMedias }) },
        getDeviceFilePicker: function (uploadParams) { return _callNativeFunction("getDeviceFilePicker", {uploadParams: uploadParams}) },
    }
})();

export default Modus;<commit_msg>updated name to excludeMedias<commit_after>/* eslint-disable */
var Modus = (function() {
    //Variables
    let _os = _getParameterByName("os");

    //Helpers
    function _getParameterByName(name, url) {
        if (!url) url = window.location.href;
        name = name.replace(/[\[\]]/g, '\\$&');
        var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }

    //Break this in to a different file
    let _createExampleResult = function (request) {
        //TODO: should this be somewhere else?
        var name = request.methodName;
        var result = null;

        switch (name) {
            case "getCurrentUserName":
                result = "Obi Won Kenobi"
                break;
            case "getCurrentUserEmail":
                result = "maul_killer@jedicouncil.crst"
                break;
            case "getAccessToken":
                result = "exampleToken"
                break;
            case "getCurrentUserRegions":
                result = ["Tatooine", "Stewjon", "Coruscant"]
                break;

            //Storage
            case "getItem":
            case "getGlobalItem":
                result = localStorage.getItem(request.data.key);
                break;
            case "setItem":
            case "setGlobalItem":
                localStorage.setItem(request.data.key, request.data.value);
                break;

            //Email
            case "sendAgenda":
            case "sendEmail":
            case "sendEmailHtml":
                var e = request.data;
                var body = e.body ? e.body : e.html ? e.html : "";
                var to = e.to ? e.to : e.emailAddress;
                var mailto = "mailto:" + to + "?subject=" + e.subject + "&body=" + body + "&cc=" + e.cc;
                window.open(mailto);
                break;
            //Agendas
            case "getAgendas":
                result = JSON.stringify([{agendaId: "1", agendaTitle: "Bespin Meeting"}, {agendaId: "2", agendaTitle: "Endor Visit"}, {agendaId: "3", agendaTitle: "Hoth Beach Vacation"}])
                break;
        }

        window[request.successMethodId](result);
    }

    //Marshall
    let _callNativeFunction = function (methodName, methodData) {
        var id = Math.floor(Math.random() * 10000000);
        var successId = methodName + "_success_" + id;
        var errorId = methodName + "_error_" + id;

        return new Promise(function (resolve, reject) {
            //build success function
            window[successId] = function (data) {
                resolve(data);
                window[successId] = null;
                window[errorId] = null;
                delete window[successId];
                delete window[errorId];
            };

            //build error function
            window[errorId] = function (data) {
                reject(data);
                window[successId] = null;
                window[errorId] = null;
                delete window[successId];
                delete window[errorId];
            };

            //create request
            let request = {
                methodName: methodName,
                data: methodData,
                successMethodId: successId,
                errorMethodId: errorId
            };

            let os = _getParameterByName("os");

            //For Windows builds that don't pass in the os param
            let userAgent = navigator.userAgent;

            //  Windows
            if (os === "windows" || userAgent.includes("Windows.Desktop")) {
                return window.external.notify(JSON.stringify(request));
            }

            //  iOS
            if (os === "ios" || (window.webkit && window.webkit.messageHandlers.modus != undefined)) {
                return window.webkit.messageHandlers.modus.postMessage(request);
            }
            //  Android
            if (os === "android" || window.appInterface != undefined) {
                return window.appInterface.postMessage(JSON.stringify(request));
            }

            //Defaults and Stubs
            return _createExampleResult(request);
        });
    }

    //Public
    return {
         //User
        getCurrentUserName: _callNativeFunction.bind(null, "getCurrentUserName", null),
        getCurrentUserEmail: _callNativeFunction.bind(null, "getCurrentUserEmail", null),
        getCurrentUserRegions: _callNativeFunction.bind(null, "getCurrentUserRegions", null),
        getAccessToken: _callNativeFunction.bind(null, "getAccessToken", null),

        //Storage
        getItem: function (key) { return _callNativeFunction("getItem", { key: key }) },
        setItem: function (key, value) { return _callNativeFunction("setItem", { key: key, value: value }) },
        getGlobalItem: function (key) { return _callNativeFunction("getGlobalItem", { key: key }) },
        setGlobalItem: function (key, value) { return _callNativeFunction("setGlobalItem", { key: key, value: value }) },

        //Emails
        sendEmail: function (to, cc, subject, body) { return _callNativeFunction("sendEmail", { to: to, cc: cc, subject: subject, body: body }) },
        sendEmailHtml: function (to, cc, subject, html) { return _callNativeFunction("sendEmailHtml", { to: to, cc: cc, subject: subject, html: html }) },
        sendEmailWithFileAttachmentFromBase64: function (data) { return _callNativeFunction("sendEmailWithFileAttachmentFromBase64", { data: data }) },

        //Agendas
        getAgendas: _callNativeFunction.bind(null, "getAgendas", null),
        sendAgenda: function (agendaId, emailAddress) { return _callNativeFunction("sendAgenda", { agendaId: agendaId, emailAddress: emailAddress }) },

        //Other
        scanBarcode: _callNativeFunction.bind(null, "scanPDF417Barcode", null),
        asyncHttpRequest: function (url, verb, headers, body) { return _callNativeFunction("asyncHttpRequest", { url: url, verb: verb, headers: headers, body: body }) },
        sendFollowup: function (step, bundleName, link) { return _callNativeFunction("sendFollowup", {step: step, bundle: bundleName, link : link }) },
        previewNextFollowupLink: _callNativeFunction.bind(null, "previewNextFollowupLink", null),
        getFollowupGuid: function (followupLink) { return _callNativeFunction("getFollowupGuid", { link: followupLink }) },
        promptShareMenuWithData: function (fileName, base64) { return _callNativeFunction("promptShareMenuWithData", { nane: fileName, fileAsBase64: base64 }) },
        getMediaWithPicker: function (excludeMedias) { return _callNativeFunction("getMediaWithPicker", {excludeMedias: excludeMedias }) },
        getDeviceFilePicker: function (uploadParams) { return _callNativeFunction("getDeviceFilePicker", {uploadParams: uploadParams}) },
    }
})();

export default Modus;<|endoftext|>