<commit_before>// rcpt_to.routes - per email/domain mail routes
//
// validates incoming recipients against flat file & Redis
// routes mail based on per-email or per-domain specified routes

exports.register = function() {
    var plugin = this;
    plugin.inherits('redis');

    plugin.cfg = {};
    plugin.route_list={};

    plugin.load_rcpt_to_routes_ini();
    plugin.load_redis_ini();

    plugin.register_hook('init_master',  'init_redis_plugin');
    plugin.register_hook('init_child',   'init_redis_plugin');

    plugin.register_hook('rcpt',   'rcpt');
    plugin.register_hook('get_mx', 'get_mx');
};

exports.load_rcpt_to_routes_ini = function () {
    var plugin = this;
    plugin.cfg = plugin.config.get('rcpt_to.routes.ini', function () {
        plugin.load_rcpt_to_routes_ini();
    });

    if (!plugin.cfg.redis) plugin.cfg.redis = {};
    var r = plugin.cfg.redis;

    plugin.cfg.redis.opts = {
        host: r.server_ip || r.host || '127.0.0.1',
        port: r.server_port || r.port || 6379,
    };

    var lowered = {};
    if (plugin.cfg.routes) {
        var keys = Object.keys(plugin.cfg.routes);
        for (var i=0; i < keys.length; i++) {
            lowered[keys[i].toLowerCase()] = plugin.cfg.routes[keys[i]];
        }
        plugin.route_list = lowered;
    }
};

exports.rcpt = function(next, connection, params) {
    var plugin = this;
    var txn = connection.transaction;
    if (!txn) { return next(); }

    var rcpt = params[0];

    // ignore RCPT TO without an @ first
    if (!rcpt.host) {
        txn.results.add(plugin, {fail: 'rcpt!domain'});
        return next();
    }

    var address = rcpt.address().toLowerCase();
    var domain = rcpt.host.toLowerCase();

    var do_file_search = function () {
        if (plugin.route_list[address]) {
            txn.results.add(plugin, {pass: 'file.email'});
            return next(OK);
        }
        if (plugin.route_list[domain])  {
            txn.results.add(plugin, {pass: 'file.domain'});
            return next(OK);
        }

        // not permitted (by this rcpt_to plugin)
        txn.results.add(plugin, {fail: 'file'});
        return next();
    };

    // if we can't use redis, try files and return
    if (!plugin.redis_pings) { return do_file_search(); }

    // redis connection open, try it
    plugin.db.multi()
        .get(address)
        .get(domain)
        .exec(function (err, replies) {
            if (err) {
                connection.results.add(plugin, {err: err});
                return next();
            }

            // got replies from Redis, any with an MX?
            if (replies[0]) {
                txn.results.add(plugin, {pass: 'redis.email'});
                return next(OK);
            }
            if (replies[1]) {
                txn.results.add(plugin, {pass: 'redis.domain'});
                return next(OK);
            }

            return do_file_search(); // no redis record, try files
        });
};

exports.get_mx = function(next, hmail, domain) {
    var plugin = this;

    // get email address
    var address = domain;
    if (hmail && hmail.todo && hmail.todo.rcpt_to && hmail.todo.rcpt_to[0]) {
        address = hmail.todo.rcpt_to[0].address();
    }
    else {
        plugin.logerror('no rcpt from hmail, falling back to domain' );
    }

    var do_file_search = function () {
        // check email adress for route
        if (plugin.route_list[address]) {
            return next(OK, plugin.route_list[address]);
        }

        // check email domain for route
        if (plugin.route_list[domain]) {
            return next(OK, plugin.route_list[domain]);
        }

        plugin.loginfo('using DNS MX for: ' + address);
        return next();
    };

    // if we can't use redis, try files and return
    if (!plugin.redis_pings) { return do_file_search(); }

    // redis connection open, try it
    plugin.db.multi()
        .get(address)
        .get(domain)
        .exec(function (err, replies) {
            if (err) {
                connection.results.add(plugin, {err: err});
                return next();
            }

            // got replies from Redis, any with an MX?
            if (replies[0]) { return next(OK, replies[0]); }
            if (replies[1]) { return next(OK, replies[1]); }

            return do_file_search(); // no redis record, try files
        });
};

exports.insert_route = function (email, route) {
    // for importing, see http://redis.io/topics/mass-insert
    if (!this.db || !this.redis_pings) { return false; }
    this.db.set(email, route);
};

exports.delete_route = function (email, cb) {
    if (!this.redis_pings) {
        if (cb) cb();
        return false;
    }
    if (cb) {
        this.db.del(email, cb);
    }
    else {
        this.db.del(email);
    }
};
<commit_msg>Support for new URI format with LMTP and fallback to old format<commit_after>// rcpt_to.routes - per email/domain mail routes
//
// validates incoming recipients against flat file & Redis
// routes mail based on per-email or per-domain specified routes

var urlparser = require('url-parse');

exports.register = function() {
    var plugin = this;
    plugin.inherits('redis');

    plugin.cfg = {};
    plugin.route_list={};

    plugin.load_rcpt_to_routes_ini();
    plugin.load_redis_ini();

    plugin.register_hook('init_master',  'init_redis_plugin');
    plugin.register_hook('init_child',   'init_redis_plugin');

    plugin.register_hook('rcpt',   'rcpt');
    plugin.register_hook('get_mx', 'get_mx');
};

exports.load_rcpt_to_routes_ini = function () {
    var plugin = this;
    plugin.cfg = plugin.config.get('rcpt_to.routes.ini', function () {
        plugin.load_rcpt_to_routes_ini();
    });

    if (!plugin.cfg.redis) plugin.cfg.redis = {};
    var r = plugin.cfg.redis;

    plugin.cfg.redis.opts = {
        host: r.server_ip || r.host || '127.0.0.1',
        port: r.server_port || r.port || 6379,
    };

    var lowered = {};
    if (plugin.cfg.routes) {
        var keys = Object.keys(plugin.cfg.routes);
        for (var i=0; i < keys.length; i++) {
            lowered[keys[i].toLowerCase()] = plugin.cfg.routes[keys[i]];
        }
        plugin.route_list = lowered;
    }
};

exports.rcpt = function(next, connection, params) {
    var plugin = this;
    var txn = connection.transaction;
    if (!txn) { return next(); }

    var rcpt = params[0];

    // ignore RCPT TO without an @ first
    if (!rcpt.host) {
        txn.results.add(plugin, {fail: 'rcpt!domain'});
        return next();
    }

    var address = rcpt.address().toLowerCase();
    var domain = rcpt.host.toLowerCase();

    var do_file_search = function () {
        if (plugin.route_list[address]) {
            txn.results.add(plugin, {pass: 'file.email'});
            return next(OK);
        }
        if (plugin.route_list[domain])  {
            txn.results.add(plugin, {pass: 'file.domain'});
            return next(OK);
        }

        // not permitted (by this rcpt_to plugin)
        txn.results.add(plugin, {fail: 'file'});
        return next();
    };

    // if we can't use redis, try files and return
    if (!plugin.redis_pings) { return do_file_search(); }

    // redis connection open, try it
    plugin.db.multi()
        .get(address)
        .get(domain)
        .exec(function (err, replies) {
            if (err) {
                connection.results.add(plugin, {err: err});
                return next();
            }

            // got replies from Redis, any with an MX?
            if (replies[0]) {
                txn.results.add(plugin, {pass: 'redis.email'});
                return next(OK);
            }
            if (replies[1]) {
                txn.results.add(plugin, {pass: 'redis.domain'});
                return next(OK);
            }

            return do_file_search(); // no redis record, try files
        });
};

exports.get_mx = function(next, hmail, domain) {
    var plugin = this;

    // get email address
    var address = domain;
    if (hmail && hmail.todo && hmail.todo.rcpt_to && hmail.todo.rcpt_to[0]) {
        address = hmail.todo.rcpt_to[0].address();
    }
    else {
        plugin.logerror('no rcpt from hmail, falling back to domain' );
    }

    var do_file_search = function () {
        var mx = {};
        // check email adress for route
        if (plugin.route_list[address]) {
            var uri = new urlparser(plugin.route_list[address]);
            if ( uri.protocol == 'lmtp:' ) {
                mx.exchange = uri.hostname;
                mx.port = uri.port;
                mx.using_lmtp = true;
                return next(OK, mx);
            }
            else if ( uri.protocol == 'smtp:' ) {
                mx.exchange = uri.hostname;
                mx.port = uri.port;
                return next(OK, mx);
            }
            else {
                return next(OK, plugin.route_list[address]);
            }
        }

        // check email domain for route
        if (plugin.route_list[domain]) {
            return next(OK, plugin.route_list[domain]);
        }

        plugin.loginfo('using DNS MX for: ' + address);
        return next();
    };

    // if we can't use redis, try files and return
    if (!plugin.redis_pings) { return do_file_search(); }

    // redis connection open, try it
    plugin.db.multi()
        .get(address)
        .get(domain)
        .exec(function (err, replies) {
            if (err) {
                connection.results.add(plugin, {err: err});
                return next();
            }

            // got replies from Redis, any with an MX?
            if (replies[0]) { return next(OK, replies[0]); }
            if (replies[1]) { return next(OK, replies[1]); }

            return do_file_search(); // no redis record, try files
        });
};

exports.insert_route = function (email, route) {
    // for importing, see http://redis.io/topics/mass-insert
    if (!this.db || !this.redis_pings) { return false; }
    this.db.set(email, route);
};

exports.delete_route = function (email, cb) {
    if (!this.redis_pings) {
        if (cb) cb();
        return false;
    }
    if (cb) {
        this.db.del(email, cb);
    }
    else {
        this.db.del(email);
    }
};
<|endoftext|>


<commit_before>function redraw_multiple_selects(container) {
  var c = container;
  if (typeof container == "string") {
    c = "#"+container;
  }
  $("select[multiple]",c).selectn("destroy",c);
  $("select[multiple]",c).selectn();
}

// Make the XML request thing, specify the callback function
function refreshProjectList(radiobutton) {
  url = get_alloc_var('url')+'task/updateProjectList.php?projectType='+radiobutton.value;
  makeAjaxRequest(url, 'projectListDropdown','',1)
}

function add_gst(value) {
  var tax_percent = get_alloc_var("tax_percent");
  return Math.round(value * (tax_percent/100 +1)*100)/100;
}

function deduct_gst(value) {
  var tax_percent = get_alloc_var("tax_percent");
  return Math.round(value / (tax_percent/100 +1)*100)/100;
}

function toggle_view_edit(reset) {
  $(".view").toggle();
  $(".edit").toggle();
  if (reset) {
    $(this).parents("form").reset();
  }
  redraw_multiple_selects();
  return false;
}

function makeAjaxRequest(url,entityid,extra_fields,redraw) {
  $("#"+entityid).html('');
  jQuery.get(url,extra_fields,function(data) {
    $("#"+entityid).hide();
    $("#"+entityid).html(data);
    $("#"+entityid).fadeIn("fast");

    // We may need to redraw the select widgets
    if (redraw) {
      redraw_multiple_selects(entityid);
    }
  });
}

