<commit_before>var nqp = require('nqp-runtime');
var Null = nqp.Null;
var CodeRef = require('nqp-runtime/code-ref');
var op = {};

var Scalar, True, False, Int, Num, Str, Code;

op.p6settypes = function(types) {
  Scalar = types.content.get('Scalar');
  True = types.content.get('True');
  False = types.content.get('False');
  Int = types.content.get('Int');
  Num = types.content.get('Num');
  Str = types.content.get('Str');
  Code = types.content.get('Code');
  Mu = types.content.get('Mu');
  return types;
};

op.p6bool = function(value) {
  return value ? True : False;
};

op.p6definite = function(obj) {
  return obj.typeObject_ ? False : True;
};

op.p6typecheckrv = function(rv, routine, bypassType) {
  // STUB
  return rv;
};

op.p6decontrv = function(rountine, cont) {
  // STUB
  return cont;
};

op.p6box_i = function(int) {
  var repr = Int._STable.REPR;
  var boxed = repr.allocate(Int._STable);
  boxed.$$setInt(int);
  return boxed;
};

op.p6box_n = function(num) {
  var repr = Num._STable.REPR;
  var boxed = repr.allocate(Num._STable);
  boxed.$$setNum(num);
  return boxed;
};

op.p6box_s = function(str) {
  var repr = Str._STable.REPR;
  var boxed = repr.allocate(Str._STable);
  boxed.$$setStr(str);
  return boxed;
};

op.p6captureouters2 = function(ctx, capList, target) {
  var cf = (target.outerCtx || closure.forcedOuter);

  if (cf === Null) {
    return capList;
  }

  var elems = capList.$$elems();

  for (var i = 0; i < elems; i++) {
      var codeObj = capList.$$atpos(i);

      var closure = codeObj.$$getattr(Code, "$!do");

      var ctxToDiddle = (closure.outerCtx || closure.forcedOuter);
      ctxToDiddle.$$outer = cf;
  }

  return capList;
};

op.p6captureouters = function(ctx, capList) {

  var elems = capList.$$elems();

  for (var i = 0; i < elems; i++) {
      var codeObj = capList.$$atpos(i);
      var closure = codeObj.$$getattr(Code, "$!do");
      var ctxToDiddle = (closure.outerCtx || closure.forcedOuter);
      ctxToDiddle.$$outer = ctx;
  }

  return capList;
};

op.p6capturelex = function(ctx, codeObj) {

  var closure = codeObj.$$getattr(Code, "$!do");
  var wantedStaticInfo = closure.staticCode.outerCodeRef;

  if (ctx.codeRef().staticCode === wantedStaticInfo) {
    closure.forcedOuter = ctx;
  } else if (ctx.$$outer.codeRef().staticCode === wantedStaticInfo) {
    closure.forcedOuter = ctx.$$outer;
  }

  return codeObj;
};

op.p6var = function(cont) {
  if (cont.$$iscont && cont.$$iscont()) {
    var wrapper = Scalar._STable.REPR.allocate(Scalar._STable);
    wrapper.$$bindattr(Scalar, '$!value', cont);
    return wrapper;
  } else {
    return cont;
  }
}

op.p6bindassert = function(ctx, value, type) {
  if (type !== Mu) {
    if (nqp.op.istype(ctx, value.$$decont(ctx), type) == 0) {
      ctx.die("Type check failed in binding");
    }
  }
  return value;
};

op.p6store = function(ctx, cont, value) {
  if (cont) {
    cont.$$assign(ctx, value.$$decont(ctx));
  } else {
    if (!cont.STORE) {
      // TODO throw typed exception X::Assignment::RO
      ctx.die("Cannot assign to a non-container");
    } else {
      cont.STORE(ctx, null, cont, value);
    }
  }
  return cont;
};

op.p6argvmarray = function(ctx, args) {
  var array = [];
  for (var i=2; i < args.length; i++) {
    array[i-2] = nqp.op.hllizefor(ctx, args[i], 'perl6');
  }
  return nqp.createArray(array);
};

var containerSpecs = require('nqp-runtime/container-specs.js');

function RakudoScalar(STable) {
  this.STable = STable;
}

RakudoScalar.prototype.configure = function(conf) {
  this.setupSTable();
};

RakudoScalar.prototype.setupSTable = function() {
  this.STable.addInternalMethod('$$assignunchecked', function(ctx, value) {
    console.log('storing into rakudo_scalar unchecked');
    return this.$$bindattr(Scalar, '$!value', value);
  });

  this.STable.addInternalMethod('$$assign', function(ctx, value) {
    /* TODO - checking and WHENCE */
    return this.$$bindattr(Scalar, '$!value', value);
  });

  this.STable.addInternalMethod('$$decont', function(ctx) {
    return this.$$getattr(Scalar, '$!value');
  });

  this.STable.addInternalMethod('$$getInt', function(ctx) {
    return this.$$decont().$$getInt();
  });

  this.STable.addInternalMethod('$$getNum', function(ctx) {
    return this.$$decont().$$getNum();
  });

  this.STable.addInternalMethod('$$getStr', function(ctx) {
    return this.$$decont().$$getStr();
  });

  this.STable.addInternalMethod('$$iscont', function() {
    return 1;
  });
};

RakudoScalar.prototype.serialize = function(cursor) {
  console.log('// serializing rakudo_scalar');
};

RakudoScalar.prototype.deserialize = function(cursor) {
  console.log('// deserializing rakudo_scalar');
};

RakudoScalar.prototype.name = 'rakudo_scalar';

containerSpecs.rakudo_scalar = RakudoScalar;

nqp.loadOps({op: op});
module.exports = null;

<commit_msg>[js] Implement nqp::p6scalarfromdesc.<commit_after>var nqp = require('nqp-runtime');
var Null = nqp.Null;
var CodeRef = require('nqp-runtime/code-ref');
var op = {};

var Scalar, True, False, Int, Num, Str, Code, Mu, Any, ContainerDescriptor;

var defaultContainerDescriptor;

op.p6settypes = function(types) {
  Scalar = types.content.get('Scalar');
  True = types.content.get('True');
  False = types.content.get('False');
  Int = types.content.get('Int');
  Num = types.content.get('Num');
  Str = types.content.get('Str');
  Code = types.content.get('Code');
  Mu = types.content.get('Mu');
  Any = types.content.get('Any');
  ContainerDescriptor = types.content.get('ContainerDescriptor');

  defaultContainerDescriptor = ContainerDescriptor._STable.REPR.allocate(ContainerDescriptor._STable);

  defaultContainerDescriptor.$$bindattr(ContainerDescriptor, '$!of', Mu);

  defaultContainerDescriptor.$$bindattr_s(ContainerDescriptor, '$!name', "<element>");
  defaultContainerDescriptor.$$bindattr_i(ContainerDescriptor, '$!rw', 1);
  defaultContainerDescriptor.$$bindattr(ContainerDescriptor, '$!default', Any);

  return types;
};

op.p6bool = function(value) {
  return value ? True : False;
};

op.p6definite = function(obj) {
  return obj.typeObject_ ? False : True;
};

op.p6typecheckrv = function(rv, routine, bypassType) {
  // STUB
  return rv;
};

op.p6decontrv = function(rountine, cont) {
  // STUB
  return cont;
};

op.p6box_i = function(int) {
  var repr = Int._STable.REPR;
  var boxed = repr.allocate(Int._STable);
  boxed.$$setInt(int);
  return boxed;
};

op.p6box_n = function(num) {
  var repr = Num._STable.REPR;
  var boxed = repr.allocate(Num._STable);
  boxed.$$setNum(num);
  return boxed;
};

op.p6box_s = function(str) {
  var repr = Str._STable.REPR;
  var boxed = repr.allocate(Str._STable);
  boxed.$$setStr(str);
  return boxed;
};

op.p6captureouters2 = function(ctx, capList, target) {
  var cf = (target.outerCtx || closure.forcedOuter);

  if (cf === Null) {
    return capList;
  }

  var elems = capList.$$elems();

  for (var i = 0; i < elems; i++) {
      var codeObj = capList.$$atpos(i);

      var closure = codeObj.$$getattr(Code, "$!do");

      var ctxToDiddle = (closure.outerCtx || closure.forcedOuter);
      ctxToDiddle.$$outer = cf;
  }

  return capList;
};

op.p6captureouters = function(ctx, capList) {

  var elems = capList.$$elems();

  for (var i = 0; i < elems; i++) {
      var codeObj = capList.$$atpos(i);
      var closure = codeObj.$$getattr(Code, "$!do");
      var ctxToDiddle = (closure.outerCtx || closure.forcedOuter);
      ctxToDiddle.$$outer = ctx;
  }

  return capList;
};

op.p6capturelex = function(ctx, codeObj) {

  var closure = codeObj.$$getattr(Code, "$!do");
  var wantedStaticInfo = closure.staticCode.outerCodeRef;

  if (ctx.codeRef().staticCode === wantedStaticInfo) {
    closure.forcedOuter = ctx;
  } else if (ctx.$$outer.codeRef().staticCode === wantedStaticInfo) {
    closure.forcedOuter = ctx.$$outer;
  }

  return codeObj;
};

op.p6var = function(cont) {
  if (cont.$$iscont && cont.$$iscont()) {
    var wrapper = Scalar._STable.REPR.allocate(Scalar._STable);
    wrapper.$$bindattr(Scalar, '$!value', cont);
    return wrapper;
  } else {
    return cont;
  }
}

op.p6bindassert = function(ctx, value, type) {
  if (type !== Mu) {
    if (nqp.op.istype(ctx, value.$$decont(ctx), type) == 0) {
      ctx.die("Type check failed in binding");
    }
  }
  return value;
};

op.p6store = function(ctx, cont, value) {
  if (cont) {
    cont.$$assign(ctx, value.$$decont(ctx));
  } else {
    if (!cont.STORE) {
      // TODO throw typed exception X::Assignment::RO
      ctx.die("Cannot assign to a non-container");
    } else {
      cont.STORE(ctx, null, cont, value);
    }
  }
  return cont;
};

op.p6argvmarray = function(ctx, args) {
  var array = [];
  for (var i=2; i < args.length; i++) {
    array[i-2] = nqp.op.hllizefor(ctx, args[i], 'perl6');
  }
  return nqp.createArray(array);
};

op.p6scalarfromdesc = function(desc) {
  console.log("p6scalarfromdesc");

  if (desc === Null || desc.typeObject_)
      desc = defaultContainerDescriptor;

  let defVal = desc.$$getattr(ContainerDescriptor, '$!default');

  let cont = Scalar._STable.REPR.allocate(Scalar._STable);

  cont.$$bindattr(Scalar, '$!descriptor', desc);

  cont.$$bindattr(Scalar, '$!value', defVal);

  return cont;

};

var containerSpecs = require('nqp-runtime/container-specs.js');

function RakudoScalar(STable) {
  this.STable = STable;
}

RakudoScalar.prototype.configure = function(conf) {
  this.setupSTable();
};

RakudoScalar.prototype.setupSTable = function() {
  this.STable.addInternalMethod('$$assignunchecked', function(ctx, value) {
    console.log('storing into rakudo_scalar unchecked');
    return this.$$bindattr(Scalar, '$!value', value);
  });

  this.STable.addInternalMethod('$$assign', function(ctx, value) {
    /* TODO - checking and WHENCE */
    return this.$$bindattr(Scalar, '$!value', value);
  });

  this.STable.addInternalMethod('$$decont', function(ctx) {
    return this.$$getattr(Scalar, '$!value');
  });

  this.STable.addInternalMethod('$$getInt', function(ctx) {
    return this.$$decont().$$getInt();
  });

  this.STable.addInternalMethod('$$getNum', function(ctx) {
    return this.$$decont().$$getNum();
  });

  this.STable.addInternalMethod('$$getStr', function(ctx) {
    return this.$$decont().$$getStr();
  });

  this.STable.addInternalMethod('$$iscont', function() {
    return 1;
  });
};

RakudoScalar.prototype.serialize = function(cursor) {
  console.log('// serializing rakudo_scalar');
};

RakudoScalar.prototype.deserialize = function(cursor) {
  console.log('// deserializing rakudo_scalar');
};

RakudoScalar.prototype.name = 'rakudo_scalar';

containerSpecs.rakudo_scalar = RakudoScalar;

nqp.loadOps({op: op});
module.exports = null;

<|endoftext|>


<commit_before>{% set config_web = config.web|json %}

// Make console.log not crash JS browsers that don't support it
if (!window.console) window.console = {
  log: $.noop,
  group: $.noop,
  groupEnd: $.noop,
  info: $.noop,
  error: $.noop
};

Mailpile = {
  instance:           {},
  search_target:      'none',
  search_cache:       [],
  messages_cache:     [],
  messages_composing: {},
  crypto_keylookup:   [],
  tags_cache:         [],
  contacts_cache:     [],
  keybindings:        [
    ["normal", "/",      function() { $("#search-query").focus(); return false; }],
    ["normal", "c",      function() { Mailpile.activities.compose(); }],
    ["normal", "g i",    function() { Mailpile.go("/in/inbox/"); }],
    ["normal", "g d",    function() { Mailpile.go("/in/drafts/"); }],
    ["normal", "g c",    function() { Mailpile.go("/contacts/"); }],
    ["normal", "g n c",  function() { Mailpile.go("/contacts/add/"); }],
    ["normal", "g t",    function() { Mailpile.go("/tags/"); }],
    ["normal", "g n t",  function() { Mailpile.go("/tag/add/"); }],
    ["normal", "g s",    function() { Mailpile.go("/settings/profiles/"); }],
    ["normal", "h",      function() { Mailpile.go("/help/"); }],
    ["normal", "right",  function() { if ($('#pile-next').length) { Mailpile.go($('#pile-next').attr('href'));} }],
    ["normal", "left",   function() { if ($('#pile-previous').length) { Mailpile.go($('#pile-previous').attr('href'));} }],
    ["normal", "command+z ctrl+z",  function() { alert('Undo Something ') }],
    ["normal", "space",  function() { Mailpile.bulk_action_toggle_target(); }],
    ["normal", "s a",    function() { Mailpile.bulk_action_select_all(); }],
    ["normal", "s b",    function() { Mailpile.bulk_action_select_between(); }],
    ["normal", "s n",    function() { Mailpile.bulk_action_select_none(); }],
    ["normal", "s i",    function() { Mailpile.bulk_action_select_invert(); }],
    ["normal", "k",      function() { Mailpile.bulk_action_selection_down(); }],
    ["normal", "j",      function() { Mailpile.bulk_action_selection_up(); }],
    ["normal", "enter",  function() { Mailpile.open_selected_thread(); }],
    ["normal", "f",      function() { Mailpile.update_search(); }],
    ["normal", ["m a"],  function() { Mailpile.keybinding_move_message(''); }],
    ["normal", ["m s"],  function() { Mailpile.keybinding_move_message('spam'); }],
    ["normal", ["m d"],  function() { Mailpile.keybinding_move_message('trash'); }],
    ["normal", ["t"],    function() { Mailpile.render_modal_tags(); }],
    ["normal", ["r"],    function() { Mailpile.bulk_action_read(); }],
    ["normal", ["u"],    function() { Mailpile.bulk_action_unread(); }],
    ["normal", ["up"],   function() { Mailpile.keybinding_target('up'); }],
    ["normal", ["down"], function() { Mailpile.keybinding_target('down'); }],
    ["normal", "shift",  function() { Mailpile.keybinding_shift_router(); }],
    ["global", "esc",    function() {
      $('input[type=text]').blur();
      $('textarea').blur();
    }]
  ],
  nagify: 1000 * 60 * 60 * 24 * 7, // Default nag is 1 per week
  commands:      [],
  graphselected: [],
  defaults: {
    view_size: "comfy",
  },
  config: {
    web: {{config_web|safe}}
  },
  api: {
    compose      : "/api/0/message/compose/",
    compose_send : "/api/0/message/update/send/",
    compose_save : "/api/0/message/update/",
    contacts     : "/api/0/search/address/",
    message      : "/api/0/message/=",
    tag          : "/api/0/tag/",
    tag_list     : "/api/0/tags/",
    tag_add      : "/api/0/tags/add/",
    tag_update   : "/api/0/settings/set/",
    search_new   : "/api/0/search/?q=in%3Anew",
    search       : "/api/0/search/",
    settings_add : "/api/0/settings/add/"
  },
  urls: {
    message_draft : "/message/draft/=",
    message_sent  : "/thread/=",
    tags          : "/tags/"
  },
  plugins: [],
  theme: {},
  activities: {}
};

/* **[ Mailpile - JSAPI ]******************************************************

This autogenerates JS methods which fire GET & POST calls to Mailpile
API/command endpoints.

It also name-spaces and wraps any and all plugin javascript code.

**************************************************************************** */

/* **[ Mailpile - Theme Settings ]****************************************** */
{% set theme_settings = theme_settings() %}
Mailpile.theme = {{ theme_settings|json|safe }}

/* **[AJAX Wappers - for the Mailpile API]********************************** */
Mailpile.API = {
  _endpoints: {
{% for command in result.api_methods %}
    {{command.url|replace("/", "_")}}_{{command.method|lower}}: "/0/{{command.url}}/"{% if not loop.last %},{% endif %}

{% endfor %}
  },
  _sync_url: "/api",
  _async_url: "/async",
};

Mailpile.API._ajax_error =  function(base_url, command, data, method, response, status) {
  console.log('Oops, an AJAX call returned as error :(');
  console.log('status: ' + status + ' method: ' + method + ' base_url: ' + base_url + ' command: ' + command);
  console.log(response);

  // Hide Connection Down
  if (command == '/0/eventlog/' && status == 'error' && response.status == 404) {
    console.log('SHOW CONNECTION DOWN!!!');
    //$('body').append($('#template-connection-down').html());
  }
};

Mailpile.API._action = function(base_url, command, data, method, callback) {

  // Output format
  var output = '';
  if (data._output) {
    output = data._output;
    data = _.omit(data, '_output');
  }

  // Default to GET
  if (method !== 'GET' && method !== 'POST') {
    method = 'GET';
  }

  // GET
  if (method === 'GET') {
    for (var k in data) {
      if (!data[k] || data[k] == undefined) {
        delete data[k];
      }
    }

    // Make Querystring
    var params = $.param(data);

    $.ajax({
      url      : base_url + command + output + "?" + params,
      type     : 'GET',
      dataType : 'json',
      success  : callback,
      error: function(response, status) {
        Mailpile.API._ajax_error(base_url, command, data, method, response, status);
      }
    });
  }
  // POST
  else if (method === 'POST') {
    $.ajax({
      url      : base_url + command + output,
      type     : 'POST',
      data     : data,
      dataType : 'json',
      success  : callback,
      error    : function(response, status) {
        Mailpile.API._ajax_error(base_url, command, data, method, response, status);
      }
    });
  }

  return true;
};

Mailpile.API._sync_action = function(command, data, method, callback) {
  return Mailpile.API._action(Mailpile.API._sync_url, command, data, method, callback);
};

Mailpile.API._async_action = function(command, data, method, callback, flags) {

  function handle_event(data) {

    if (data.result.resultid) {

      subreq = {event_id: data.result.resultid, flags: flags};

      var subid = EventLog.subscribe(subreq, function(ev) {

        callback(ev.private_data, ev);

        if (ev.flags == "c") {
          EventLog.unsubscribe(data.result.resultid, subid);
        }
      });
    }
  }

  Mailpile.API._action(Mailpile.API._async_url, command, data, method, handle_event, flags);
};

/* Loop Commands */
{% for command in result.api_methods -%}

/* Create sync API commands */
Mailpile.API.{{command.url|replace("/", "_")}}_{{command.method|lower}} = function(data, callback, method) {
  var methods = ["{{command.method}}"];
  if (!method || methods.indexOf(method) == -1) {
    method = methods[0];
  }
  return Mailpile.API._sync_action(
    Mailpile.API._endpoints.{{command.url|replace("/", "_")}}_{{command.method|lower}},
    data,
    method,
    callback
  );
};

/* Create async API commands */
Mailpile.API.async_{{command.url|replace("/", "_")}}_{{command.method|lower}} = function(data, callback, method) {
  var methods = ["{{command.method}}"];
  if (!method || methods.indexOf(method) == -1) {
    method = methods[0];
  }
  return Mailpile.API._async_action(
    Mailpile.API._endpoints.{{command.url|replace("/", "_")}}_{{command.method|lower}},
    data,
    method,
    callback
  );
};
{% endfor %}

/* Plugin Javascript - we do this in multiple commands instead of one big
   dict, so plugin setup code can reference other plugins. Plugins are
   expected to return a dictionary of values they want to make globally
   accessible.
   FIXME: Make sure the order is somehow sane given dependenies.
*/
{% for js_class in result.javascript_classes %}
{{ js_class.classname.capitalize() }} = {% if js_class.code %}(function(){
{{ js_class.code|safe }}})(); /* EOF:{{ js_class.classname }} */
{% else %}{};
{% endif %}
{% endfor %}

<commit_msg>Used JS delete instead of _.omit<commit_after>{% set config_web = config.web|json %}

// Make console.log not crash JS browsers that don't support it
if (!window.console) window.console = {
  log: $.noop,
  group: $.noop,
  groupEnd: $.noop,
  info: $.noop,
  error: $.noop
};