// This is a generic show/hide for anything
function set_grow_shrink(id, id_to_hide, use_classes_instead_of_ids) {
  // toggle the other div - if any
  if (use_classes_instead_of_ids && id_to_hide) {
    $("."+id_to_hide).slideToggle("fast");
  } else if (id_to_hide) {
    $("#"+id_to_hide).slideToggle("fast");
  }
  // hide or show the actual div
  if (use_classes_instead_of_ids) {
    $("."+id).slideToggle("fast");
  } else {
    $("#"+id).slideToggle("fast");
  }
  return false;
}

function sidebyside_activate(id,redraw) {
  var arr = [];
  $.each($(".sidebyside"), function(k,v) {
    arr[arr.length] = v.id.replace("sbs_link_","");
  });

  if (!id) {
    id = get_alloc_var("side_by_side_link");
  }
  if (!id) {
    id = arr[0];
  }

  if (id == "sbsAll") {
    for (var i=0; i<arr.length; i++) {
      if (arr[i] != "sbsAll") {
        $("#"+arr[i]).show();
        $('#sbs_link_'+arr[i]).removeClass("sidebyside_active").addClass("sidebyside");
      }
    }
    $('#sbs_link_' + id).addClass("sidebyside_active");

  } else {
    for (var i=0; i<arr.length; i++) {
      if (arr[i] != "sbsAll") {
        $("#"+arr[i]).hide();
      }
      $('#sbs_link_' + arr[i]).removeClass("sidebyside_active").addClass("sidebyside");
    }
    $('#sbs_link_' + id).addClass("sidebyside_active");
    $("#"+id).show();
  }

  // allows us to target particular pages for redraw_multiple_selects();
  redraw_multiple_selects();
}