Mailpile = {
  instance:           {},
  search_target:      'none',
  search_cache:       [],
  messages_cache:     [],
  messages_composing: {},
  crypto_keylookup:   [],
  tags_cache:         [],
  contacts_cache:     [],
  keybindings:        [
    ["normal", "/",      function() { $("#search-query").focus(); return false; }],
    ["normal", "c",      function() { Mailpile.activities.compose(); }],
    ["normal", "g i",    function() { Mailpile.go("/in/inbox/"); }],
    ["normal", "g d",    function() { Mailpile.go("/in/drafts/"); }],
    ["normal", "g c",    function() { Mailpile.go("/contacts/"); }],
    ["normal", "g n c",  function() { Mailpile.go("/contacts/add/"); }],
    ["normal", "g t",    function() { Mailpile.go("/tags/"); }],
    ["normal", "g n t",  function() { Mailpile.go("/tag/add/"); }],
    ["normal", "g s",    function() { Mailpile.go("/settings/profiles/"); }],
    ["normal", "h",      function() { Mailpile.go("/help/"); }],
    ["normal", "right",  function() { if ($('#pile-next').length) { Mailpile.go($('#pile-next').attr('href'));} }],
    ["normal", "left",   function() { if ($('#pile-previous').length) { Mailpile.go($('#pile-previous').attr('href'));} }],
    ["normal", "command+z ctrl+z",  function() { alert('Undo Something ') }],
    ["normal", "space",  function() { Mailpile.bulk_action_toggle_target(); }],
    ["normal", "s a",    function() { Mailpile.bulk_action_select_all(); }],
    ["normal", "s b",    function() { Mailpile.bulk_action_select_between(); }],
    ["normal", "s n",    function() { Mailpile.bulk_action_select_none(); }],
    ["normal", "s i",    function() { Mailpile.bulk_action_select_invert(); }],
    ["normal", "k",      function() { Mailpile.bulk_action_selection_down(); }],
    ["normal", "j",      function() { Mailpile.bulk_action_selection_up(); }],
    ["normal", "enter",  function() { Mailpile.open_selected_thread(); }],
    ["normal", "f",      function() { Mailpile.update_search(); }],
    ["normal", ["m a"],  function() { Mailpile.keybinding_move_message(''); }],
    ["normal", ["m s"],  function() { Mailpile.keybinding_move_message('spam'); }],
    ["normal", ["m d"],  function() { Mailpile.keybinding_move_message('trash'); }],
    ["normal", ["t"],    function() { Mailpile.render_modal_tags(); }],
    ["normal", ["r"],    function() { Mailpile.bulk_action_read(); }],
    ["normal", ["u"],    function() { Mailpile.bulk_action_unread(); }],
    ["normal", ["up"],   function() { Mailpile.keybinding_target('up'); }],
    ["normal", ["down"], function() { Mailpile.keybinding_target('down'); }],
    ["normal", "shift",  function() { Mailpile.keybinding_shift_router(); }],
    ["global", "esc",    function() {
      $('input[type=text]').blur();
      $('textarea').blur();
    }]
  ],
  nagify: 1000 * 60 * 60 * 24 * 7, // Default nag is 1 per week
  commands:      [],
  graphselected: [],
  defaults: {
    view_size: "comfy",
  },
  config: {
    web: {{config_web|safe}}
  },
  api: {
    compose      : "/api/0/message/compose/",
    compose_send : "/api/0/message/update/send/",
    compose_save : "/api/0/message/update/",
    contacts     : "/api/0/search/address/",
    message      : "/api/0/message/=",
    tag          : "/api/0/tag/",
    tag_list     : "/api/0/tags/",
    tag_add      : "/api/0/tags/add/",
    tag_update   : "/api/0/settings/set/",
    search_new   : "/api/0/search/?q=in%3Anew",
    search       : "/api/0/search/",
    settings_add : "/api/0/settings/add/"
  },
  urls: {
    message_draft : "/message/draft/=",
    message_sent  : "/thread/=",
    tags          : "/tags/"
  },
  plugins: [],
  theme: {},
  activities: {}
};

/* **[ Mailpile - JSAPI ]******************************************************

This autogenerates JS methods which fire GET & POST calls to Mailpile
API/command endpoints.

It also name-spaces and wraps any and all plugin javascript code.

**************************************************************************** */

/* **[ Mailpile - Theme Settings ]****************************************** */
{% set theme_settings = theme_settings() %}
Mailpile.theme = {{ theme_settings|json|safe }}

/* **[AJAX Wappers - for the Mailpile API]********************************** */
Mailpile.API = {
  _endpoints: {
{% for command in result.api_methods %}
    {{command.url|replace("/", "_")}}_{{command.method|lower}}: "/0/{{command.url}}/"{% if not loop.last %},{% endif %}

{% endfor %}
  },
  _sync_url: "/api",
  _async_url: "/async",
};

Mailpile.API._ajax_error =  function(base_url, command, data, method, response, status) {
  console.log('Oops, an AJAX call returned as error :(');
  console.log('status: ' + status + ' method: ' + method + ' base_url: ' + base_url + ' command: ' + command);
  console.log(response);

  // Hide Connection Down
  if (command == '/0/eventlog/' && status == 'error' && response.status == 404) {
    console.log('SHOW CONNECTION DOWN!!!');
    //$('body').append($('#template-connection-down').html());
  }
};

Mailpile.API._action = function(base_url, command, data, method, callback) {

  // Output format
  var output = '';
  if (data._output) {
    output = data._output;
    delete data['_output'];
  }

  // Default to GET
  if (method !== 'GET' && method !== 'POST') {
    method = 'GET';
  }

  // GET
  if (method === 'GET') {
    for (var k in data) {
      if (!data[k] || data[k] == undefined) {
        delete data[k];
      }
    }

    // Make Querystring
    var params = $.param(data);

    $.ajax({
      url      : base_url + command + output + "?" + params,
      type     : 'GET',
      dataType : 'json',
      success  : callback,
      error: function(response, status) {
        Mailpile.API._ajax_error(base_url, command, data, method, response, status);
      }
    });
  }
  // POST
  else if (method === 'POST') {
    $.ajax({
      url      : base_url + command + output,
      type     : 'POST',
      data     : data,
      dataType : 'json',
      success  : callback,
      error    : function(response, status) {
        Mailpile.API._ajax_error(base_url, command, data, method, response, status);
      }
    });
  }

  return true;
};

Mailpile.API._sync_action = function(command, data, method, callback) {
  return Mailpile.API._action(Mailpile.API._sync_url, command, data, method, callback);
};

Mailpile.API._async_action = function(command, data, method, callback, flags) {

  function handle_event(data) {

    if (data.result.resultid) {

      subreq = {event_id: data.result.resultid, flags: flags};

      var subid = EventLog.subscribe(subreq, function(ev) {

        callback(ev.private_data, ev);

        if (ev.flags == "c") {
          EventLog.unsubscribe(data.result.resultid, subid);
        }
      });
    }
  }

  Mailpile.API._action(Mailpile.API._async_url, command, data, method, handle_event, flags);
};

/* Loop Commands */
{% for command in result.api_methods -%}

/* Create sync API commands */
Mailpile.API.{{command.url|replace("/", "_")}}_{{command.method|lower}} = function(data, callback, method) {
  var methods = ["{{command.method}}"];
  if (!method || methods.indexOf(method) == -1) {
    method = methods[0];
  }
  return Mailpile.API._sync_action(
    Mailpile.API._endpoints.{{command.url|replace("/", "_")}}_{{command.method|lower}},
    data,
    method,
    callback
  );
};

/* Create async API commands */
Mailpile.API.async_{{command.url|replace("/", "_")}}_{{command.method|lower}} = function(data, callback, method) {
  var methods = ["{{command.method}}"];
  if (!method || methods.indexOf(method) == -1) {
    method = methods[0];
  }
  return Mailpile.API._async_action(
    Mailpile.API._endpoints.{{command.url|replace("/", "_")}}_{{command.method|lower}},
    data,
    method,
    callback
  );
};
{% endfor %}

/* Plugin Javascript - we do this in multiple commands instead of one big
   dict, so plugin setup code can reference other plugins. Plugins are
   expected to return a dictionary of values they want to make globally
   accessible.
   FIXME: Make sure the order is somehow sane given dependenies.
*/
{% for js_class in result.javascript_classes %}
{{ js_class.classname.capitalize() }} = {% if js_class.code %}(function(){
{{ js_class.code|safe }}})(); /* EOF:{{ js_class.classname }} */
{% else %}{};
{% endif %}
{% endfor %}

<|endoftext|>


<commit_before>/*!
 * Author:        Pierre-Henry Soria <hello@ph7cms.com>
 * Copyright:     (c) 2020, Pierre-Henry Soria. All Rights Reserved.
 * License:       GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
 */

const localKeyName = 'agreed18';

class Disclaimer {
    constructor() {
        this.dialogStatus = 0;
    }

    load() {
        if (this.dialogStatus == 0) {
            $('#disclaimer-background').css({
                'opacity': 0.95
            });
            $('#disclaimer-background').fadeIn('slow');
            $('#disclaimer-window').fadeIn('slow');
            this.dialogStatus = 1;
        }
    }

    disable() {
        if (this.dialogStatus == 1) {
            $('#disclaimer-window').fadeOut('slow');
            $('#disclaimer-background').fadeOut('slow');
            this.dialogStatus = 0;
        }
    }

    centerDialog() {
        const windowWidth = document.documentElement.clientWidth;
        const windowHeight = document.documentElement.clientHeight;

        const popupHeight = $('#disclaimer-window').height();
        const popupWidth = $('#disclaimer-window').width();

        $('#disclaimer-window').css({
            "position": "absolute",
            "top": windowHeight / 2 - popupHeight / 2,
            "left": windowWidth / 2 - popupWidth / 2
        });

        $('#disclaimer-background').css({
            "height": windowHeight
        });
    }

    isAccepted() {
        try {
            return localStorage.getItem(localKeyName);
        } catch (e) {
            console.log('Cannot use localStorage', e);
        }

        return null;
    }

    setAccepted() {
        try {
            localStorage.setItem(localKeyName, '1');
        } catch (e) {
            console.log('Cannot use localStorage', e);
        }
    }
}
<commit_msg>[Disclaimer.js] Use sessionStorage instead of localStorage<commit_after>/*!
 * Author:        Pierre-Henry Soria <hello@ph7cms.com>
 * Copyright:     (c) 2020, Pierre-Henry Soria. All Rights Reserved.
 * License:       GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
 */

const localKeyName = 'agreed18';

class Disclaimer {
    constructor() {
        this.dialogStatus = 0;
    }

    load() {
        if (this.dialogStatus == 0) {
            $('#disclaimer-background').css({
                'opacity': 0.95
            });
            $('#disclaimer-background').fadeIn('slow');
            $('#disclaimer-window').fadeIn('slow');
            this.dialogStatus = 1;
        }
    }

    disable() {
        if (this.dialogStatus == 1) {
            $('#disclaimer-window').fadeOut('slow');
            $('#disclaimer-background').fadeOut('slow');
            this.dialogStatus = 0;
        }
    }

    centerDialog() {
        const windowWidth = document.documentElement.clientWidth;
        const windowHeight = document.documentElement.clientHeight;

        const popupHeight = $('#disclaimer-window').height();
        const popupWidth = $('#disclaimer-window').width();

        $('#disclaimer-window').css({
            "position": "absolute",
            "top": windowHeight / 2 - popupHeight / 2,
            "left": windowWidth / 2 - popupWidth / 2
        });

        $('#disclaimer-background').css({
            "height": windowHeight
        });
    }

    isAccepted() {
        try {
            return sessionStorage.getItem(localKeyName);
        } catch (e) {
            console.log('Cannot use sessionStorage', e);
        }

        return null;
    }

    setAccepted() {
        try {
            sessionStorage.setItem(localKeyName, '1');
        } catch (e) {
            console.log('Cannot use sessionStorage', e);
        }
    }
}
<|endoftext|>


<commit_before>GridViewCtrl.$inject = ['$scope', '$element'];
function GridViewCtrl($scope, $element) {

	DSViewCtrl('grid', $scope, $element);

	var ds = $scope._dataSource;
	var page = {};

	$scope.dataView = new Slick.Data.DataView();
	$scope.selection = [];

	ds.on('change', function(e, records, page){
		$scope.setItems(records, page);
	});

	var initialized = false;
	$scope.onShow = function(viewPromise) {

		if (!initialized) {

			viewPromise.then(function(){
				var view = $scope.schema,
					sortBy = view.orderBy;

				if (sortBy) {
					sortBy = sortBy.split('\\.');
				}

				$scope.view = view;
				$scope.filter({
					_sortBy: sortBy
				});
			});

			initialized = true;
		}
	};

	$scope.setItems = function(items, pageInfo) {

		var dataView = $scope.dataView;

		//XXX: clear existing items (bug?)
		if (dataView.getLength()) {
			dataView.beginUpdate();
			dataView.setItems([]);
		    dataView.endUpdate();
		}

		dataView.beginUpdate();
	    dataView.setItems(items);
	    dataView.endUpdate();

		if (pageInfo) {
	    	page = pageInfo;
		}

		if (dataView.adjustSize) {
			dataView.adjustSize();
		}
	};

	$scope.canEdit = function() {
		return $scope.canDelete();
	};

	$scope.canDelete = function() {
		return $scope.selection.length > 0;
	};

	$scope.filter = function(searchFilter) {

		var filter = {}, sortBy,
			domain = null,
			context = null,
			criteria = {
				operator: 'and'
			};

		var fields = _.pluck($scope.fields, 'name');
		for(var name in searchFilter) {
			var value = searchFilter[name];
			if (value !== '') filter[name] = value;
		}

		sortBy = filter._sortBy;
		domain = filter._domain;
		context = filter._context;

		delete filter._sortBy;
		delete filter._domain;
		delete filter._context;

		criteria.criteria = _.map(filter, function(value, key) {

			var field = $scope.fields[key] || {};
			var type = field.type || 'string';
			var operator = 'iContains';

			//TODO: implement expression parser

			if (type === 'many-to-one') {
				if (field.targetName) {
					key = key + '.' + field.targetName;
				} else {
					console.warn("Can't search on field: ", key);
				}
			}

			switch(type) {
				case 'integer':
				case 'long':
				case 'decimal':
					operator = 'iEquals';
					break;
				case 'boolean':
					operator = 'iEquals';
					value = !/f|n|false|no|0/.test(value);
					break;
				case 'date':
					operator = 'iEquals';
					value = moment(value, 'DD/MM/YYYY').format('YYYY-MM-DD'); //TODO: user date format
					break;
				case 'time':
					operator = 'iEquals';
					break;
				case 'datetime':
					operator = 'iEquals';
					value = moment(value, 'DD/MM/YYYY HH:mm').format(); //TODO: user datetime format
					break;
			}

			return {
				operator: operator,
				fieldName: key,
				value: value
			};
		});

		domain = domain || $scope._domain;
		if (domain && $scope.getContext) {
			context = _.extend({}, $scope._context, context, $scope.getContext());
		}

		return ds.search({
			filter: criteria,
			fields: fields,
			sortBy: sortBy,
			domain: domain,
			context: context
		});
	};

	$scope.pagerText = function() {
		if (page && page.from !== undefined) {
			if (page.total == 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.next(fields);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.prev(fields);
	};

	$scope.onNew = function() {
		page.index = -1;
		$scope.switchTo('form', function(viewScope){
			viewScope.$broadcast("on:new");
		});
	};

	$scope.onEdit = function() {
		page.index = $scope.selection[0];
		$scope.switchTo('form');
	};

	$scope.onDelete = function() {

		axelor.dialogs.confirm(_t("Do you really want to delete the selected record(s)?"), function(confirmed){

			if (!confirmed)
				return;

			var selected = _.map($scope.selection, function(index) {
				return $scope.dataView.getItem(index);
			});

			ds.removeAll(selected).success(function(records, page){
				if (records.length == 0 && page.total > 0) {
					$scope.onRefresh();
				}
			});
		});
	};

	$scope.onRefresh = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.search({
			fields: fields
		});
	};

	$scope.onSort = function(event, args) {

		var sortBy = [],
			fields = _.pluck($scope.fields, 'name');

		angular.forEach(args.sortCols, function(column){
			var name = column.sortCol.field;
			var spec = column.sortAsc ? name : '-' + name;
			sortBy.push(spec);
		});

		ds.search({
			sortBy: sortBy,
			fields: fields
		});
	};

	$scope.onSelectionChanged = function(event, args) {
		$scope.selection = args.rows || [];
		$scope.$apply();
	};

	$scope.onItemClick = function(event, args) {

	};

	$scope.onItemDblClick = function(event, args) {
		$scope.onEdit();
		$scope.$apply();
	};

	var _getContext = $scope.getContext;
	$scope.getContext = function() {

		// if nested grid then return parent's context
		if (_getContext) {
			return _getContext();
		}

		var dataView = $scope.dataView,
			selected = _.map($scope.selection, function(index) {
				return dataView.getItem(index);
			});
		return {
			'_ids': _.pluck(selected, "id")
		};
	};
}

angular.module('axelor.ui').directive('uiViewGrid', function(){
	return {
		replace: true,
		template: '

'
	};
});
<commit_msg>Fixed selecting of new item row in editable grid.<commit_after>GridViewCtrl.$inject = ['$scope', '$element'];
function GridViewCtrl($scope, $element) {

	DSViewCtrl('grid', $scope, $element);

	var ds = $scope._dataSource;
	var page = {};

	$scope.dataView = new Slick.Data.DataView();
	$scope.selection = [];

	ds.on('change', function(e, records, page){
		$scope.setItems(records, page);
	});

	var initialized = false;
	$scope.onShow = function(viewPromise) {

		if (!initialized) {

			viewPromise.then(function(){
				var view = $scope.schema,
					sortBy = view.orderBy;

				if (sortBy) {
					sortBy = sortBy.split('\\.');
				}

				$scope.view = view;
				$scope.filter({
					_sortBy: sortBy
				});
			});

			initialized = true;
		}
	};

	$scope.setItems = function(items, pageInfo) {

		var dataView = $scope.dataView;

		//XXX: clear existing items (bug?)
		if (dataView.getLength()) {
			dataView.beginUpdate();
			dataView.setItems([]);
		    dataView.endUpdate();
		}

		dataView.beginUpdate();
	    dataView.setItems(items);
	    dataView.endUpdate();

		if (pageInfo) {
	    	page = pageInfo;
		}

		if (dataView.adjustSize) {
			dataView.adjustSize();
		}
	};

	$scope.canEdit = function() {
		return $scope.canDelete();
	};

	$scope.canDelete = function() {
		return $scope.selection.length > 0;
	};

	$scope.filter = function(searchFilter) {

		var filter = {}, sortBy,
			domain = null,
			context = null,
			criteria = {
				operator: 'and'
			};

		var fields = _.pluck($scope.fields, 'name');
		for(var name in searchFilter) {
			var value = searchFilter[name];
			if (value !== '') filter[name] = value;
		}

		sortBy = filter._sortBy;
		domain = filter._domain;
		context = filter._context;

		delete filter._sortBy;
		delete filter._domain;
		delete filter._context;

		criteria.criteria = _.map(filter, function(value, key) {

			var field = $scope.fields[key] || {};
			var type = field.type || 'string';
			var operator = 'iContains';

			//TODO: implement expression parser

			if (type === 'many-to-one') {
				if (field.targetName) {
					key = key + '.' + field.targetName;
				} else {
					console.warn("Can't search on field: ", key);
				}
			}

			switch(type) {
				case 'integer':
				case 'long':
				case 'decimal':
					operator = 'iEquals';
					break;
				case 'boolean':
					operator = 'iEquals';
					value = !/f|n|false|no|0/.test(value);
					break;
				case 'date':
					operator = 'iEquals';
					value = moment(value, 'DD/MM/YYYY').format('YYYY-MM-DD'); //TODO: user date format
					break;
				case 'time':
					operator = 'iEquals';
					break;
				case 'datetime':
					operator = 'iEquals';
					value = moment(value, 'DD/MM/YYYY HH:mm').format(); //TODO: user datetime format
					break;
			}

			return {
				operator: operator,
				fieldName: key,
				value: value
			};
		});

		domain = domain || $scope._domain;
		if (domain && $scope.getContext) {
			context = _.extend({}, $scope._context, context, $scope.getContext());
		}

		return ds.search({
			filter: criteria,
			fields: fields,
			sortBy: sortBy,
			domain: domain,
			context: context
		});
	};

	$scope.pagerText = function() {
		if (page && page.from !== undefined) {
			if (page.total == 0) return null;
			return _t("{0} to {1} of {2}", page.from + 1, page.to, page.total);
		}
	};

	$scope.onNext = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.next(fields);
	};

	$scope.onPrev = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.prev(fields);
	};

	$scope.onNew = function() {
		page.index = -1;
		$scope.switchTo('form', function(viewScope){
			viewScope.$broadcast("on:new");
		});
	};

	$scope.onEdit = function() {
		page.index = $scope.selection[0];
		$scope.switchTo('form');
	};

	$scope.onDelete = function() {

		axelor.dialogs.confirm(_t("Do you really want to delete the selected record(s)?"), function(confirmed){

			if (!confirmed)
				return;

			var selected = _.map($scope.selection, function(index) {
				return $scope.dataView.getItem(index);
			});

			ds.removeAll(selected).success(function(records, page){
				if (records.length == 0 && page.total > 0) {
					$scope.onRefresh();
				}
			});
		});
	};

	$scope.onRefresh = function() {
		var fields = _.pluck($scope.fields, 'name');
		ds.search({
			fields: fields
		});
	};

	$scope.onSort = function(event, args) {

		var sortBy = [],
			fields = _.pluck($scope.fields, 'name');

		angular.forEach(args.sortCols, function(column){
			var name = column.sortCol.field;
			var spec = column.sortAsc ? name : '-' + name;
			sortBy.push(spec);
		});

		ds.search({
			sortBy: sortBy,
			fields: fields
		});
	};

	$scope.onSelectionChanged = function(event, args) {
		var selection = _.filter(args.rows, function(index) {
			var item = $scope.dataView.getItem(index);
			return item && item.id !== 0;
		});
		$scope.selection = selection;
		$scope.$apply();
	};

	$scope.onItemClick = function(event, args) {

	};

	$scope.onItemDblClick = function(event, args) {
		$scope.onEdit();
		$scope.$apply();
	};

	var _getContext = $scope.getContext;
	$scope.getContext = function() {

		// if nested grid then return parent's context
		if (_getContext) {
			return _getContext();
		}

		var dataView = $scope.dataView,
			selected = _.map($scope.selection, function(index) {
				return dataView.getItem(index);
			});
		return {
			'_ids': _.pluck(selected, "id")
		};
	};
}

angular.module('axelor.ui').directive('uiViewGrid', function(){
	return {
		replace: true,
		template: '

'
	};
});
<|endoftext|>