function help_text_on(id, str) {
  $('#main').append("

");
  $('#helper').html(str).hide();
  var offset = $('#'+id).position();
  var x = offset.left -420;
  if (x < 0) {
    x = x + 400;
  }
  var y = offset.top + 20;
  if ((y + $('#helper').height() + 40) > $(document).height()) {
    y = y-$('#helper').height() - 40;
  }
  $("#helper").css('left',x);
  $("#helper").css('top',y);
  $("#helper").fadeIn("normal");
}
function help_text_off(id) {
  $("#helper").fadeOut("normal");
  $('#helper').remove();
}
function preload_field(element, text) {
  $(document).ready(function() {
    $(element).bind("focus", function(e){
      if (this.value == text) {
        this.style.color = "#333333";
        this.value = "";
      }
    });
    $(element).each(function(){
      if (this.value == "") {
        this.style.color = "#bbbbbb";
        this.value = text;
      }
    });
    $('form').submit(function(){
      $(element).each(function(){
        if (this.value == text) {
          this.value = "";
        }
      });
    });
  });
}

function save_recipients(data) {
  var p = data.select.parent();
  var commentID = p.find("input[name=commentID]").val();
  var values = data.select.val();

  jQuery.post("../comment/updateRecipients.php",{ "commentID":commentID, "comment_recipients":values},function(data) {
    p.parent().hide();
    if (data == 'external') {
      var label = '[ External Conversation ]'
      p.parents(".panel").addClass("loud");
    } else {
      var label = '[ Internal Conversation ]'
      p.parents(".panel").removeClass("loud");
    }
    p.parent().parent().find("a.recipient_editor_link").html(label).show();
  });
}

calendar_counter = 0
function refresh_calendars() {
  $(".datefield").each(function(){
    calendar_counter = calendar_counter +1;
    var calendar_id = "calendar_"+calendar_counter;
    var button_id = "calendar_button_"+calendar_counter;
    $(this).attr("id",calendar_id);
    $(this).next("img").attr("id",button_id);

    var settings = {}
    settings["inputField"] = calendar_id;
    settings["ifFormat"] = "%Y-%m-%d";
    settings["button"] = button_id;
    settings["showOthers"] = 1;
    settings["align"] = "Bl";
    settings["firstDay"] = get_alloc_var("cal_first_day");
    settings["step"] = 1;
    settings["weekNumbers"] = 0;
    settings["date"] = $(this).attr("value");
    Calendar.setup(settings);
  });
}

// Preload mouseover images
if (document.images) {
  pic5= new Image(119,13);
  pic5.src="../images/ticker2.gif";
  pic6= new Image(16,16);
  pic6.src="../images/spinner.gif";
}

// When the document has loaded...
$(document).ready(function() {

  // Focus the login form fields
  if ($("#login_form").length) {
    if (!$("#username").val()) {
      $("#username").focus();
    } else {
      $("#password").focus();
    }
  }

  // Enable the dynamic table sorting
  $("table.sortable").each(function() {
    var t = $(this).stupidtable();
    var arr = $("").css("visibility","hidden");
    $('thead > tr > th',this).not(function(){ return $(this).data("sort") == "none"; }).append(arr).css({"cursor":"pointer"});

    t.bind('aftertablesort', function (event, data) {
      var th = $(this).find("th");
      th.find(".arrow").css("visibility","hidden");
      th.eq(data.column).find(".arrow").removeClass("icon-circle-arrow-down icon-circle-arrow-up").addClass(data.direction === "asc" ? "icon-circle-arrow-up" : "icon-circle-arrow-down").css("visibility","visible");
      $("tr:nth-child(even)",this).removeClass("even odd").addClass("even");
      $("tr:nth-child(odd)",this).removeClass("even odd").addClass("odd");
    });
  });

  // Give the tables alternating stripes
  $(".list tr:nth-child(even)").addClass("even");
  $(".list tr:nth-child(odd)").addClass("odd");
  $(".delete_button").on("click", function(e){
    return confirm("Click OK to confirm deletion.");
  });
  $(".confirm_button").bind("click", function(e){
    return confirm("Click OK to confirm.");
  });
  $("input.datefield").bind("dblclick", function(e){
    var now = new Date();
    this.value=now.getFullYear()+'-'+((1e2 + now.getMonth()+1 + '').substr(1))+'-'+(1e2 + now.getDate() + '').substr(1);
  });

  $('tr.clickrow').bind('click',function(e){
    var id = this.id.split('_')[1]; // clickrow_43242
    if (id && !$(e.target).is('input:checkbox') && !$(e.target).is('a') && !$(e.target).is('b')) {
      $('#checkbox_'+id).attr('checked',!$('#checkbox_'+id).attr('checked'));
    }
  });

  // This loads up certain textboxes with faint help text that vanishes upon focus
  preload_field("#menu_form_needle", "Enter search text or ID...");

  $("#search_action").change(function(){
    var bits = $(this).val().split("_");
    if (bits[0] == "create" || bits[0] == "history") {
      $("#form_search").submit();
    }
  });

  // Add resize grippies to all textareas
  $('textarea:not(.processed)').TextAreaResizer();

  // Add toggles for filters
  $(".toggleFilter").click(function(){
    var d = $(".filter").css("display");
    if (d == "table") {
      var l = "Show Filter";
      var d = "none";
    } else {
      var l = "Hide Filter";
      var d = "table";
    }
    $(".filter").css("display",d);
    $(this).text(l);
    redraw_multiple_selects();
    return false;
  });

  // Activate user preference for displaying filters
  if (get_alloc_var("show_filters") != "yes") {
    $(".toggleFilter").trigger("click");
  }

  $("td.calendar_day img").hide();
  $(".alloc_calendar").on('click', 'td.calendar_day' ,function(e){
    $("img",this).toggle();
    $(this).toggleClass("selected");
    return true;
  });

  $('input.toggler').click(function(){
    return $('.task_checkboxes').each(function() {
      this.checked = !this.checked
    });
  });

  // Activate side by side links/tabs, if any
  $(".sidebyside").click(function(e) {
    var redraw = $(this).attr("data-sbs-redraw");
    sidebyside_activate(e.target.id.replace("sbs_link_",""),redraw);
    return false;
  });
  sidebyside_activate();

  $(".edit").hide();

  // Mark up the date fields with dynamic calendars
  refresh_calendars();

  // Interested Parties recipients editor
  $(".recipient_editor_link").click(function(){
    var commentID = this.id.split("_")[2]
    $(this).toggle();
    $("#recipient_dropdown_"+commentID).slideToggle("fast");
    redraw_multiple_selects("recipient_dropdown_"+commentID);
    return false;
  });

  $(".commentreply").click(function(e){
    $("#interested_parties_selector").hide();
    $(this).parents(".pcomment").append($("#id_new_comment").css( { "display" : "inline-table" } ));
    $("#id_new_comment").find("input[name=entity]").val("comment");
    $("#id_new_comment").find("input[name=entityID]").val($(this).parents(".pcomment").attr("data-comment-id"));
    return false;
  });

  $(".commentnew").click(function(e){
    $("#interested_parties_selector").show();
    $("#id_new_comment").find("input[name=entity]").val($("#id_new_comment").find("input[name=commentMaster]").val());
    $("#id_new_comment").find("input[name=entityID]").val($("#id_new_comment").find("input[name=commentMasterID]").val());
    $("#new_comment_container").append($("#id_new_comment").css( { "display" : "inline-table" } ));
    return false;
  });

  $("a.star").on('click',function(){
    if ($("b",$(this)).hasClass('icon-star-empty')) {
      $("b",$(this)).removeClass('icon-star-empty').addClass('icon-star');
      $(this).addClass("hot");
      $(this).closest("td").attr({"data-sort-value":"1"});
    } else {
      $("b",$(this)).removeClass('icon-star hot').addClass('icon-star-empty');
      $(this).removeClass("hot");
      $(this).closest("td").attr({"data-sort-value":"2"});
    }
    $.get($(this).attr("href"));
    return false;
  });

  // This duplicates a button with class=default and puts it first,
  // so that the web browser treats it as the default submission button.
  $('form').each(function () {
    var thisform = $(this);
    thisform.prepend(thisform.find('button.default').clone().css({
      position: 'absolute',
      left: '-999px',
      top: '-999px',
      height: 0,
      width: 0
    }));
  });

  $(".config-link").click(function(){

    var elem = $("."+$(this).attr("id")).css({"position":"absolute"
                                             ,"top":10
                                             ,"right":0
                                             ,"display":"inline"
                                             }).addClass("corner").addClass("shadow");

    // config boxes that have class "lazy" are lazy-loading, i.e. dynamically populated via ajax
    var i = $(this).attr("id");
    if ($("."+i).hasClass("lazy")) {
      $("."+i).html('');
      $.get(get_alloc_var("url")+"home/"+i+".php", function(data) {
        $("."+i).html(data);
        redraw_multiple_selects();
      });
    }

    $(this).parent().append(elem);
    redraw_multiple_selects($(this).parent());
    return false;
  });

  // This is mainly used for the edit comment recipient logic on the comments screens
  // The data-callback is most likely save_recipients(); Go there.
  $("html").bind("selectn-closed",function(e,data){
    if (data.select.attr("data-callback")) {
      window[data.select.attr("data-callback")](data);
    }
  });

});

<commit_msg>reformat zz_alloc.js with esformatter<commit_after>function redraw_multiple_selects(container) {
  var c = container;
  if (typeof container == 'string') {
    c = '#' + container;
  }
  $('select[multiple]', c).selectn('destroy', c);
  $('select[multiple]', c).selectn();
}

// Make the XML request thing, specify the callback function
function refreshProjectList(radiobutton) {
  url = get_alloc_var('url') + 'task/updateProjectList.php?projectType=' + radiobutton.value;
  makeAjaxRequest(url, 'projectListDropdown', '', 1);
}

function add_gst(value) {
  var tax_percent = get_alloc_var('tax_percent');
  return Math.round(value * (tax_percent / 100 + 1) * 100) / 100;
}

function deduct_gst(value) {
  var tax_percent = get_alloc_var('tax_percent');
  return Math.round(value / (tax_percent / 100 + 1) * 100) / 100;
}

function toggle_view_edit(reset) {
  $('.view').toggle();
  $('.edit').toggle();
  if (reset) {
    $(this).parents('form').reset();
  }
  redraw_multiple_selects();
  return false;
}

function makeAjaxRequest(url, entityid, extra_fields, redraw) {
  $('#' + entityid).html('');
  jQuery.get(url, extra_fields, function(data) {
    $('#' + entityid).hide();
    $('#' + entityid).html(data);
    $('#' + entityid).fadeIn('fast');

    // We may need to redraw the select widgets
    if (redraw) {
      redraw_multiple_selects(entityid);
    }
  });
}

// This is a generic show/hide for anything
function set_grow_shrink(id, id_to_hide, use_classes_instead_of_ids) {
  // toggle the other div - if any
  if (use_classes_instead_of_ids && id_to_hide) {
    $('.' + id_to_hide).slideToggle('fast');
  } else if (id_to_hide) {
    $('#' + id_to_hide).slideToggle('fast');
  }
  // hide or show the actual div
  if (use_classes_instead_of_ids) {
    $('.' + id).slideToggle('fast');
  } else {
    $('#' + id).slideToggle('fast');
  }
  return false;
}

function sidebyside_activate(id, redraw) {
  var arr = [];
  $.each($('.sidebyside'), function(k, v) {
    arr[arr.length] = v.id.replace('sbs_link_', '');
  });

  if (!id) {
    id = get_alloc_var('side_by_side_link');
  }
  if (!id) {
    id = arr[0];
  }

  if (id == 'sbsAll') {
    for (var i = 0; i < arr.length; i++) {
      if (arr[i] != 'sbsAll') {
        $('#' + arr[i]).show();
        $('#sbs_link_' + arr[i]).removeClass('sidebyside_active').addClass('sidebyside');
      }
    }
    $('#sbs_link_' + id).addClass('sidebyside_active');

  } else {
    for (var i = 0; i < arr.length; i++) {
      if (arr[i] != 'sbsAll') {
        $('#' + arr[i]).hide();
      }
      $('#sbs_link_' + arr[i]).removeClass('sidebyside_active').addClass('sidebyside');
    }
    $('#sbs_link_' + id).addClass('sidebyside_active');
    $('#' + id).show();
  }

  // allows us to target particular pages for redraw_multiple_selects();
  redraw_multiple_selects();
}

function help_text_on(id, str) {
  $('#main').append("

");
  $('#helper').html(str).hide();
  var offset = $('#' + id).position();
  var x = offset.left - 420;
  if (x < 0) {
    x = x + 400;
  }
  var y = offset.top + 20;
  if ((y + $('#helper').height() + 40) > $(document).height()) {
    y = y - $('#helper').height() - 40;
  }
  $('#helper').css('left', x);
  $('#helper').css('top', y);
  $('#helper').fadeIn('normal');
}
function help_text_off(id) {
  $('#helper').fadeOut('normal');
  $('#helper').remove();
}
function preload_field(element, text) {
  $(document).ready(function() {
    $(element).bind('focus', function(e) {
      if (this.value == text) {
        this.style.color = '#333333';
        this.value = '';
      }
    });
    $(element).each(function() {
      if (this.value == '') {
        this.style.color = '#bbbbbb';
        this.value = text;
      }
    });
    $('form').submit(function() {
      $(element).each(function() {
        if (this.value == text) {
          this.value = '';
        }
      });
    });
  });
}

function save_recipients(data) {
  var p = data.select.parent();
  var commentID = p.find('input[name=commentID]').val();
  var values = data.select.val();

  jQuery.post('../comment/updateRecipients.php', {
    'commentID': commentID,
    'comment_recipients': values
  }, function(data) {
    p.parent().hide();
    if (data == 'external') {
      var label = '[ External Conversation ]';
      p.parents('.panel').addClass('loud');
    } else {
      var label = '[ Internal Conversation ]';
      p.parents('.panel').removeClass('loud');
    }
    p.parent().parent().find('a.recipient_editor_link').html(label).show();
  });
}

calendar_counter = 0;
function refresh_calendars() {
  $('.datefield').each(function() {
    calendar_counter = calendar_counter + 1;
    var calendar_id = 'calendar_' + calendar_counter;
    var button_id = 'calendar_button_' + calendar_counter;
    $(this).attr('id', calendar_id);
    $(this).next('img').attr('id', button_id);

    var settings = {};
    settings.inputField = calendar_id;
    settings.ifFormat = '%Y-%m-%d';
    settings.button = button_id;
    settings.showOthers = 1;
    settings.align = 'Bl';
    settings.firstDay = get_alloc_var('cal_first_day');
    settings.step = 1;
    settings.weekNumbers = 0;
    settings.date = $(this).attr('value');
    Calendar.setup(settings);
  });
}

// Preload mouseover images
if (document.images) {
  pic5 = new Image(119, 13);
  pic5.src = '../images/ticker2.gif';
  pic6 = new Image(16, 16);
  pic6.src = '../images/spinner.gif';
}

// When the document has loaded...
$(document).ready(function() {

  // Focus the login form fields
  if ($('#login_form').length) {
    if (!$('#username').val()) {
      $('#username').focus();
    } else {
      $('#password').focus();
    }
  }

  // Enable the dynamic table sorting
  $('table.sortable').each(function() {
    var t = $(this).stupidtable();
    var arr = $("").css('visibility', 'hidden');
    $('thead > tr > th', this).not(function() {
      return $(this).data('sort') == 'none';
    }).append(arr).css({
      'cursor': 'pointer'
    });

    t.bind('aftertablesort', function(event, data) {
      var th = $(this).find('th');
      th.find('.arrow').css('visibility', 'hidden');
      th.eq(data.column).find('.arrow').removeClass('icon-circle-arrow-down icon-circle-arrow-up').addClass(data.direction === 'asc' ? 'icon-circle-arrow-up' : 'icon-circle-arrow-down').css('visibility', 'visible');
      $('tr:nth-child(even)', this).removeClass('even odd').addClass('even');
      $('tr:nth-child(odd)', this).removeClass('even odd').addClass('odd');
    });
  });

  // Give the tables alternating stripes
  $('.list tr:nth-child(even)').addClass('even');
  $('.list tr:nth-child(odd)').addClass('odd');
  $('.delete_button').on('click', function(e) {
    return confirm('Click OK to confirm deletion.');
  });
  $('.confirm_button').bind('click', function(e) {
    return confirm('Click OK to confirm.');
  });
  $('input.datefield').bind('dblclick', function(e) {
    var now = new Date();
    this.value = now.getFullYear() + '-' + ((1e2 + now.getMonth() + 1 + '').substr(1)) + '-' + (1e2 + now.getDate() + '').substr(1);
  });

  $('tr.clickrow').bind('click', function(e) {
    var id = this.id.split('_')[1]; // clickrow_43242
    if (id && !$(e.target).is('input:checkbox') && !$(e.target).is('a') && !$(e.target).is('b')) {
      $('#checkbox_' + id).attr('checked', !$('#checkbox_' + id).attr('checked'));
    }
  });

  // This loads up certain textboxes with faint help text that vanishes upon focus
  preload_field('#menu_form_needle', 'Enter search text or ID...');

  $('#search_action').change(function() {
    var bits = $(this).val().split('_');
    if (bits[0] == 'create' || bits[0] == 'history') {
      $('#form_search').submit();
    }
  });

  // Add resize grippies to all textareas
  $('textarea:not(.processed)').TextAreaResizer();

  // Add toggles for filters
  $('.toggleFilter').click(function() {
    var d = $('.filter').css('display');
    if (d == 'table') {
      var l = 'Show Filter';
      var d = 'none';
    } else {
      var l = 'Hide Filter';
      var d = 'table';
    }
    $('.filter').css('display', d);
    $(this).text(l);
    redraw_multiple_selects();
    return false;
  });

  // Activate user preference for displaying filters
  if (get_alloc_var('show_filters') != 'yes') {
    $('.toggleFilter').trigger('click');
  }

  $('td.calendar_day img').hide();
  $('.alloc_calendar').on('click', 'td.calendar_day', function(e) {
    $('img', this).toggle();
    $(this).toggleClass('selected');
    return true;
  });

  $('input.toggler').click(function() {
    return $('.task_checkboxes').each(function() {
      this.checked = !this.checked;
    });
  });

  // Activate side by side links/tabs, if any
  $('.sidebyside').click(function(e) {
    var redraw = $(this).attr('data-sbs-redraw');
    sidebyside_activate(e.target.id.replace('sbs_link_', ''), redraw);
    return false;
  });
  sidebyside_activate();

  $('.edit').hide();

  // Mark up the date fields with dynamic calendars
  refresh_calendars();

  // Interested Parties recipients editor
  $('.recipient_editor_link').click(function() {
    var commentID = this.id.split('_')[2];
    $(this).toggle();
    $('#recipient_dropdown_' + commentID).slideToggle('fast');
    redraw_multiple_selects('recipient_dropdown_' + commentID);
    return false;
  });

  $('.commentreply').click(function(e) {
    $('#interested_parties_selector').hide();
    $(this).parents('.pcomment').append($('#id_new_comment').css({
      'display': 'inline-table'
    }));
    $('#id_new_comment').find('input[name=entity]').val('comment');
    $('#id_new_comment').find('input[name=entityID]').val($(this).parents('.pcomment').attr('data-comment-id'));
    return false;
  });

  $('.commentnew').click(function(e) {
    $('#interested_parties_selector').show();
    $('#id_new_comment').find('input[name=entity]').val($('#id_new_comment').find('input[name=commentMaster]').val());
    $('#id_new_comment').find('input[name=entityID]').val($('#id_new_comment').find('input[name=commentMasterID]').val());
    $('#new_comment_container').append($('#id_new_comment').css({
      'display': 'inline-table'
    }));
    return false;
  });

  $('a.star').on('click', function() {
    if ($('b', $(this)).hasClass('icon-star-empty')) {
      $('b', $(this)).removeClass('icon-star-empty').addClass('icon-star');
      $(this).addClass('hot');
      $(this).closest('td').attr({
        'data-sort-value': '1'
      });
    } else {
      $('b', $(this)).removeClass('icon-star hot').addClass('icon-star-empty');
      $(this).removeClass('hot');
      $(this).closest('td').attr({
        'data-sort-value': '2'
      });
    }
    $.get($(this).attr('href'));
    return false;
  });

  // This duplicates a button with class=default and puts it first,
  // so that the web browser treats it as the default submission button.
  $('form').each(function() {
    var thisform = $(this);
    thisform.prepend(thisform.find('button.default').clone().css({
      position: 'absolute',
      left: '-999px',
      top: '-999px',
      height: 0,
      width: 0
    }));
  });

  $('.config-link').click(function() {

    var elem = $('.' + $(this).attr('id')).css({
      'position': 'absolute',
      'top': 10,
      'right': 0,
      'display': 'inline'
    }).addClass('corner').addClass('shadow');

    // config boxes that have class "lazy" are lazy-loading, i.e. dynamically populated via ajax
    var i = $(this).attr('id');
    if ($('.' + i).hasClass('lazy')) {
      $('.' + i).html('');
      $.get(get_alloc_var('url') + 'home/' + i + '.php', function(data) {
        $('.' + i).html(data);
        redraw_multiple_selects();
      });
    }

    $(this).parent().append(elem);
    redraw_multiple_selects($(this).parent());
    return false;
  });

  // This is mainly used for the edit comment recipient logic on the comments screens
  // The data-callback is most likely save_recipients(); Go there.
  $('html').bind('selectn-closed', function(e, data) {
    if (data.select.attr('data-callback')) {
      window[data.select.attr('data-callback')](data);
    }
  });

});

<|endoftext|>


<commit_before>// Global Ajax Setup
var globalAjaxOptions = {};
$.ajaxSetup = function (options) {
    if (options.type) options.method = options.type;
    for (var option in options) {
        globalAjaxOptions[option]  = options[option];
    }
};

// Ajax
var _jsonpRequests = 0;
$.ajax = function (options) {
    var defaults = {
        method: 'GET',
        data: false,
        async: true,
        cache: true,
        user: '',
        password: '',
        headers: {},
        xhrFields: {},
        statusCode: {},
        processData: true,
        dataType: 'text',
        contentType: 'application/x-www-form-urlencoded',
        timeout: 0
    };

    /*==
    Available callbacks options:
    beforeSend (start)
    error
    complete
    success
    statusCode - object with functions
    ==*/

    //For jQuery guys
    if (options.type) options.method = options.type;

    // Merge global and defaults
    for (var globalOption in globalAjaxOptions) {
        defaults[globalOption] = globalAjaxOptions[globalOption];
    }

    // Merge options and defaults
    for (var prop in defaults) {
        if (!(prop in options)) options[prop] = defaults[prop];
    }

    // Default URL
    if (!options.url) {
        options.url = window.location.toString();
    }
    // UC method
    var _method = options.method.toUpperCase();
    // Data to modify GET URL
    if ((_method === 'GET' || _method === 'HEAD') && options.data) {
        var stringData;
        if (typeof options.data === 'string') {
            // Should be key=value string
            if (options.data.indexOf('?') >= 0) stringData = options.data.split('?')[1];
            else stringData = options.data;
        }
        else {
            // Should be key=value object
            stringData = $.serializeObject(options.data);
        }
        if (options.url.indexOf('?') >= 0) options.url += '&' + stringData;
        else options.url += '?' + stringData;
    }
    // JSONP
    if (options.dataType === 'json' && options.url.indexOf('callback=') >= 0) {

        var callbackName = 'f7jsonp_' + Date.now() + (_jsonpRequests++);
        var requestUrl, abortTimeout;
        var callbackSplit = options.url.split('callback=');
        if (callbackSplit[1].indexOf('&') >= 0) {
            var addVars = callbackSplit[1].split('&').filter(function (el) { return el.indexOf('=') > 0; }).join('&');
            requestUrl = callbackSplit[0] + 'callback=' + callbackName + (addVars.length > 0 ? '&' + addVars : '');
        }
        else {
            requestUrl = callbackSplit[0] + 'callback=' + callbackName;
        }

        // Create script
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.onerror = function() {
            clearTimeout(abortTimeout);
            if (options.error) options.error();
        };
        script.src = requestUrl;

        // Handler
        window[callbackName] = function (data) {
            clearTimeout(abortTimeout);
            if (options.success) options.success(data);
            script.parentNode.removeChild(script);
            script = null;
            delete window[callbackName];
        };
        document.querySelector('head').appendChild(script);

        if (options.timeout > 0) {
            abortTimeout = setTimeout(function () {
                script.parentNode.removeChild(script);
                script = null;
                if (options.error) options.error();
            }, options.timeout);
        }

        return;
    }

    // Cache for GET/HEAD requests
    if (_method === 'GET' || _method === 'HEAD') {
        if (options.cache === false) options.url += ('_nocache=' + Date.now());
    }

    // Create XHR
    var xhr = new XMLHttpRequest();

    // Save Request URL
    xhr.requestUrl = options.url;

    // Open XHR
    xhr.open(_method, options.url, options.async, options.user, options.password);

    // Create POST Data
    var postData = null;

    if ((_method === 'POST' || _method === 'PUT') && options.data) {
        if (options.processData) {
            var postDataInstances = [ArrayBuffer, Blob, Document, FormData];
            // Post Data
            if (postDataInstances.indexOf(options.data.constructor) >= 0) {
                postData = options.data;
            }
            else {
                // POST Headers
                var boundary = '---------------------------' + Date.now().toString(16);

                if (options.contentType === 'multipart\/form-data') {
                    xhr.setRequestHeader('Content-Type', 'multipart\/form-data; boundary=' + boundary);
                }
                else {
                    xhr.setRequestHeader('Content-Type', options.contentType);
                }
                postData = '';
                var _data = $.serializeObject(options.data);
                if (options.contentType === 'multipart\/form-data') {
                    boundary = '---------------------------' + Date.now().toString(16);
                    _data = _data.split('&');
                    var _newData = [];
                    for (var i = 0; i < _data.length; i++) {
                        _newData.push('Content-Disposition: form-data; name="' + _data[i].split('=')[0] + '"\r\n\r\n' + _data[i].split('=')[1] + '\r\n');
                    }
                    postData = '--' + boundary + '\r\n' + _newData.join('--' + boundary + '\r\n') + '--' + boundary + '--\r\n';
                }
                else {
                    postData = options.contentType === 'application/x-www-form-urlencoded' ? _data : _data.replace(/&/g, '\r\n');
                }
            }
        }
        else {
            postData = options.data;
        }

    }

    // Additional headers
    if (options.headers) {
        for (var header in options.headers) {
            xhr.setRequestHeader(header, options.headers[header]);
        }
    }

    // Check for crossDomain
    if (typeof options.crossDomain === 'undefined') {
        options.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(options.url) && RegExp.$2 !== window.location.host;
    }

    if (!options.crossDomain) {
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    }

    if (options.xhrFields) {
        for (var field in options.xhrFields) {
            xhr[field] = options.xhrFields[field];
        }
    }

    var xhrTimeout;
    // Handle XHR
    xhr.onload = function (e) {
        if (xhrTimeout) clearTimeout(xhrTimeout);
        if (xhr.status === 200 || xhr.status === 0) {
            $(document).trigger('ajaxSuccess', {xhr: xhr});
            if (options.success) {
                var responseData = xhr.responseText;

                if (options.dataType === 'json') responseData = JSON.parse(responseData);
                options.success(responseData, xhr.status, xhr);
            }
        }
        else {
            $(document).trigger('ajaxError', {xhr: xhr});
            if (options.error) options.error(xhr, xhr.status);
        }
        if (options.statusCode) {
            if (options.statusCode[xhr.status]) options.statusCode[xhr.status](xhr);
        }
        $(document).trigger('ajaxComplete', {xhr: xhr});
        if (options.complete) {
            options.complete(xhr, xhr.status);
        }
    };

    xhr.onerror = function (e) {
        if (xhrTimeout) clearTimeout(xhrTimeout);
        $(document).trigger('ajaxError', {xhr: xhr});
        if (options.error) options.error(xhr, xhr.status);
    };

    // Ajax start callback
    $(document).trigger('ajaxStart', {xhr: xhr});
    if (options.start) options.start(xhr);
    if (options.beforeSend) options.beforeSend(xhr);

    // Send XHR
    xhr.send(postData);

    // Timeout
    if (options.timeout > 0) {
        xhrTimeout = setTimeout(function () {
            xhr.abort();
            if (options.complete) options.complete(xhr, 'timeout');
            if (options.error) options.error(xhr, 'timeout');
            $(document).trigger('ajaxComplete', {xhr: xhr, timeout: true});
            $(document).trigger('ajaxError', {xhr: xhr, timeout: true});
        }, options.timeout);
    }

    // Return XHR object
    return xhr;
};
// Shrotcuts
(function () {
    var methods = ('get post getJSON').split(' ');
    function createMethod(method) {
        $[method] = function (url, data, success) {
            return $.ajax({
                url: url,
                method: method === 'post' ? 'POST' : 'GET',
                data: typeof data === 'function' ? undefined : data,
                success: typeof data === 'function' ? data : success,
                dataType: method === 'getJSON' ? 'json' : undefined
            });
        };
    }
    for (var i = 0; i < methods.length; i++) {
        createMethod(methods[i]);
    }
})();<commit_msg>Catch JSON error and global ajax callbacks<commit_after>// Global Ajax Setup
var globalAjaxOptions = {};
$.ajaxSetup = function (options) {
    if (options.type) options.method = options.type;
    for (var option in options) {
        globalAjaxOptions[option]  = options[option];
    }
};

// Ajax
var _jsonpRequests = 0;
$.ajax = function (options) {
    var defaults = {
        method: 'GET',
        data: false,
        async: true,
        cache: true,
        user: '',
        password: '',
        headers: {},
        xhrFields: {},
        statusCode: {},
        processData: true,
        dataType: 'text',
        contentType: 'application/x-www-form-urlencoded',
        timeout: 0
    };
    var callbacks = ['beforeSend', 'error', 'complete', 'success', 'statusCode'];

    //For jQuery guys
    if (options.type) options.method = options.type;

    // Merge global and defaults
    for (var globalOption in globalAjaxOptions) {
        if (callbacks.indexOf(globalOption) < 0) defaults[globalOption] = globalAjaxOptions[globalOption];
    }
    // Global callbacks
    function globalAjaxCallback(callback) {
        if (callback in globalAjaxOptions) globalAjaxOptions[callback](arguments[1], arguments[2], arguments[3], arguments[4]);
    }

    // Merge options and defaults
    for (var prop in defaults) {
        if (!(prop in options)) options[prop] = defaults[prop];
    }

    // Default URL
    if (!options.url) {
        options.url = window.location.toString();
    }
    // UC method
    var _method = options.method.toUpperCase();
    // Data to modify GET URL
    if ((_method === 'GET' || _method === 'HEAD') && options.data) {
        var stringData;
        if (typeof options.data === 'string') {
            // Should be key=value string
            if (options.data.indexOf('?') >= 0) stringData = options.data.split('?')[1];
            else stringData = options.data;
        }
        else {
            // Should be key=value object
            stringData = $.serializeObject(options.data);
        }
        if (options.url.indexOf('?') >= 0) options.url += '&' + stringData;
        else options.url += '?' + stringData;
    }
    // JSONP
    if (options.dataType === 'json' && options.url.indexOf('callback=') >= 0) {

        var callbackName = 'f7jsonp_' + Date.now() + (_jsonpRequests++);
        var requestUrl, abortTimeout;
        var callbackSplit = options.url.split('callback=');
        if (callbackSplit[1].indexOf('&') >= 0) {
            var addVars = callbackSplit[1].split('&').filter(function (el) { return el.indexOf('=') > 0; }).join('&');
            requestUrl = callbackSplit[0] + 'callback=' + callbackName + (addVars.length > 0 ? '&' + addVars : '');
        }
        else {
            requestUrl = callbackSplit[0] + 'callback=' + callbackName;
        }

        // Create script
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.onerror = function() {
            clearTimeout(abortTimeout);
            globalAjaxCallback('error', null, 'scripterror');
            if (options.error) options.error();
        };
        script.src = requestUrl;

        // Handler
        window[callbackName] = function (data) {
            clearTimeout(abortTimeout);
            globalAjaxCallback('success', data);
            if (options.success) options.success(data);
            script.parentNode.removeChild(script);
            script = null;
            delete window[callbackName];
        };
        document.querySelector('head').appendChild(script);

        if (options.timeout > 0) {
            abortTimeout = setTimeout(function () {
                script.parentNode.removeChild(script);
                script = null;
                globalAjaxCallback('error', null, 'timeout');
                if (options.error) options.error(null, 'timeout');
            }, options.timeout);
        }

        return;
    }

    // Cache for GET/HEAD requests
    if (_method === 'GET' || _method === 'HEAD') {
        if (options.cache === false) options.url += ('_nocache=' + Date.now());
    }

    // Create XHR
    var xhr = new XMLHttpRequest();

    // Save Request URL
    xhr.requestUrl = options.url;

    // Open XHR
    xhr.open(_method, options.url, options.async, options.user, options.password);

    // Create POST Data
    var postData = null;

    if ((_method === 'POST' || _method === 'PUT') && options.data) {
        if (options.processData) {
            var postDataInstances = [ArrayBuffer, Blob, Document, FormData];
            // Post Data
            if (postDataInstances.indexOf(options.data.constructor) >= 0) {
                postData = options.data;
            }
            else {
                // POST Headers
                var boundary = '---------------------------' + Date.now().toString(16);

                if (options.contentType === 'multipart\/form-data') {
                    xhr.setRequestHeader('Content-Type', 'multipart\/form-data; boundary=' + boundary);
                }
                else {
                    xhr.setRequestHeader('Content-Type', options.contentType);
                }
                postData = '';
                var _data = $.serializeObject(options.data);
                if (options.contentType === 'multipart\/form-data') {
                    boundary = '---------------------------' + Date.now().toString(16);
                    _data = _data.split('&');
                    var _newData = [];
                    for (var i = 0; i < _data.length; i++) {
                        _newData.push('Content-Disposition: form-data; name="' + _data[i].split('=')[0] + '"\r\n\r\n' + _data[i].split('=')[1] + '\r\n');
                    }
                    postData = '--' + boundary + '\r\n' + _newData.join('--' + boundary + '\r\n') + '--' + boundary + '--\r\n';
                }
                else {
                    postData = options.contentType === 'application/x-www-form-urlencoded' ? _data : _data.replace(/&/g, '\r\n');
                }
            }
        }
        else {
            postData = options.data;
        }

    }

    // Additional headers
    if (options.headers) {
        for (var header in options.headers) {
            xhr.setRequestHeader(header, options.headers[header]);
        }
    }

    // Check for crossDomain
    if (typeof options.crossDomain === 'undefined') {
        options.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(options.url) && RegExp.$2 !== window.location.host;
    }

    if (!options.crossDomain) {
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    }

    if (options.xhrFields) {
        for (var field in options.xhrFields) {
            xhr[field] = options.xhrFields[field];
        }
    }

    var xhrTimeout;
    // Handle XHR
    xhr.onload = function (e) {
        if (xhrTimeout) clearTimeout(xhrTimeout);
        if (xhr.status === 200 || xhr.status === 0) {
            var isSuccess, responseData;
            if (options.dataType === 'json') {
                try {
                    responseData = JSON.parse(xhr.responseText);
                    $(document).trigger('ajaxSuccess', {xhr: xhr});
                    globalAjaxCallback('success', responseData, xhr.status, xhr);
                    if (options.success) {
                        options.success(responseData, xhr.status, xhr);
                    }
                }
                catch (e) {
                    $(document).trigger('ajaxError', {xhr: xhr, parseerror: true});
                    globalAjaxCallback('error', xhr, 'parseerror');
                    if (options.error) options.error(xhr, 'parseerror');
                }
            }
            else {
                $(document).trigger('ajaxSuccess', {xhr: xhr});
                globalAjaxCallback('success', responseData, xhr.status, xhr);
                if (options.success) {
                    responseData = xhr.responseText;
                    options.success(responseData, xhr.status, xhr);
                }
            }
        }
        else {
            $(document).trigger('ajaxError', {xhr: xhr});
            globalAjaxCallback('error', xhr, xhr.status);
            if (options.error) options.error(xhr, xhr.status);
        }
        if (options.statusCode) {
            if (globalAjaxOptions.statusCode && globalAjaxOptions.statusCode[xhr.status]) globalAjaxOptions.statusCode[xhr.status](xhr);
            if (options.statusCode[xhr.status]) options.statusCode[xhr.status](xhr);
        }
        $(document).trigger('ajaxComplete', {xhr: xhr});
        globalAjaxCallback('complete', xhr, xhr.status);
        if (options.complete) {
            options.complete(xhr, xhr.status);
        }
    };

    xhr.onerror = function (e) {
        if (xhrTimeout) clearTimeout(xhrTimeout);
        $(document).trigger('ajaxError', {xhr: xhr});
        globalAjaxCallback('error', xhr, xhr.status);
        if (options.error) options.error(xhr, xhr.status);
    };

    // Ajax start callback
    $(document).trigger('ajaxStart', {xhr: xhr});
    globalAjaxCallback('start', xhr);
    globalAjaxCallback('beforeSend', xhr);
    if (options.start) options.start(xhr);
    if (options.beforeSend) options.beforeSend(xhr);

    // Send XHR
    xhr.send(postData);

    // Timeout
    if (options.timeout > 0) {
        xhrTimeout = setTimeout(function () {
            xhr.abort();
            $(document).trigger('ajaxError', {xhr: xhr, timeout: true});
            $(document).trigger('ajaxComplete', {xhr: xhr, timeout: true});
            globalAjaxCallback('error', xhr, 'timeout');
            globalAjaxCallback('complete', xhr, 'timeout');
            if (options.complete) options.complete(xhr, 'timeout');
            if (options.error) options.error(xhr, 'timeout');
        }, options.timeout);
    }

    // Return XHR object
    return xhr;
};
// Shrotcuts
(function () {
    var methods = ('get post getJSON').split(' ');
    function createMethod(method) {
        $[method] = function (url, data, success) {
            return $.ajax({
                url: url,
                method: method === 'post' ? 'POST' : 'GET',
                data: typeof data === 'function' ? undefined : data,
                success: typeof data === 'function' ? data : success,
                dataType: method === 'getJSON' ? 'json' : undefined
            });
        };
    }
    for (var i = 0; i < methods.length; i++) {
        createMethod(methods[i]);
    }
})();<|endoftext|>


<commit_before>function focusedSection() {
  var pos = $(document).scrollTop() + $(window).height()/2;
  var sections = $('section')
  for(var i = 0; i<sections.length; i++) {
    if(sections[i].offsetTop > pos) {
      return sections[Math.max(i-1, 0)];
    }
  }
  return sections.last()[0];
}

function updateSelected() {
  var hash = window.location.hash.substring(1)
  if(hash) { $('body').attr('data-selected', hash); }
}

function setSelected(section) {
  var selected = $('body').attr('data-selected');
  if(selected != section) {
    history.replaceState(undefined, undefined, "#" + section);
  }
  $('body').attr('data-selected', section);
}

$(document).ready(function() {
  $('nav a').click(function() {
    setSelected($(this).attr('href').substring(1));
    return false;
  });

  $(window).scroll(function() {
    if($(document).width() <= 960) {
      setSelected(focusedSection().id);
    }
  });

  $(window).on('hashchange', updateSelected);
});<commit_msg>Enable google analytics<commit_after>// Google Analytics
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-25043089-1', 'auto');
ga('send', 'pageview');

function focusedSection() {
  var pos = $(document).scrollTop() + $(window).height()/2;
  var sections = $('section')
  for(var i = 0; i<sections.length; i++) {
    if(sections[i].offsetTop > pos) {
      return sections[Math.max(i-1, 0)];
    }
  }
  return sections.last()[0];
}

function updateSelected() {
  var hash = window.location.hash.substring(1)
  if(hash) { $('body').attr('data-selected', hash); }
}

function setSelected(section) {
  var selected = $('body').attr('data-selected');
  if(selected != section) {
    history.replaceState(undefined, undefined, "#" + section);
  }
  $('body').attr('data-selected', section);
  ga('send', 'pageview', section);
}

$(document).ready(function() {
  $('nav a').click(function() {
    setSelected($(this).attr('href').substring(1));
    return false;
  });

  $(window).scroll(function() {
    if($(document).width() <= 960) {
      setSelected(focusedSection().id);
    }
  });

  $(window).on('hashchange', updateSelected);
});
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var Async = require('async');

var Util = require('./util.js');

var BaseType = module.exports = function(spec) {
  if (spec) this.spec = spec;
  this.type = "base_type";
  this.converters = {};
}

BaseType.prototype.stringify = function() {
  return JSON.stringify(this.spec, null, 2);
}

BaseType.prototype.parsers = [];
BaseType.prototype.checkFormat = undefined;
BaseType.prototype.fixSpec = function () {
};
BaseType.prototype.resolveSubResources = function (source, sourceType, callback) {
  callback(null);
};

BaseType.prototype.parse = function (data, callback) {
  var self = this;
  if (!_.isString(data))
    callback(null, data);

  var result;
  var errMsg = 'Fail to parse spec:\n';
  Async.detect(self.parsers, function (parser, asyncCb) {
    parser(data, function (err, spec) {
      if (err)
        errMsg += '\t' + err;
      else
        result = spec;
      asyncCb(!err);
    });
  }, function (isParsed) {
    if (isParsed)
      callback(null, result);
    else
      callback(Error(errMsg));
  });
}

BaseType.prototype.readSpec = function (source, callback) {
  var self = this;
  var sourceType = Util.getSourceType(source);

  if (!sourceType)
    callback(Error('Spec source should be object, string, URL or filename.'));

  Util.resourceReaders[sourceType](source, function(err, data) {
    if (err) return callback(err);

    self.parse(data, function (err, spec) {
      if (err) return callback(err);

      callback(err, spec, sourceType);
    });
  });
}

BaseType.prototype.resolveResources = function(source, callback) {
  var self = this;

  self.readSpec(source, function(err, spec, sourceType) {
    if (err) return callback(err);

    if (!self.checkFormat(spec))
      return callback(Error('Incorrect format'));

    self.spec = spec;

    //In make cases URL has some usefull info
    if (sourceType === 'url')
      self.url = source;

    self.resolveSubResources(source, sourceType, function (error) {
      if (err) return callback(err);

      self.fixSpec();

      var version = self.getFormatVersion();
      if (self.supportedVersions.indexOf(version) === -1)
        return callback(Error('Unsupported version'));

      callback();
    });
  });
}

BaseType.prototype.convertTo = function(type, callback) {
  if (type === this.type)
    return callback(null, this);

  var convert = this.converters[type];
  if (!convert) {
    throw new Error("Unable to convert from " + this.type + " to " + type);
  }
  var converted = convert(this, callback);
  return converted;
}
<commit_msg>Fix error propagation<commit_after>'use strict';

var _ = require('lodash');
var Async = require('async');

var Util = require('./util.js');

var BaseType = module.exports = function(spec) {
  if (spec) this.spec = spec;
  this.type = "base_type";
  this.converters = {};
}

BaseType.prototype.stringify = function() {
  return JSON.stringify(this.spec, null, 2);
}

BaseType.prototype.parsers = [];
BaseType.prototype.checkFormat = undefined;
BaseType.prototype.fixSpec = function () {
};
BaseType.prototype.resolveSubResources = function (source, sourceType, callback) {
  callback(null);
};

BaseType.prototype.parse = function (data, callback) {
  var self = this;
  if (!_.isString(data))
    callback(null, data);

  var result;
  var errMsg = 'Fail to parse spec:\n';
  Async.detect(self.parsers, function (parser, asyncCb) {
    parser(data, function (err, spec) {
      if (err)
        errMsg += '\t' + err;
      else
        result = spec;
      asyncCb(!err);
    });
  }, function (isParsed) {
    if (isParsed)
      callback(null, result);
    else
      callback(Error(errMsg));
  });
}

BaseType.prototype.readSpec = function (source, callback) {
  var self = this;
  var sourceType = Util.getSourceType(source);

  if (!sourceType)
    callback(Error('Spec source should be object, string, URL or filename.'));

  Util.resourceReaders[sourceType](source, function(err, data) {
    if (err) return callback(err);

    self.parse(data, function (err, spec) {
      if (err) return callback(err);

      callback(err, spec, sourceType);
    });
  });
}

BaseType.prototype.resolveResources = function(source, callback) {
  var self = this;

  self.readSpec(source, function(err, spec, sourceType) {
    if (err) return callback(err);

    if (!self.checkFormat(spec))
      return callback(Error('Incorrect format'));

    self.spec = spec;

    //In make cases URL has some usefull info
    if (sourceType === 'url')
      self.url = source;

    self.resolveSubResources(source, sourceType, function (error) {
      if (error) return callback(error);

      self.fixSpec();

      var version = self.getFormatVersion();
      if (self.supportedVersions.indexOf(version) === -1)
        return callback(Error('Unsupported version'));

      callback();
    });
  });
}

BaseType.prototype.convertTo = function(type, callback) {
  if (type === this.type)
    return callback(null, this);

  var convert = this.converters[type];
  if (!convert) {
    throw new Error("Unable to convert from " + this.type + " to " + type);
  }
  var converted = convert(this, callback);
  return converted;
}
<|endoftext|>


<commit_before>'use strict'

/**
 * Module dependencies
 */
var DEP = require('./dep')
var toArray = require('lodash.toarray')
var foreach = require('lodash.foreach')
var indexOf = require('lodash.indexof')
var Injector = require('insider')
var isPlainObject = require('lodash.isplainobject')
var ERROR_NAME = 'error'

var setDepWithState = exports.setDepWithState = function(state, name, dep, props) {
  var next = state.next
  var result = state.result
  var setDep = state.setDep
  var supplies = state.supplies
  var setDepMap = state.setDepMap
  var fulfilled = state.fulfilled
  var fnReturned = state.fnReturned

  var normalized = DEP.normalizeSetDepArgs(name, dep, props)
  var _dep = normalized.dep
  var _name = normalized.name
  var _props = normalized.props

  // Error happens in previous setDep call return to avoid call error handler twice.
  if (state.hasError)
    return

  if (_name && !_props) {
    var checkName = _name
    var mappedName = setDepMap && setDepMap[_name]
    if (mappedName) {
      // Check against the mapping name
      checkName = _name + ':' + mappedName
      // Set the mappedName as the real dependency name
      _name = mappedName
    }
    checkFulfillment(checkName, state, supplies, fulfilled)
    setDep(_name, _dep)
  } else if (_dep && _props) {
    foreach(_props, function(propName, idx) {
      var nameTOCheck = propName
      var mappedName = setDepMap && setDepMap[propName]
      if (mappedName) {
        // Check against the mapping name
        nameTOCheck = propName + ':' + mappedName
        // Link the propName to the mappedName in _dep
        _dep[mappedName] = _dep[propName]
        // Set the original propName to mappedName so we set `mappedName` as
        // depenency name instead of `propName`
        _props[idx] = mappedName
      }
      checkFulfillment(nameTOCheck, state, supplies, fulfilled)
    })
    setDep(_dep, _props)
  } else {
    throw new Error('Unsupported arguments for in pipe `setDep`.')
  }

  // Call next immediately if there is an error
  if (state.hasError) {
    state.autoNext = false
    next()
  } else if (fulfilled) {
    if (fulfilled.length === supplies.length) {
      // Set the auto next to true or call next when all required
      // supplies are fulfilled.
      if (fnReturned && ('undefined' === typeof result || false !== result))
        // Function has been returned.
        // We should call next when the returned value is either
        // undefined or not false.
        next()
      else
        // Otherwise, let other part of the code to handle when to call next.
        state.autoNext = true
    } else if (fulfilled.length > supplies.length) {
      throw new Error('Got more dependencies than what supplies required.')
    }
  }
}

var executePipe = exports.executePipe = function(err, args, pipe, next, setDep, injector) {
  var fn = pipe.fn
  var deps = pipe.deps
  var fnName = pipe.fnName
  var supplies = pipe.supplies

  var state = {
    next: next,
    setDep: setDep,
    result: undefined,
    supplies: supplies,
    // No deps or has deps but next is not required as dependency.
    autoNext: !deps || -1 === indexOf(deps, 'next'),
    setDepMap: pipe.setDepMap,
    fnReturned: false
  }

  // Get the pipe function demanded from dependency container.
  if ('string' === typeof fnName)
    // Set it as the original pipe function
    pipe.ofn = injector.get(fnName)

  if (pipe.optional) {
    // Optional pipe, go next if we can't get all dependencies.
    var _deps = injector.getAll(deps)
    if (-1 < _deps.indexOf(undefined)) {
      next()
      return
    }
  }

  var modifiedSetDep
  if (supplies && 0 < supplies.length) {
    if (true === state.autoNext) {
      // Only track dependencies when autoNext is true.
      state.fulfilled = []
      // We will handle the auto next behaviour in setDepWithState function.
      state.autoNext = false
    }
    // Call customized setDep function instead.
    modifiedSetDep = function(name, dep, props) {
      setDepWithState(state, name, dep, props)
    }
    injector.set('setDep', modifiedSetDep)
  }

  // Run the actual function
  var _args = injector.getAll(deps, args)
  // Ensure to put back the original `setDep` function if modified.
  if (modifiedSetDep)
    injector.set('setDep', setDep)

  state.result = fn.apply(0, _args)
  state.fnReturned = true

  // Call setDep if a plain object was returned
  if (isPlainObject(state.result))
    (modifiedSetDep || setDep)(state.result)

  // Check if we need to run next automatically when:
  // 1. result is true
  // 2. autoNext is true and no error and result is not false
  if (true === state.result || (state.autoNext && !err && false !== state.result))
    next()
}

exports.toPipe = function(pipeline, pipes, superpipe) {
  pipes = pipes || pipeline.pipes

  if (superpipe)
    pipeline.connect(superpipe)
  else
    superpipe = pipeline.superpipe

  var _injector = pipeline.injector

  var debug = pipeline.debug()
  if (debug)
    pipes = addDebugPipes(pipes, 'function' === typeof debug ? debug : null)

  return function() {
    if (pipes[0]) {
      var step = 0
      var args = toArray(arguments)
      var injector = new Injector(_injector)

      var setDep = function(name, dep, props) {
        DEP.setDep(superpipe, injector, name, dep, props)
      }

      // local deps setter unique for each pipeline
      injector.set('setDep', setDep)

      if (superpipe) {
        // Global dependencies setter for sharing states cross pipelines.
        injector.set('set', function(name, dep, props) {
          DEP.setDep(superpipe, _injector, name, dep, props)
          setDep(name, dep, props)
        })
      }

      var next = function next(err, name, dep, props) {
        if (arguments.length > 1)
          setDep(name, dep, props)

        // Should execute error pipe when `error` could be found in injector.
        if (err)
          injector.set('error', err)
        else
          err = injector.get('error')

        var pipe
        if (err)
          pipe = getErrorPipe(err, args, pipeline, pipes, step, injector)
        else
          // get the right unit from the pipes array
          pipe = pipes[step++]
        if (pipe)
          executePipe(err, args, pipe, next, setDep, injector)
      }

      // register `next` function as dependency
      injector.set('next', next)
      // start executing the chain
      next()
    }
  }
}

/**
 * Private functions
 */

var log = 'undefined' !== typeof console ? console.log : function() {}
function addDebugPipes(pipes, debug) {
  var _pipes = []
  debug = debug || log
  foreach(pipes, function(pipe, idx) {

    var beforePipe = {
      fn: function() {
        var args = formatArguments(arguments)
        var fnName = pipe.fnName || 'function'
        debug('\nExecuting pipeline [%d]: [%s] \n\t[deps expect]: %s\t[deps actual]: %s\t[sups expect]: %s'
          , idx, fnName,
          formatArguments(pipe.deps).join(', '),
          args.join(', '),
          formatArguments(pipe.supplies).join(', '))
        return true
      },
      deps: pipe.deps
    }

    var afterPipe = {
      fn: function() {
        var args = formatArguments(arguments)
        debug('\t[sups actual]: %s\n'
          , args.join(', '))
        return true
      },
      deps: pipe.supplies
    }

    _pipes.push(beforePipe)
    _pipes.push(pipe)
    _pipes.push(afterPipe)

  })

  return _pipes
}

function formatArguments(args) {
  var _args = toArray(args)
  foreach(_args, function(arg, idx) {
    var type = typeof arg
    if ('string' === type) {
      _args[idx] = '"' + arg + '"'
    } else if ('function' === type) {
      _args[idx] = '[Function' + (arg.name ? ': ' + arg.name : '') + ']'
    } else if (null === arg) {
      _args[idx] = 'null'
    } else if ('undefined' === type) {
      _args[idx] = 'undefined'
    }
  })
  return _args
}

function getErrorPipe(error, args, pipeline, pipes, step, injector) {
  if (pipeline.errorHandler) {
    // error handler fn as a pipe
    var pipe = pipeline.errorHandler
    var previousPipe = pipes[step - 1]
    if (previousPipe && previousPipe.fnName) {
      var ofn = previousPipe.ofn || previousPipe.fn.__original_fn__
      injector.set('errPipeName', previousPipe.fnName)
      injector.set('errPipeBody', 'function' === typeof ofn ? ofn.toString() : undefined)
    }
    // assign the err object to the first argument
    // so the error handler can get the error later
    args[0] = error
    return pipe
  }

  var ex = error
  if ('string' === typeof error) {
    ex = new Error()
    ex.name = 'Pipeline has no error handler function'
    ex.message = error
  }
  throw ex
}

function checkFulfillment(propName, state, supplies, fulfilled) {
  if (propName === ERROR_NAME) {
    state.hasError = true
  } else if (-1 === indexOf(supplies, propName))
    throw new Error('Dependency "' + propName + '" is not defined in supplies.')
  if (fulfilled && -1 === indexOf(fulfilled, propName))
    fulfilled.push(propName)
}
<commit_msg>Only string pipe could be optional. Fix ie6/7/8 indexOf.<commit_after>'use strict'

/**
 * Module dependencies
 */
var DEP = require('./dep')
var toArray = require('lodash.toarray')
var foreach = require('lodash.foreach')
var indexOf = require('lodash.indexof')
var Injector = require('insider')
var isPlainObject = require('lodash.isplainobject')
var ERROR_NAME = 'error'

var setDepWithState = exports.setDepWithState = function(state, name, dep, props) {
  var next = state.next
  var result = state.result
  var setDep = state.setDep
  var supplies = state.supplies
  var setDepMap = state.setDepMap
  var fulfilled = state.fulfilled
  var fnReturned = state.fnReturned

  var normalized = DEP.normalizeSetDepArgs(name, dep, props)
  var _dep = normalized.dep
  var _name = normalized.name
  var _props = normalized.props

  // Error happens in previous setDep call return to avoid call error handler twice.
  if (state.hasError)
    return

  if (_name && !_props) {
    var checkName = _name
    var mappedName = setDepMap && setDepMap[_name]
    if (mappedName) {
      // Check against the mapping name
      checkName = _name + ':' + mappedName
      // Set the mappedName as the real dependency name
      _name = mappedName
    }
    checkFulfillment(checkName, state, supplies, fulfilled)
    setDep(_name, _dep)
  } else if (_dep && _props) {
    foreach(_props, function(propName, idx) {
      var nameTOCheck = propName
      var mappedName = setDepMap && setDepMap[propName]
      if (mappedName) {
        // Check against the mapping name
        nameTOCheck = propName + ':' + mappedName
        // Link the propName to the mappedName in _dep
        _dep[mappedName] = _dep[propName]
        // Set the original propName to mappedName so we set `mappedName` as
        // depenency name instead of `propName`
        _props[idx] = mappedName
      }
      checkFulfillment(nameTOCheck, state, supplies, fulfilled)
    })
    setDep(_dep, _props)
  } else {
    throw new Error('Unsupported arguments for in pipe `setDep`.')
  }

  // Call next immediately if there is an error
  if (state.hasError) {
    state.autoNext = false
    next()
  } else if (fulfilled) {
    if (fulfilled.length === supplies.length) {
      // Set the auto next to true or call next when all required
      // supplies are fulfilled.
      if (fnReturned && ('undefined' === typeof result || false !== result))
        // Function has been returned.
        // We should call next when the returned value is either
        // undefined or not false.
        next()
      else
        // Otherwise, let other part of the code to handle when to call next.
        state.autoNext = true
    } else if (fulfilled.length > supplies.length) {
      throw new Error('Got more dependencies than what supplies required.')
    }
  }
}

var executePipe = exports.executePipe = function(err, args, pipe, next, setDep, injector) {
  var fn = pipe.fn
  var deps = pipe.deps
  var fnName = pipe.fnName
  var supplies = pipe.supplies

  var state = {
    next: next,
    setDep: setDep,
    result: undefined,
    supplies: supplies,
    // No deps or has deps but next is not required as dependency.
    autoNext: !deps || -1 === indexOf(deps, 'next'),
    setDepMap: pipe.setDepMap,
    fnReturned: false
  }

  // Get the pipe function demanded from dependency container.
  if ('string' === typeof fnName) {
    // Set it as the original pipe function
    pipe.ofn = injector.get(fnName)
    if (pipe.optional) {
      // Optional pipe, go next if we can't get all dependencies.
      var _deps = injector.getAll(deps)
      if (-1 < indexOf(_deps, undefined)) {
        next()
        return
      }
    }
  }

  var modifiedSetDep
  if (supplies && 0 < supplies.length) {
    if (true === state.autoNext) {
      // Only track dependencies when autoNext is true.
      state.fulfilled = []
      // We will handle the auto next behaviour in setDepWithState function.
      state.autoNext = false
    }
    // Call customized setDep function instead.
    modifiedSetDep = function(name, dep, props) {
      setDepWithState(state, name, dep, props)
    }
    injector.set('setDep', modifiedSetDep)
  }

  // Run the actual function
  var _args = injector.getAll(deps, args)
  // Ensure to put back the original `setDep` function if modified.
  if (modifiedSetDep)
    injector.set('setDep', setDep)

  state.result = fn.apply(0, _args)
  state.fnReturned = true

  // Call setDep if a plain object was returned
  if (isPlainObject(state.result))
    (modifiedSetDep || setDep)(state.result)

  // Check if we need to run next automatically when:
  // 1. result is true
  // 2. autoNext is true and no error and result is not false
  if (true === state.result || (state.autoNext && !err && false !== state.result))
    next()
}

exports.toPipe = function(pipeline, pipes, superpipe) {
  pipes = pipes || pipeline.pipes

  if (superpipe)
    pipeline.connect(superpipe)
  else
    superpipe = pipeline.superpipe

  var _injector = pipeline.injector

  var debug = pipeline.debug()
  if (debug)
    pipes = addDebugPipes(pipes, 'function' === typeof debug ? debug : null)

  return function() {
    if (pipes[0]) {
      var step = 0
      var args = toArray(arguments)
      var injector = new Injector(_injector)

      var setDep = function(name, dep, props) {
        DEP.setDep(superpipe, injector, name, dep, props)
      }

      // local deps setter unique for each pipeline
      injector.set('setDep', setDep)

      if (superpipe) {
        // Global dependencies setter for sharing states cross pipelines.
        injector.set('set', function(name, dep, props) {
          DEP.setDep(superpipe, _injector, name, dep, props)
          setDep(name, dep, props)
        })
      }

      var next = function next(err, name, dep, props) {
        if (arguments.length > 1)
          setDep(name, dep, props)

        // Should execute error pipe when `error` could be found in injector.
        if (err)
          injector.set('error', err)
        else
          err = injector.get('error')

        var pipe
        if (err)
          pipe = getErrorPipe(err, args, pipeline, pipes, step, injector)
        else
          // get the right unit from the pipes array
          pipe = pipes[step++]
        if (pipe)
          executePipe(err, args, pipe, next, setDep, injector)
      }

      // register `next` function as dependency
      injector.set('next', next)
      // start executing the chain
      next()
    }
  }
}

/**
 * Private functions
 */

var log = 'undefined' !== typeof console ? console.log : function() {}
function addDebugPipes(pipes, debug) {
  var _pipes = []
  debug = debug || log
  foreach(pipes, function(pipe, idx) {

    var beforePipe = {
      fn: function() {
        var args = formatArguments(arguments)
        var fnName = pipe.fnName || 'function'
        debug('\nExecuting pipeline [%d]: [%s] \n\t[deps expect]: %s\t[deps actual]: %s\t[sups expect]: %s'
          , idx, fnName,
          formatArguments(pipe.deps).join(', '),
          args.join(', '),
          formatArguments(pipe.supplies).join(', '))
        return true
      },
      deps: pipe.deps
    }

    var afterPipe = {
      fn: function() {
        var args = formatArguments(arguments)
        debug('\t[sups actual]: %s\n'
          , args.join(', '))
        return true
      },
      deps: pipe.supplies
    }

    _pipes.push(beforePipe)
    _pipes.push(pipe)
    _pipes.push(afterPipe)

  })

  return _pipes
}

function formatArguments(args) {
  var _args = toArray(args)
  foreach(_args, function(arg, idx) {
    var type = typeof arg
    if ('string' === type) {
      _args[idx] = '"' + arg + '"'
    } else if ('function' === type) {
      _args[idx] = '[Function' + (arg.name ? ': ' + arg.name : '') + ']'
    } else if (null === arg) {
      _args[idx] = 'null'
    } else if ('undefined' === type) {
      _args[idx] = 'undefined'
    }
  })
  return _args
}

function getErrorPipe(error, args, pipeline, pipes, step, injector) {
  if (pipeline.errorHandler) {
    // error handler fn as a pipe
    var pipe = pipeline.errorHandler
    var previousPipe = pipes[step - 1]
    if (previousPipe && previousPipe.fnName) {
      var ofn = previousPipe.ofn || previousPipe.fn.__original_fn__
      injector.set('errPipeName', previousPipe.fnName)
      injector.set('errPipeBody', 'function' === typeof ofn ? ofn.toString() : undefined)
    }
    // assign the err object to the first argument
    // so the error handler can get the error later
    args[0] = error
    return pipe
  }

  var ex = error
  if ('string' === typeof error) {
    ex = new Error()
    ex.name = 'Pipeline has no error handler function'
    ex.message = error
  }
  throw ex
}

function checkFulfillment(propName, state, supplies, fulfilled) {
  if (propName === ERROR_NAME) {
    state.hasError = true
  } else if (-1 === indexOf(supplies, propName))
    throw new Error('Dependency "' + propName + '" is not defined in supplies.')
  if (fulfilled && -1 === indexOf(fulfilled, propName))
    fulfilled.push(propName)
}
<|endoftext|>


<commit_before>/* globals communication, shot, main, auth, catcher, analytics */

"use strict";

this.takeshot = (function() {
  let exports = {};
  const Shot = shot.AbstractShot;
  const { sendEvent } = analytics;

  communication.register("takeShot", catcher.watchFunction((sender, options) => {
    let { captureType, captureText, scroll, selectedPos, shotId, shot, imageBlob } = options;
    shot = new Shot(main.getBackend(), shotId, shot);
    shot.favicon = sender.tab.favIconUrl;
    let capturePromise = Promise.resolve();
    let openedTab;
    if (!shot.clipNames().length) {
      // canvas.drawWindow isn't available, so we fall back to captureVisibleTab
      capturePromise = screenshotPage(selectedPos, scroll).then((dataUrl) => {
        shot.addClip({
          createdDate: Date.now(),
          image: {
            url: "data:",
            captureType,
            text: captureText,
            location: selectedPos,
            dimensions: {
              x: selectedPos.right - selectedPos.left,
              y: selectedPos.bottom - selectedPos.top
            }
          }
        });
      });
    }
    if (!imageBlob) {
      imageBlob = base64ToBinary(shot.getClip(shot.clipNames()[0]).image.url);
      shot.getClip(shot.clipNames()[0]).image.url = "";
    }
    let shotAbTests = {};
    let abTests = auth.getAbTests();
    for (let testName of Object.keys(abTests)) {
      if (abTests[testName].shotField) {
        shotAbTests[testName] = abTests[testName].value;
      }
    }
    if (Object.keys(shotAbTests).length) {
      shot.abTests = shotAbTests;
    }
    return catcher.watchPromise(capturePromise.then(() => {
      return browser.tabs.create({url: shot.creatingUrl})
    }).then((tab) => {
      openedTab = tab;
      return uploadShot(shot, imageBlob);
    }).then(() => {
      return browser.tabs.update(openedTab.id, {url: shot.viewUrl}).then(
        null,
        (error) => {
          // FIXME: If https://bugzilla.mozilla.org/show_bug.cgi?id=1365718 is resolved,
          // use the errorCode added as an additional check:
          if ((/invalid tab id/i).test(error)) {
            // This happens if the tab was closed before the upload completed
            return browser.tabs.create({url: shot.viewUrl});
          }
          throw error;
        }
      );
    }).then(() => {
      return shot.viewUrl;
    }).catch((error) => {
      browser.tabs.remove(openedTab.id);
      throw error;
    }));
  }));

  communication.register("screenshotPage", (sender, selectedPos, scroll) => {
    return screenshotPage(selectedPos, scroll);
  });

  function screenshotPage(pos, scroll) {
    pos = {
      top: pos.top - scroll.scrollY,
      left: pos.left - scroll.scrollX,
      bottom: pos.bottom - scroll.scrollY,
      right: pos.right - scroll.scrollX
    };
    pos.width = pos.right - pos.left;
    pos.height = pos.bottom - pos.top;
    return catcher.watchPromise(browser.tabs.captureVisibleTab(
      null,
      {format: "png"}
    ).then((dataUrl) => {
      let image = new Image();
      image.src = dataUrl;
      return new Promise((resolve, reject) => {
        image.onload = catcher.watchFunction(() => {
          let xScale = image.width / scroll.innerWidth;
          let yScale = image.height / scroll.innerHeight;
          let canvas = document.createElement("canvas");
          canvas.height = pos.height * yScale;
          canvas.width = pos.width * xScale;
          let context = canvas.getContext("2d");
          context.drawImage(
            image,
            pos.left * xScale, pos.top * yScale,
            pos.width * xScale, pos.height * yScale,
            0, 0,
            pos.width * xScale, pos.height * yScale
          );
          let result = canvas.toDataURL();
          resolve(result);
        });
      });
    }));
  }

  function base64ToBinary(url) {
    const binary = atob(url.split(',')[1]);
    const data = Uint8Array.from(binary, char => char.charCodeAt(0));
    const blob = new Blob([data], {type: "image/png"});
    return blob;
  }

  function uploadShot(shot, blob) {
    return auth.authHeaders().then((headers) => {
      let formData = new FormData();
      formData.append("shot", JSON.stringify(shot.asJson()));
      formData.append("blob", blob);
      let body = formData;
      sendEvent("upload", "started", {eventValue: Math.floor(body.length / 1000)});
      return fetch(shot.jsonUrl, {
        method: "PUT",
        mode: "cors",
        headers,
        body
      });
    }).then((resp) => {
      if (!resp.ok) {
        sendEvent("upload-failed", `status-${resp.status}`);
        let exc = new Error(`Response failed with status ${resp.status}`);
        exc.popupMessage = "REQUEST_ERROR";
        throw exc;
      } else {
        sendEvent("upload", "success");
      }
    }, (error) => {
      // FIXME: I'm not sure what exceptions we can expect
      sendEvent("upload-failed", "connection");
      error.popupMessage = "CONNECTION_ERROR";
      throw error;
    });
  }

  return exports;
})();
<commit_msg>Fix #3472, avoid form uploads from being truncated<commit_after>/* globals communication, shot, main, auth, catcher, analytics */

"use strict";

this.takeshot = (function() {
  let exports = {};
  const Shot = shot.AbstractShot;
  const { sendEvent } = analytics;

  communication.register("takeShot", catcher.watchFunction((sender, options) => {
    let { captureType, captureText, scroll, selectedPos, shotId, shot, imageBlob } = options;
    shot = new Shot(main.getBackend(), shotId, shot);
    shot.favicon = sender.tab.favIconUrl;
    let capturePromise = Promise.resolve();
    let openedTab;
    if (!shot.clipNames().length) {
      // canvas.drawWindow isn't available, so we fall back to captureVisibleTab
      capturePromise = screenshotPage(selectedPos, scroll).then((dataUrl) => {
        shot.addClip({
          createdDate: Date.now(),
          image: {
            url: "data:",
            captureType,
            text: captureText,
            location: selectedPos,
            dimensions: {
              x: selectedPos.right - selectedPos.left,
              y: selectedPos.bottom - selectedPos.top
            }
          }
        });
      });
    }
    if (!imageBlob) {
      imageBlob = base64ToBinary(shot.getClip(shot.clipNames()[0]).image.url);
      shot.getClip(shot.clipNames()[0]).image.url = "";
    }
    let shotAbTests = {};
    let abTests = auth.getAbTests();
    for (let testName of Object.keys(abTests)) {
      if (abTests[testName].shotField) {
        shotAbTests[testName] = abTests[testName].value;
      }
    }
    if (Object.keys(shotAbTests).length) {
      shot.abTests = shotAbTests;
    }
    return catcher.watchPromise(capturePromise.then(() => {
      return browser.tabs.create({url: shot.creatingUrl})
    }).then((tab) => {
      openedTab = tab;
      return uploadShot(shot, imageBlob);
    }).then(() => {
      return browser.tabs.update(openedTab.id, {url: shot.viewUrl}).then(
        null,
        (error) => {
          // FIXME: If https://bugzilla.mozilla.org/show_bug.cgi?id=1365718 is resolved,
          // use the errorCode added as an additional check:
          if ((/invalid tab id/i).test(error)) {
            // This happens if the tab was closed before the upload completed
            return browser.tabs.create({url: shot.viewUrl});
          }
          throw error;
        }
      );
    }).then(() => {
      return shot.viewUrl;
    }).catch((error) => {
      browser.tabs.remove(openedTab.id);
      throw error;
    }));
  }));

  communication.register("screenshotPage", (sender, selectedPos, scroll) => {
    return screenshotPage(selectedPos, scroll);
  });

  function screenshotPage(pos, scroll) {
    pos = {
      top: pos.top - scroll.scrollY,
      left: pos.left - scroll.scrollX,
      bottom: pos.bottom - scroll.scrollY,
      right: pos.right - scroll.scrollX
    };
    pos.width = pos.right - pos.left;
    pos.height = pos.bottom - pos.top;
    return catcher.watchPromise(browser.tabs.captureVisibleTab(
      null,
      {format: "png"}
    ).then((dataUrl) => {
      let image = new Image();
      image.src = dataUrl;
      return new Promise((resolve, reject) => {
        image.onload = catcher.watchFunction(() => {
          let xScale = image.width / scroll.innerWidth;
          let yScale = image.height / scroll.innerHeight;
          let canvas = document.createElement("canvas");
          canvas.height = pos.height * yScale;
          canvas.width = pos.width * xScale;
          let context = canvas.getContext("2d");
          context.drawImage(
            image,
            pos.left * xScale, pos.top * yScale,
            pos.width * xScale, pos.height * yScale,
            0, 0,
            pos.width * xScale, pos.height * yScale
          );
          let result = canvas.toDataURL();
          resolve(result);
        });
      });
    }));
  }

  function base64ToBinary(url) {
    const binary = atob(url.split(',')[1]);
    const data = Uint8Array.from(binary, char => char.charCodeAt(0));
    const blob = new Blob([data], {type: "image/png"});
    return blob;
  }

  /** Combines two buffers or Uint8Array's */
  function concatBuffers(buffer1, buffer2) {
    var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
    tmp.set(new Uint8Array(buffer1), 0);
    tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
    return tmp.buffer;
  }

  /** Returns a promise that converts a Blob to a TypedArray */
  function blobToArray(blob) {
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.addEventListener("loadend", function() {
        resolve(reader.result);
      });
      reader.readAsArrayBuffer(blob);
    });
  }

  /** Creates a multipart TypedArray, given {name: value} fields
      and {name: blob} files

      Returns {body, "content-type"}
      */
  function createMultipart(fields, fileField, fileFilename, blob) {
    let boundary = "---------------------------ScreenshotBoundary" + Date.now();
    return blobToArray(blob).then((blobAsBuffer) => {
      let body = [];
      for (let name in fields) {
        body.push("--" + boundary);
        body.push(`Content-Disposition: form-data; name="${name}"`);
        body.push("");
        body.push(fields[name]);
      }
      body.push("--" + boundary);
      body.push(`Content-Disposition: form-data; name="${fileField}"; filename="${fileFilename}"`);
      body.push(`Content-Type: ${blob.type}`);
      body.push("");
      body.push("");
      body = body.join("\r\n");
      let enc = new TextEncoder("utf-8");
      body = enc.encode(body);
      body = concatBuffers(body.buffer, blobAsBuffer);
      let tail = "\r\n" + "--" + boundary + "--";
      tail = enc.encode(tail);
      body = concatBuffers(body, tail.buffer);
      return {
        "content-type": `multipart/form-data; boundary=${boundary}`,
        body
      };
    });
  }

  function uploadShot(shot, blob) {
    let headers;
    return auth.authHeaders().then((_headers) => {
      headers = _headers;
      return createMultipart(
        {shot: JSON.stringify(shot.asJson())},
        "blob", "screenshot.png", blob
      );
    }).then((submission) => {
      headers["content-type"] = submission["content-type"];
      sendEvent("upload", "started", {eventValue: Math.floor(submission.body.length / 1000)});
      return fetch(shot.jsonUrl, {
        method: "PUT",
        mode: "cors",
        headers,
        body: submission.body
      });
    }).then((resp) => {
      if (!resp.ok) {
        sendEvent("upload-failed", `status-${resp.status}`);
        let exc = new Error(`Response failed with status ${resp.status}`);
        exc.popupMessage = "REQUEST_ERROR";
        throw exc;
      } else {
        sendEvent("upload", "success");
      }
    }, (error) => {
      // FIXME: I'm not sure what exceptions we can expect
      sendEvent("upload-failed", "connection");
      error.popupMessage = "CONNECTION_ERROR";
      throw error;
    });
  }

  return exports;
})();
<|endoftext|>