<commit_before>/**
 * A general object for ticking a set of values up or down
 *
 * Updated with ES6 syntax
 *
 * Each tick is done on a unit (e.g. hour / minute / second)
 * And compared against all the fractions of the unit
 * e.g. seconds are fractions of a minute
 * Short for chained tick I think of it as 'chuck' rather than 'check'
 * Also see: @link https://en.wikipedia.org/wiki/!!!
 *
 * This code was based off https://github.com/kellishaver/stopwatch
 * all that is left of that are the zeroPad and hand functions
 * they've both been modifed as well, but conceptually the same
 */
const chk = () => {
  "use strict";
  // internal state
  // @todo I still get confused by how this all hangs together
  // we use external state from the state of the counters in the HTML elements
  let _checkSum = null;
  const _upOverflow = () => 0;
  // @todo the down overflow for the primary number should be 'limit'
  // for example t-minus goes from 10...1, but possibly it should also hit zero,
  // the counter should be left at zero after the lift off
  // but for tenths of a second counting down it goes 9...0
  // so the action happens on the transition from 1 -> 0 instead of 0 -> 9
  // for a regular clock though the tick happens from 00 -> 59
  const _downOverflow = (limit) => (limit <= 0 ? 0 : limit - 1);
  const _countUp = (unit, check) => (check ? unit + 1 : unit);
  const _countDown = (unit, check) => (check ? unit - 1 : unit);
  const _wrapUp = (unit, limit, overflow) => (unit >= limit ? overflow : unit);
  // @todo in the primary number case the wrap should happen when the everything is zero except the smallest unit is 1
  const _wrapDown = (unit, limit, overflow) => (unit < 0 ? overflow : unit);
  const _check = () =>
    typeof _checkSum === undefined || _checkSum === null || _checkSum === 0;
  const _setCheckSum = (newHand, overflow) => {
    _checkSum += newHand - overflow;
  };
  const _print = (unitElement, displayHand) => {
    unitElement.innerHTML = displayHand;
  };
  const _hand = (unitElement) => parseFloat(unitElement.innerText);
  // for the main count down e.g. t-minus we want 10..01 (then blast off at 00)
  // this is also similar to the primary counter in a count down timer
  // e.g. for a 25:00 pomodoro, when you get down to 00:01 you want to transition to 25:00 again
  // but at 01:01 you want to transition to 01:00 and then to 00:59
  // for regular time count down e.g. tenth's of a second we want 9..0
  // this function can't handle both
  // to handle the t-minus/pomodoro minutes case we should use limit + 1
  const limitLength = (limit) => Math.ceil(Math.log10(limit));
  // these functions are still confusing
  // we prepend length zeros
  // and then cut off as many zeros as newhand.length
  // zeroPad(3)(99) ---> '00099' ---> '099'
  // zeroPad(1)(99) ---> '099' ---> '9'
  const zeroPad = (length) => (newHand) =>
    ("0".repeat(length) + newHand).slice(-length);
  // With the comments for limitLength we have an oddity for zeroPadLimit(100)(1) === '01' not '001'
  const zeroPadLimit = (limit) => zeroPad(limitLength(limit));
  const _callback = (func, newHand, oldHand) => {
    if (typeof func == "function" && newHand != oldHand) {
      try {
        func();
      } catch (e) {
        // bury the chker
      }
    }
  };
  const _controller = (
    unitElement,
    limit,
    func,
    overflowFunc,
    countFunc,
    wrapFunc
  ) => {
    const overflow = overflowFunc(limit);
    const oldHand = _hand(unitElement);
    const newHand = wrapFunc(countFunc(oldHand, _check()), limit, overflow);
    _callback(func, newHand, oldHand);
    _print(unitElement, zeroPadLimit(limit)(newHand));
    _setCheckSum(newHand, overflow);
  };
  return {
    up: function (unitElement, limit, func) {
      _controller(unitElement, limit, func, _upOverflow, _countUp, _wrapUp);
      return this;
    },
    down: function (unitElement, limit, func) {
      _controller(
        unitElement,
        limit,
        func,
        _downOverflow,
        _countDown,
        _wrapDown
      );
      return this;
    },
    zeroPadLimit: zeroPadLimit,
    zeroPad: zeroPad,
    limitLength: limitLength,
  };
};
export { chk };
<commit_msg>Dev, merge comments from chk.js<commit_after>/**
 * A general object for ticking a set of values up or down
 *
 * Updated with ES6 syntax
 *
 * Each tick is done on a unit (e.g. hour / minute / second)
 * And compared against all the fractions of the unit
 * e.g. seconds are fractions of a minute
 * Short for chained tick I think of it as 'chuck' rather than 'check'
 * Also see: @link https://en.wikipedia.org/wiki/!!!
 *
 * This code was based off https://github.com/kellishaver/stopwatch
 * all that is left of that are the zeroPad and hand functions
 * they've both been modifed as well, but conceptually the same
 *
 * @todo I still get confused by how this all hangs together
 */
const chk = () => {
  "use strict";
  /**
   * The checkSum to see if the current unit should tick up or down
   *
   * This is the magic to allow all the chaining
   * It means that a larger unit will only tick
   * when all smaller factions have just 'ticked'
   *
   * Going up we know that all the values have ticked
   * if they are zero
   * Going down we know that all the values have ticked
   * if they are one less than the limit e.g. 59 seconds
   *
   * The default of null means always tick
   *
   * effectively internal state
   * we use external state from the state of the counters in the HTML elements
   * @type {Number}
   */
  let _checkSum = null;

  /**
   * What to set the value to when it overflows the limit
   * @param  {Number} limit The count at which to overflow and cycle back
   * @return {Number} zero, ignoring the limit
   */
  const _upOverflow = () => 0;

  /**
   * What to set the value to when it overflows the limit
   *
   * @todo the down overflow for the primary number should be 'limit'
   * for example t-minus goes from 10...1, but possibly it should also hit zero,
   * the counter should be left at zero after the lift off
   * but for tenths of a second counting down it goes 9...0
   * so the action happens on the transition from 1 -> 0 instead of 0 -> 9
   * for a regular clock though the tick happens from 00 -> 59
   *
   * @param  {Number}   limit        The count at which to overflow and cycle back, cannot be less than zero
   * @return {Number}
   */
  const _downOverflow = (limit) => (limit <= 0 ? 0 : limit - 1);

  /**
   * Tick up
   * @param  {Number}  unit   The current value
   * @param  {Boolean} check  Whether to tick this value up by one
   * @return {Number}         The ticked unit - this will be the same as unit unless check is true
   */
  const _countUp = (unit, check) => (check ? unit + 1 : unit);

  /**
   * Tick down
   * @param  {number}  unit   the current value
   * @param  {boolean} check  whether to tick this value down by one
   * @return {number}         the ticked unit - this will be the same as unit unless check is true
   */
  const _countDown = (unit, check) => (check ? unit - 1 : unit);

  /**
   * Wrap around
   * @param  {Number} unit     The current value
   * @param  {Number} limit    The count of how many units e.g. (gone in) 60 seconds
   * @param  {Number} overflow Where to go when you're over the limit (avoiding jail)
   * @return {Number}          The wrapped unit - this will be the same as unit unless we've gone over the limit
   */
  const _wrapUp = (unit, limit, overflow) => (unit >= limit ? overflow : unit);

  /**
   * Wrap around
   *
   * @todo in the primary number case the wrap should happen when the everything is zero except the smallest unit is 1
   *
   * @param  {Number} unit     The current value
   * @param  {Number} limit    The count of how many units e.g. (gone in) 60 seconds
   * @param  {Number} overflow Where to go when you're over the limit (avoiding jail)
   * @return {Number}          The wrapped unit - this will be the same as unit unless we've gone over the limit
   */
  const _wrapDown = (unit, limit, overflow) => (unit < 0 ? overflow : unit);

  /**
   * Whether to tick up/down
   * @param  {Number}  checkSum sum of all dependent chk objects
   * @return {Boolean}          tick or not to tick
   */
  const _check = () =>
    typeof _checkSum === undefined || _checkSum === null || _checkSum === 0;

  /**
   * Update the internal state
   *
   * The sum being calculated here is actually
   * a sum of all the previous chained states
   * See details of _checkSum property for more info
   *
   * @param  {Number} newHand     new value after tick
   * @param  {Number} overflow    Where to go when you're over the limit
   */
  const _setCheckSum = (newHand, overflow) => {
    _checkSum += newHand - overflow;
  };

  /**
   * Change the outside world
   *
   * @param  {Object} unitElement JavaScript element
   * @param  {Object} displayHand zero padded value after tick
   */
  const _print = (unitElement, displayHand) => {
    unitElement.innerHTML = displayHand;
  };

  /**
   * Get from the outside world the value of one of the clock 'hands'
   *
   * The meaning doesn't apply if you're using values
   * that aren't hours / minutes / seconds
   * But it's just getting the value for the current unit
   *
   * @param  {Object} unitElement JavaScript element
   * @return {Number}             The integer value
   */
  const _hand = (unitElement) => parseFloat(unitElement.innerText);

  /**
   * Convert a value to a power of 10
   *
   * @todo for the main count down e.g. t-minus we want 10..01 (then blast off at 00)
   * this is also similar to the primary counter in a count down timer
   * e.g. for a 25:00 pomodoro, when you get down to 00:01 you want to transition to 25:00 again
   * but at 01:01 you want to transition to 01:00 and then to 00:59
   * for regular time count down e.g. tenth's of a second we want 9..0
   * this function can't handle both
   * to handle the t-minus/pomodoro minutes case we should use limit + 1
   *
   * @param  {Number} limit Max value for the counter
   * @return {Number}       Length of zeros for the limit
   */
  const limitLength = (limit) => Math.ceil(Math.log10(limit));

  /**
   * Pad numbers with a single zero for printing
   *
   * these functions are still confusing
   * we prepend length zeros
   * and then cut off as many zeros as newhand.length
   * zeroPad(3)(99) ---> '00099' ---> '099'
   * zeroPad(1)(99) ---> '099' ---> '9'
   *
   * @param  {Number} length  How many zeros to pad
   * @param  {Number} newHand The new value to print 0+
   * @return {String}         01, ..., 09, 10, ...
   */
  const zeroPad = (length) => (newHand) =>
    ("0".repeat(length) + newHand).slice(-length);

  /**
   * Calculate the number of zeros based on the max value
   *
   * @todo With the comments for limitLength we have an oddity for zeroPadLimit(100)(1) === '01' not '001'
   *
   * @param  {Number} limit Max value for the counter
   * @return {Number}       Zero padded limit
   */
  const zeroPadLimit = (limit) => zeroPad(limitLength(limit));

  /**
   * call the callback
   * @param  {Function} func  Callback function
   * @param  {Number} newHand new value after tick
   * @param  {Number} oldHand old value before tick
   * @return {None}
   */
  const _callback = (func, newHand, oldHand) => {
    if (typeof func == "function" && newHand != oldHand) {
      try {
        func();
      } catch (e) {
        // bury the chker
      }
    }
  };

  /**
   * Get the unit, update it, print it and store chained state
   * This is now the central function
   * It allows passing in overflowFunc, countFunc and wrapFunc
   * to be independent of counting up / down
   *
   * @param  {Object}   unitElement  JavaScript element for getting, setting
   * @param  {Number}   limit        The count at which to overflow and cycle back, cannot be less than zero
   * @param  {Function} func         Callback function
   * @param  {Function} overflowFunc get the overflow value beyond the limit
   * @param  {Function} countFunc    the tick function
   * @param  {Function} wrapFunc     the wrap around function
   * @return {Object}                this (with updated _checkSum property)
   */
  const _controller = (
    unitElement,
    limit,
    func,
    overflowFunc,
    countFunc,
    wrapFunc
  ) => {
    const overflow = overflowFunc(limit);
    const oldHand = _hand(unitElement);
    const newHand = wrapFunc(countFunc(oldHand, _check()), limit, overflow);
    _callback(func, newHand, oldHand);
    _print(unitElement, zeroPadLimit(limit)(newHand));
    _setCheckSum(newHand, overflow);
  };
  return {
    /**
     * Stopwatch style tick up
     *
     * @param  {Object}   unitElement JavaScript element
     * @param  {Number}   limit       The count at which to overflow and cycle back
     * @param  {Function} func        Callback function for each tick of this object
     * @return {Object}               this, allow for chaining
     */
    up: function (unitElement, limit, func) {
      _controller(unitElement, limit, func, _upOverflow, _countUp, _wrapUp);
      return this;
    },

    /**
     * Timer style tick down
     *
     * @param  {Object} unitElement JavaScript element
     * @param  {Number} limit       The count at which to overflow and cycle back, cannot be less than zero
     * @return {Object}             this, allow for chaining
     */
    down: function (unitElement, limit, func) {
      _controller(
        unitElement,
        limit,
        func,
        _downOverflow,
        _countDown,
        _wrapDown
      );
      return this;
    },
    zeroPadLimit: zeroPadLimit,
    zeroPad: zeroPad,
    limitLength: limitLength,
  };
};
export { chk };
<|endoftext|>


<commit_before>var AWS = require('aws-sdk');
var fs = require('fs');
var multiparty = require('multiparty');
var authorization = require('../authentication/utils.js');
var config = require('../config');
var queue = require('./queue')

AWS.config.update({ accessKeyId: config.aws.accessKey, secretAccessKey: config.aws.secretKey });

module.exports = {
  uploadAddressFile: function(req, res){
    if (req.isAuthenticated() == false) {
      res.writeHead(403);
      res.write('must be authenticated to submit a file for testing')
      res.end();
      return;
    };
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('received upload:\n\n');
      res.end(JSON.stringify(files));
      var fileStream = fs.createReadStream(files.file.path);
      fileStream.on('error', function (err) {
        if (err) { throw err; }
      });
      fileStream.on('open', function () {
        var s3 = new AWS.S3();
        var groupName = authorization.stateGroupNames(req.user)[0];
        if (groupName === undefined) {
          groupName = "undefined"
        };
        var bucketName = 'address-testing';
        var fileName = groupName + '/input/' + files.file.originalFilename;
        s3.putObject({
          Bucket: bucketName,
          Key: fileName,
          Body: fileStream
        }, function (err) {
          if (err) {
            throw err;
          } else {
            queue.submitAddressFile(bucketName, fileName, groupName);
          }
        });
      });
    });

    return;

  },
  getLatestResultsFile: function(req, res){
    var s3 = new AWS.S3();
    var groupName = authorization.stateGroupNames(req.user)[0];
    if (groupName === undefined) {
      groupName = "undefined";
    }
    var bucketName = 'address-testing';
    var fileName  = groupName + "/output/results.csv"

    var params = {
      Bucket: bucketName,
      MaxKeys: 1,
      Prefix: fileName
    };
    s3.listObjects(params, function(err, data) {
      if (err) {
        console.log(err, err.stack);
        res.writeHead(500, {'content-type': 'text/plain'});
        res.end();
      } else {
        if (data["Contents"] != []) {
          var params = {Bucket: bucketName, Key: fileName, Expires: 3600};
          var url = s3.getSignedUrl('getObject', params);
          res.writeHead(200, {'content-type': 'text/plain'});
          res.write(url);
          res.end();
        } else {
          res.writeHead(404, {'content-type': 'text/plain'});
          res.end();
        }
      }
    });
  }
};
<commit_msg>add proper logging to aws requests<commit_after>var AWS = require('aws-sdk');
var fs = require('fs');
var multiparty = require('multiparty');
var authorization = require('../authentication/utils.js');
var config = require('../config');
var queue = require('./queue')
var logger = (require('../logging/vip-winston')).Logger;

AWS.config.update({ accessKeyId: config.aws.accessKey, secretAccessKey: config.aws.secretKey });

module.exports = {
  uploadAddressFile: function(req, res){
    if (req.isAuthenticated() == false) {
      res.writeHead(403);
      res.write('must be authenticated to submit a file for testing')
      res.end();
      return;
    };
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('received upload:\n\n');
      res.end(JSON.stringify(files));
      var fileStream = fs.createReadStream(files.file.path);
      fileStream.on('error', function (err) {
        if (err) { throw err; }
      });
      fileStream.on('open', function () {
        var s3 = new AWS.S3();
        var groupName = authorization.stateGroupNames(req.user)[0];
        if (groupName === undefined) {
          groupName = "undefined"
        };
        var bucketName = 'address-testing';
        var fileName = groupName + '/input/' + files.file.originalFilename;
        logger.info("putting file with name '" + fileName + "' into bucket '" + bucketName + "'");
        s3.putObject({
          Bucket: bucketName,
          Key: fileName,
          Body: fileStream
        }, function (err) {
          if (err) {
            throw err;
          } else {
            queue.submitAddressFile(bucketName, fileName, groupName);
          }
        });
      });
    });

    return;

  },
  getLatestResultsFile: function(req, res){
    var s3 = new AWS.S3();
    var groupName = authorization.stateGroupNames(req.user)[0];
    if (groupName === undefined) {
      groupName = "undefined";
    }
    var bucketName = 'address-testing';
    var fileName  = groupName + "/output/results.csv"

    var params = {
      Bucket: bucketName,
      MaxKeys: 1,
      Prefix: fileName
    };
    s3.listObjects(params, function(err, data) {
      if (err) {
        logger.error(err, err.stack);
        res.writeHead(500, {'content-type': 'text/plain'});
        res.end();
      } else {
        if (data["Contents"] != []) {
          var params = {Bucket: bucketName, Key: fileName, Expires: 3600};
          logger.info("requesting from Amazon with params: " + params);
          var url = s3.getSignedUrl('getObject', params);
          logger.info("generated pre-signed URL " + url);
          res.writeHead(200, {'content-type': 'text/plain'});
          res.write(url);
          res.end();
        } else {
          res.writeHead(404, {'content-type': 'text/plain'});
          res.end();
        }
      }
    });
  }
};
<|endoftext|>


<commit_before>import classNames from "classnames";
import { Link } from "react-router";
import PropTypes from "prop-types";
import React from "react";
import { Table } from "reactjs-components";

import { reconstructPathFromRoutes } from "#SRC/js/utils/RouterUtil";
import Volume from "../structs/Volume";
import VolumeStatus from "../constants/VolumeStatus";
import VolumeDefinitions from "../constants/VolumeDefinitions";

const METHODS_TO_BIND = ["renderIDColumn"];

class PodVolumeTable extends React.Component {
  constructor() {
    super();

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  getData(volumes) {
    return volumes.map(function(volume) {
      return {
        id: volume.getId(),
        host: volume.getHost(),
        type: volume.getType(),
        profile: volume.getProfile(),
        name: volume.getContainerPath(),
        size: volume.getSize(),
        status: volume.getStatus()
      };
    });
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{ width: "30%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
      </colgroup>
    );
  }

  getColumnClassName(prop, sortBy, row) {
    return classNames({
      active: prop === sortBy.prop,
      clickable: row == null,
      "text-overflow": prop === "profile"
    });
  }

  getColumnHeading(prop, order, sortBy) {
    const caretClassNames = classNames("caret", {
      [`caret--${order}`]: order != null,
      "caret--visible": prop === sortBy.prop
    });

    const headingStrings = {
      id: "ID",
      host: "Host",
      type: "Volume Type",
      profile: "Volume Profile",
      name: "Volume Name",
      size: "Size",
      status: "Status"
    };

    return (

        {headingStrings[prop]}

    );
  }

  getColumns() {
    return [
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "id",
        render: this.renderIDColumn,
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "host",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "type",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "profile",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "name",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "size",
        render: this.renderSizeColumn,
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "status",
        render: this.renderStatusColumn,
        sortable: true
      }
    ];
  }

  renderIDColumn(prop, row) {
    const id = row[prop];
    const { nodeID, taskID } = this.props.params;
    const volumeID = encodeURIComponent(id);
    const serviceID = encodeURIComponent(this.props.params.id);
    const currentroutePath = reconstructPathFromRoutes(this.props.routes);
    let routePath = null;

    if (currentroutePath === "/services/detail/:id/podvolumes") {
      routePath = `/services/detail/${serviceID}/podvolumes/${volumeID}`;
    } else if (
      currentroutePath === "/services/detail/:id/tasks/:taskID/podvolumes"
    ) {
      routePath = `/services/detail/${serviceID}/tasks/${taskID}/podvolumes/${volumeID}`;
    } else if (currentroutePath === "/nodes/:nodeID/tasks/:taskID/podvolumes") {
      routePath = `/nodes/${nodeID}/tasks/${taskID}/podvolumes/${volumeID}`;
    }

    return (
      <Link className="table-cell-link-primary" to={routePath}>
        {id}
      </Link>
    );
  }

  renderSizeColumn(prop, row) {
    const { size } = row;
    let unit = "MiB";

    if (
      row.type !== VolumeDefinitions.PERSISTENT.type &&
      row.type !== VolumeDefinitions.DSS.type
    ) {
      unit = "GiB";
    }

    return {`${size} ${unit}`};
  }

  renderStatusColumn(prop, row) {
    const value = row[prop];
    const classes = classNames({
      "text-danger": value === VolumeStatus.DETACHED,
      "text-success": value === VolumeStatus.ATTACHED
    });

    return {row[prop]};
  }

  render() {
    return (

    );
  }
}

PodVolumeTable.propTypes = {
  volumes: PropTypes.arrayOf(PropTypes.instanceOf(Volume)),
  params: PropTypes.object.isRequired,
  routes: PropTypes.array.isRequired
};

export default PodVolumeTable;
<commit_msg>feat(PodDetail): translate component strings<commit_after>import classNames from "classnames";
import { Link } from "react-router";
import PropTypes from "prop-types";
import React from "react";
import { Table } from "reactjs-components";
import { Trans } from "@lingui/macro";

import { reconstructPathFromRoutes } from "#SRC/js/utils/RouterUtil";
import Volume from "../structs/Volume";
import VolumeStatus from "../constants/VolumeStatus";
import VolumeDefinitions from "../constants/VolumeDefinitions";

const METHODS_TO_BIND = ["renderIDColumn"];

class PodVolumeTable extends React.Component {
  constructor() {
    super();

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  getData(volumes) {
    return volumes.map(function(volume) {
      return {
        id: volume.getId(),
        host: volume.getHost(),
        type: volume.getType(),
        profile: volume.getProfile(),
        name: volume.getContainerPath(),
        size: volume.getSize(),
        status: volume.getStatus()
      };
    });
  }

  getColGroup() {
    return (
      <colgroup>
        <col style={{ width: "30%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
        <col style={{ width: "10%" }} />
      </colgroup>
    );
  }

  getColumnClassName(prop, sortBy, row) {
    return classNames({
      active: prop === sortBy.prop,
      clickable: row == null,
      "text-overflow": prop === "profile"
    });
  }

  getColumnHeading(prop, order, sortBy) {
    const caretClassNames = classNames("caret", {
      [`caret--${order}`]: order != null,
      "caret--visible": prop === sortBy.prop
    });

    const headingStrings = {
      id: <Trans render="span">ID</Trans>,
      host: <Trans render="span">Host</Trans>,
      type: <Trans render="span">Volume Type</Trans>,
      profile: <Trans render="span">Volume Profile</Trans>,
      name: <Trans render="span">Volume Name</Trans>,
      size: <Trans render="span">Size</Trans>,
      status: <Trans render="span">Status</Trans>
    };

    return (

        {headingStrings[prop]}

    );
  }

  getColumns() {
    return [
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "id",
        render: this.renderIDColumn,
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "host",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "type",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "profile",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "name",
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "size",
        render: this.renderSizeColumn,
        sortable: true
      },
      {
        className: this.getColumnClassName,
        heading: this.getColumnHeading,
        prop: "status",
        render: this.renderStatusColumn,
        sortable: true
      }
    ];
  }

  renderIDColumn(prop, row) {
    const id = row[prop];
    const { nodeID, taskID } = this.props.params;
    const volumeID = encodeURIComponent(id);
    const serviceID = encodeURIComponent(this.props.params.id);
    const currentroutePath = reconstructPathFromRoutes(this.props.routes);
    let routePath = null;

    if (currentroutePath === "/services/detail/:id/podvolumes") {
      routePath = `/services/detail/${serviceID}/podvolumes/${volumeID}`;
    } else if (
      currentroutePath === "/services/detail/:id/tasks/:taskID/podvolumes"
    ) {
      routePath = `/services/detail/${serviceID}/tasks/${taskID}/podvolumes/${volumeID}`;
    } else if (currentroutePath === "/nodes/:nodeID/tasks/:taskID/podvolumes") {
      routePath = `/nodes/${nodeID}/tasks/${taskID}/podvolumes/${volumeID}`;
    }

    return (
      <Link className="table-cell-link-primary" to={routePath}>
        {id}
      </Link>
    );
  }

  renderSizeColumn(prop, row) {
    const { size } = row;
    let unit = "MiB";

    if (
      row.type !== VolumeDefinitions.PERSISTENT.type &&
      row.type !== VolumeDefinitions.DSS.type
    ) {
      unit = "GiB";
    }

    return {`${size} ${unit}`};
  }

  renderStatusColumn(prop, row) {
    const value = row[prop];
    const classes = classNames({
      "text-danger": value === VolumeStatus.DETACHED,
      "text-success": value === VolumeStatus.ATTACHED
    });

    return {row[prop]};
  }

  render() {
    return (

    );
  }
}

PodVolumeTable.propTypes = {
  volumes: PropTypes.arrayOf(PropTypes.instanceOf(Volume)),
  params: PropTypes.object.isRequired,
  routes: PropTypes.array.isRequired
};

export default PodVolumeTable;
<|endoftext|>


<commit_before>/* global course */

define(['underscore', 'jquery', 'gettext', 'edx-ui-toolkit/js/utils/html-utils',
    'js/views/baseview', 'js/models/uploads', 'js/views/uploads', 'text!templates/edit-chapter.underscore'],
    function(_, $, gettext, HtmlUtils, BaseView, FileUploadModel, UploadDialogView, editChapterTemplate) {
        'use strict';

        var EditChapter = BaseView.extend({
            initialize: function() {
                this.template = HtmlUtils.template(editChapterTemplate);
                this.listenTo(this.model, 'change', this.render);
            },
            tagName: 'li',
            className: function() {
                return 'field-group chapter chapter' + this.model.get('order');
            },
            render: function() {
                HtmlUtils.setHtml(
                    this.$el,
                    this.template({
                        name: this.model.get('name'),
                        asset_path: this.model.get('asset_path'),
                        order: this.model.get('order'),
                        error: this.model.validationError
                    })
                );
                return this;
            },
            events: {
                'change .chapter-name': 'changeName',
                'change .chapter-asset-path': 'changeAssetPath',
                'click .action-close': 'removeChapter',
                'click .action-upload': 'openUploadDialog',
                submit: 'uploadAsset'
            },
            changeName: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    name: this.$('.chapter-name').val()
                }, {silent: true});
                return this;
            },
            changeAssetPath: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    asset_path: this.$('.chapter-asset-path').val()
                }, {silent: true});
                return this;
            },
            removeChapter: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.collection.remove(this.model);
                return this.remove();
            },
            openUploadDialog: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    name: this.$('input.chapter-name').val(),
                    asset_path: this.$('input.chapter-asset-path').val()
                });
                var msg = new FileUploadModel({
                    title: _.template(gettext('Upload a new PDF to “<%= name %>”'))(
                        {name: course.escape('name')}),
                    message: gettext('Please select a PDF file to upload.'),
                    mimeTypes: ['application/pdf']
                });
                var that = this;
                var view = new UploadDialogView({
                    model: msg,
                    onSuccess: function(response) {
                        var options = {};
                        if (!that.model.get('name')) {
                            options.name = response.asset.displayname;
                        }
                        options.asset_path = response.asset.portable_url;
                        that.model.set(options);
                    }
                });
                view.show();
            }
        });

        return EditChapter;
    });
<commit_msg>Apply patch for edit_chapter xss lint issues<commit_after>/* global course */

define(['underscore', 'jquery', 'gettext', 'edx-ui-toolkit/js/utils/html-utils',
    'js/views/baseview', 'js/models/uploads', 'js/views/uploads', 'text!templates/edit-chapter.underscore'],
    function(_, $, gettext, HtmlUtils, BaseView, FileUploadModel, UploadDialogView, editChapterTemplate) {
        'use strict';

        var EditChapter = BaseView.extend({
            initialize: function() {
                this.template = HtmlUtils.template(editChapterTemplate);
                this.listenTo(this.model, 'change', this.render);
            },
            tagName: 'li',
            className: function() {
                return 'field-group chapter chapter' + this.model.get('order');
            },
            render: function() {
                HtmlUtils.setHtml(
                    this.$el,
                    this.template({
                        name: this.model.get('name'),
                        asset_path: this.model.get('asset_path'),
                        order: this.model.get('order'),
                        error: this.model.validationError
                    })
                );
                return this;
            },
            events: {
                'change .chapter-name': 'changeName',
                'change .chapter-asset-path': 'changeAssetPath',
                'click .action-close': 'removeChapter',
                'click .action-upload': 'openUploadDialog',
                submit: 'uploadAsset'
            },
            changeName: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    name: this.$('.chapter-name').val()
                }, {silent: true});
                return this;
            },
            changeAssetPath: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    asset_path: this.$('.chapter-asset-path').val()
                }, {silent: true});
                return this;
            },
            removeChapter: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.collection.remove(this.model);
                return this.remove();
            },
            openUploadDialog: function(e) {
                if (e && e.preventDefault) { e.preventDefault(); }
                this.model.set({
                    name: this.$('input.chapter-name').val(),
                    asset_path: this.$('input.chapter-asset-path').val()
                });
                var msg = new FileUploadModel({
                    title: _.template(gettext('Upload a new PDF to “<%- name %>”'))(
                        {name: course.get('name')}),
                    message: gettext('Please select a PDF file to upload.'),
                    mimeTypes: ['application/pdf']
                });
                var that = this;
                var view = new UploadDialogView({
                    model: msg,
                    onSuccess: function(response) {
                        var options = {};
                        if (!that.model.get('name')) {
                            options.name = response.asset.displayname;
                        }
                        options.asset_path = response.asset.portable_url;
                        that.model.set(options);
                    }
                });
                view.show();
            }
        });

        return EditChapter;
    });
<|endoftext|>


<commit_before>import { useCallback, useContext } from 'react';
import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';
import { t } from 'ttag';

import { Box } from '@material-ui/core';

import useCurrentUser from 'lib/useCurrentUser';
import Desktop from './Desktop';
import Mobile from './Mobile';
import getDedupedArticleReplies from 'lib/getDedupedArticleReplies';
import RelatedReplies from 'components/RelatedReplies';
import ReplyFormContext, { withReplyFormContext } from './ReplyForm/context';
import { withReplySearchContext } from './ReplySearch/context';

const RelatedArticleData = gql`
  fragment RelatedArticleData on Article {
    relatedArticles(filter: { replyCount: { GT: 0 } }) {
      edges {
        node {
          id
          articleReplies {
            ...RelatedArticleReplyData
          }
        }
      }
    }
  }
  ${RelatedReplies.fragments.RelatedArticleReplyData}
`;

const CREATE_REPLY = gql`
  mutation CreateReplyInArticlePage(
    $articleId: String!
    $text: String!
    $type: ReplyTypeEnum!
    $reference: String
  ) {
    CreateReply(
      articleId: $articleId
      text: $text
      type: $type
      reference: $reference
    ) {
      id
    }
  }
`;

const CONNECT_REPLY = gql`
  mutation ConnectReplyInArticlePage($articleId: String!, $replyId: String!) {
    CreateArticleReply(articleId: $articleId, replyId: $replyId) {
      articleId
    }
  }
`;

const withContext = f => withReplyFormContext(withReplySearchContext(f));

const NewReplySection = withContext(
  ({
    article,
    existingReplyIds,
    relatedArticles,
    onSubmissionComplete,
    onError,
    onClose,
  }) => {
    const { fields, handlers } = useContext(ReplyFormContext);
    const currentUser = useCurrentUser();

    const [createReply, { loading: creatingReply }] = useMutation(
      CREATE_REPLY,
      {
        refetchQueries: ['LoadArticlePage'],
        awaitRefetchQueries: true,
        onCompleted() {
          onSubmissionComplete(); // Notify upper component of submission
          handlers.clear();
          onClose();
        },
        onError,
      }
    );
    const [connectReply, { loading: connectingReply }] = useMutation(
      CONNECT_REPLY,
      {
        refetchQueries: ['LoadArticlePage'],
        awaitRefetchQueries: true,
        onCompleted() {
          onSubmissionComplete(); // Notify upper component of submission
          handlers.clear();
          onClose();
        },
        onError,
      }
    );

    const handleSubmit = useCallback(
      e => {
        const { replyType: type, reference, text } = fields;
        e.preventDefault(); // prevent reload
        if (creatingReply) return;
        createReply({
          variables: { type, reference, text, articleId: article.id },
        });
      },
      [createReply]
    );

    const relatedArticleReplies = getDedupedArticleReplies(
      relatedArticles,
      existingReplyIds
    );

    const handleConnect = replyId => {
      connectReply({ variables: { articleId: article.id, replyId } });
    };

    if (!currentUser) {
      return
{t`Please login first.`}
;
    }

    const sharedProps = {
      relatedArticleReplies,
      handleSubmit,
      handleConnect,
      connectingReply,
      creatingReply,
      existingReplyIds,
    };

    return (
      <form onSubmit={handleSubmit}>
        {/* prevent chrome auto submit behavior when 'Enter' pressed */}
        <button type="submit" disabled hidden />
        <Box display={{ xs: 'none', md: 'block' }}>
          <Desktop {...sharedProps} />
        </Box>
        <Box display={{ xs: 'block', md: 'none' }}>
          <Mobile onClose={onClose} article={article} {...sharedProps} />
        </Box>
      </form>
    );
  }
);

NewReplySection.fragments = {
  RelatedArticleData,
};

export default NewReplySection;
<commit_msg>Fix reply form empty submission bug Ref: https://www.facebook.com/groups/cofacts/permalink/2702689166629562/<commit_after>import { useCallback, useContext } from 'react';
import { useMutation } from '@apollo/react-hooks';
import gql from 'graphql-tag';
import { t } from 'ttag';

import { Box } from '@material-ui/core';

import useCurrentUser from 'lib/useCurrentUser';
import Desktop from './Desktop';
import Mobile from './Mobile';
import getDedupedArticleReplies from 'lib/getDedupedArticleReplies';
import RelatedReplies from 'components/RelatedReplies';
import ReplyFormContext, { withReplyFormContext } from './ReplyForm/context';
import { withReplySearchContext } from './ReplySearch/context';

const RelatedArticleData = gql`
  fragment RelatedArticleData on Article {
    relatedArticles(filter: { replyCount: { GT: 0 } }) {
      edges {
        node {
          id
          articleReplies {
            ...RelatedArticleReplyData
          }
        }
      }
    }
  }
  ${RelatedReplies.fragments.RelatedArticleReplyData}
`;

const CREATE_REPLY = gql`
  mutation CreateReplyInArticlePage(
    $articleId: String!
    $text: String!
    $type: ReplyTypeEnum!
    $reference: String
  ) {
    CreateReply(
      articleId: $articleId
      text: $text
      type: $type
      reference: $reference
    ) {
      id
    }
  }
`;

const CONNECT_REPLY = gql`
  mutation ConnectReplyInArticlePage($articleId: String!, $replyId: String!) {
    CreateArticleReply(articleId: $articleId, replyId: $replyId) {
      articleId
    }
  }
`;

const withContext = f => withReplyFormContext(withReplySearchContext(f));

const NewReplySection = withContext(
  ({
    article,
    existingReplyIds,
    relatedArticles,
    onSubmissionComplete,
    onError,
    onClose,
  }) => {
    const { fields, handlers } = useContext(ReplyFormContext);
    const currentUser = useCurrentUser();

    const [createReply, { loading: creatingReply }] = useMutation(
      CREATE_REPLY,
      {
        refetchQueries: ['LoadArticlePage'],
        awaitRefetchQueries: true,
        onCompleted() {
          onSubmissionComplete(); // Notify upper component of submission
          handlers.clear();
          onClose();
        },
        onError,
      }
    );
    const [connectReply, { loading: connectingReply }] = useMutation(
      CONNECT_REPLY,
      {
        refetchQueries: ['LoadArticlePage'],
        awaitRefetchQueries: true,
        onCompleted() {
          onSubmissionComplete(); // Notify upper component of submission
          handlers.clear();
          onClose();
        },
        onError,
      }
    );

    const handleSubmit = useCallback(
      e => {
        const { replyType: type, reference, text } = fields;
        e.preventDefault(); // prevent reload
        if (creatingReply) return;
        createReply({
          variables: { type, reference, text, articleId: article.id },
        });
      },
      [createReply, fields]
    );

    const relatedArticleReplies = getDedupedArticleReplies(
      relatedArticles,
      existingReplyIds
    );

    const handleConnect = replyId => {
      connectReply({ variables: { articleId: article.id, replyId } });
    };

    if (!currentUser) {
      return
{t`Please login first.`}
;
    }

    const sharedProps = {
      relatedArticleReplies,
      handleSubmit,
      handleConnect,
      connectingReply,
      creatingReply,
      existingReplyIds,
    };

    return (
      <form onSubmit={handleSubmit}>
        {/* prevent chrome auto submit behavior when 'Enter' pressed */}
        <button type="submit" disabled hidden />
        <Box display={{ xs: 'none', md: 'block' }}>
          <Desktop {...sharedProps} />
        </Box>
        <Box display={{ xs: 'block', md: 'none' }}>
          <Mobile onClose={onClose} article={article} {...sharedProps} />
        </Box>
      </form>
    );
  }
);

NewReplySection.fragments = {
  RelatedArticleData,
};

export default NewReplySection;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Text } from 'react-native';

import styles from '../../styles/styles.ios';
import InPageImage from '../inPageImage';
import AnimalText from '../animalText';
import AnimalTemplate from '../animalTemplate';

const IMAGES = [
  require('../../images/animals/dzelada/01.jpg'),
  require('../../images/animals/dzelada/02.jpg'),
  require('../../images/animals/dzelada/03.jpg'),
];

const THUMBNAILS = [
  require('../../images/animals/dzelada/01-thumb.jpg'),
  require('../../images/animals/dzelada/02-thumb.jpg'),
  require('../../images/animals/dzelada/03-thumb.jpg'),
];

var AnimalDetail = React.createClass({
  render() {
    return (
      <AnimalTemplate firstIndex={[1]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator}>
        <AnimalText>
          Ahoj děti, my jsme dželady. Že nás neznáte? Rádi se vám představíme. Jmenujeme se Heiko, Helge a Asrak. Já jsem Heiko a s Helgem zde pobýváme od roku 2005, Asrak je tu od roku 2008. Všichni jsme byli přivezeni z Německa, ze země, která sousedí s Českou republikou. Známí jsme také pod názvem dželada hnědá nebo pavián dželada.
        </AnimalText>
      <AnimalTemplate firstIndex={[2]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator} />
        <AnimalText>
          Charakteristickým znakem je pro nás holá prsní růžová ploška, kterou lemuje světle zbarvená srst. My samci jsme však celkově nápadnější než samice. Máme hnědočernou srst v mnoha odstínech a nápadně zbarvený obličej. Samice jsou většinou čistě tmavě hnědé barvy. Naše průměrná délka těla se pohybuje mezi 70–74 cm a hmotnost kolem 19 kg, přičemž samice bývají spíše drobnější a lehčí a mají také kratší ocas. Naše sedací mozoly nám umožňují sedět na kamenitých podložkách. Vydržíme to dlouho, dlouho, předlouho. Lidé nás občas přirovnávají k paviánům, ale od nich se lišíme v několika bodech. Máme kratší čelisti, delší obličeje s tupým nosem a vystupujícími lícními vaky, velmi dlouhé špičáky a stoličky, které mají vrásčitý povrch.
        </AnimalText>
      <AnimalTemplate firstIndex={[0]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator} />
        <AnimalText>
          Obýváme vysoko položené náhorní plošiny a louky na území Etiopie a Eritreje, tedy v zemích na jiném kontinentě. Většinu dne trávíme na horských loukách, kde vsedě vyhledáváme rostlinnou stravu. V období dešťů dáváme přednost zeleným částem rostlin, plodům a květům. V období sucha zase baštíme semena, trávu nebo hlízy. Protistojný palec nám umožňuje dokonalé třídění trávy a travních semen. Jednu ruku používáme ke shromažďování potravy, druhou ruku k jejímu třídění. Naše mláďata se rodí jako jedináčci a na matce jsou závislá po dobu 12–18 měsíců, my samci se na péči o potomky většinou nepodílíme. Víte, jak se dorozumíváme? Používáme celou řadu posunků, např. cenění zubů, trhání hlavou, ale i hlasové projevy – občas prostě křičíme jako opice.
        </AnimalText>
      </AnimalTemplate>
    );
  }
});

module.exports = AnimalDetail;
<commit_msg>Update dzelada.child.js<commit_after>import React, { Component } from 'react';
import { Text } from 'react-native';

import styles from '../../styles/styles.ios';
import InPageImage from '../inPageImage';
import AnimalText from '../animalText';
import AnimalTemplate from '../animalTemplate';

const IMAGES = [
  require('../../images/animals/dzelada/01.jpg'),
  require('../../images/animals/dzelada/02.jpg'),
  require('../../images/animals/dzelada/03.jpg'),
];

const THUMBNAILS = [
  require('../../images/animals/dzelada/01-thumb.jpg'),
  require('../../images/animals/dzelada/02-thumb.jpg'),
  require('../../images/animals/dzelada/03-thumb.jpg'),
];

var AnimalDetail = React.createClass({
  render() {
    return (
      <AnimalTemplate firstIndex={[1]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator}>
        <AnimalText>
          Ahoj děti, my jsme dželady. Že nás neznáte? Rádi se vám představíme. Jmenujeme se Heiko, Helge a Asrak. Já jsem Heiko a s Helgem zde pobýváme od roku 2005, Asrak je tu od roku 2008. Všichni jsme byli přivezeni z Německa, ze země, která sousedí s Českou republikou. Známí jsme také pod názvem dželada hnědá nebo pavián dželada.
        </AnimalText>
      <InPageImage indexes={[2]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator} />
        <AnimalText>
          Charakteristickým znakem je pro nás holá prsní růžová ploška, kterou lemuje světle zbarvená srst. My samci jsme však celkově nápadnější než samice. Máme hnědočernou srst v mnoha odstínech a nápadně zbarvený obličej. Samice jsou většinou čistě tmavě hnědé barvy. Naše průměrná délka těla se pohybuje mezi 70–74 cm a hmotnost kolem 19 kg, přičemž samice bývají spíše drobnější a lehčí a mají také kratší ocas. Naše sedací mozoly nám umožňují sedět na kamenitých podložkách. Vydržíme to dlouho, dlouho, předlouho. Lidé nás občas přirovnávají k paviánům, ale od nich se lišíme v několika bodech. Máme kratší čelisti, delší obličeje s tupým nosem a vystupujícími lícními vaky, velmi dlouhé špičáky a stoličky, které mají vrásčitý povrch.
        </AnimalText>
      <InPageImage indexes={[0]} thumbnails={THUMBNAILS} images={IMAGES} navigator={this.props.navigator} />
        <AnimalText>
          Obýváme vysoko položené náhorní plošiny a louky na území Etiopie a Eritreje, tedy v zemích na jiném kontinentě. Většinu dne trávíme na horských loukách, kde vsedě vyhledáváme rostlinnou stravu. V období dešťů dáváme přednost zeleným částem rostlin, plodům a květům. V období sucha zase baštíme semena, trávu nebo hlízy. Protistojný palec nám umožňuje dokonalé třídění trávy a travních semen. Jednu ruku používáme ke shromažďování potravy, druhou ruku k jejímu třídění. Naše mláďata se rodí jako jedináčci a na matce jsou závislá po dobu 12–18 měsíců, my samci se na péči o potomky většinou nepodílíme. Víte, jak se dorozumíváme? Používáme celou řadu posunků, např. cenění zubů, trhání hlavou, ale i hlasové projevy – občas prostě křičíme jako opice.
        </AnimalText>
      </AnimalTemplate>
    );
  }
});

module.exports = AnimalDetail;
<|endoftext|>