<commit_before>/*
 * Scripts propios para ejecutar en toda la platafroma
 */

//Insert text in the textarea#areaId where the caret is
function insertAtCaret(obj, string) {
    obj = document.getElementById(obj);
    obj.focus();

    if (typeof (document.selection) != 'undefined') {
        var range = document.selection.createRange();
        if (range.parentElement() != obj) return;
        range.text = string;
        range.select();
    }
    else if (typeof (obj.selectionStart) != 'undefined') {
        var start = obj.selectionStart;
        obj.value = obj.value.substr(0, start) + string + obj.value.substr(obj.selectionEnd, obj.value.length);
        start += string.length;
        obj.setSelectionRange(start, start);
    }
    else {
        obj.value += string;
    }

    obj.focus();
}

var PulsarApp = function() {

    "use strict";

    /******************************
     * FIXED dual list box        *
     ******************************/
    var fixedConfigureBoxes = function() {
        if ($.configureBoxes)
        {
            $('.dual-list-submit').closest('form').submit(function() {
                $('.dual-list-submit option').prop('selected', true);
            });
        }
    }

    /**************************
     * Validation             *
     **************************/
    var initValidation = function() {
        if ($.validator) {
            // Set default options
            $.extend( $.validator.defaults, {
                errorClass: "has-error",
                validClass: "has-success",
                highlight: function(element, errorClass, validClass) {

                    if (element.type === 'radio') {
                        this.findByName(element.name).addClass(errorClass).removeClass(validClass);
                    } else {
                        $(element).addClass(errorClass).removeClass(validClass);
                    }

                    // @see http://support.stammtec.de/discussion/412/form-vertical-validation
                    if ($(element).closest("form").hasClass('form-vertical')) {
                        var class_selector = "*[class^=col-]";
                    } else {
                        var class_selector = ".form-group";
                    }
                    $(element).closest(class_selector).addClass(errorClass).removeClass(validClass);
                },
                unhighlight: function(element, errorClass, validClass) {
                    if (element.type === 'radio') {
                        this.findByName(element.name).removeClass(errorClass).addClass(validClass);
                    } else {
                        $(element).removeClass(errorClass).addClass(validClass);
                    }

                    // @see http://support.stammtec.de/discussion/412/form-vertical-validation
                    if ($(element).closest("form").hasClass('form-vertical')) {
                        var class_selector = "*[class^=col-]";
                    } else {
                        var class_selector = ".form-group";
                    }

                    $(element).closest(class_selector).removeClass(errorClass).addClass(validClass);

                    // Fix for not removing label in BS3
                    $(element).closest(class_selector).find('label[generated="true"]').html('');
                },
                errorPlacement: function(error, element) {
                    if (element.data("error-placement"))
                    {
                        error.insertAfter("#" + element.data("error-placement"));
                    }
                    else if(element.closest(".error-placement-after").length > 0)
                    {
                        error.insertAfter(element.closest(".error-placement-after"));
                    }
                    else
                    {
                        error.insertAfter(element);
                    }
                }
            });

            var _base_resetForm = $.validator.prototype.resetForm;
            $.extend( $.validator.prototype, {
                resetForm: function() {
                    var resetForm_this = this;
                    _base_resetForm.call( this );

                    $(this.currentForm).find('.form-group').each(function () {
                        $(this).removeClass(resetForm_this.settings.errorClass + ' ' + resetForm_this.settings.validClass);
                    });

                    // Removing states from select2-boxes
                    $(this.currentForm).find('.select2-container').removeClass(resetForm_this.settings.errorClass + ' ' + resetForm_this.settings.validClass);

                    $(this.currentForm).find('label[generated="true"]').html('');
                },
                showLabel: function(element, message) {
                    var label = this.errorsFor( element );
                    if ( label.length ) {
                        // refresh error/success class
                        label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );

                        // check if we have a generated label, replace the message then
                        if ( label.attr("generated") ) {
                            label.html(message);
                        }
                    } else {
                        // create label
                        label = $("<" + this.settings.errorElement + "/>")
                            .attr({"for":  this.idOrName(element), generated: true})
                            .addClass(this.settings.errorClass)
                            .addClass('help-block')
                            .html(message || "");
                        if ( this.settings.wrapper ) {
                            // make sure the element is visible, even in IE
                            // actually showing the wrapped element is handled elsewhere
                            label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
                        }
                        if ( !this.labelContainer.append(label).length ) {
                            if ( this.settings.errorPlacement ) {
                                this.settings.errorPlacement(label, $(element) );
                            } else {
                                label.insertAfter(element);
                            }
                        }
                    }
                    if ( !message && this.settings.success ) {
                        label.text("");
                        if ( typeof this.settings.success === "string" ) {
                            label.addClass( this.settings.success );
                        } else {
                            this.settings.success( label, element );
                        }
                    }
                    this.toShow = this.toShow.add(label);
                }
            });
        }

        // Run validate on forms
        if ($.fn.validate)
        {
            $("form").validate({
                submitHandler: function(form) {
                    $('button[type=submit], input[type=submit]').prop('disabled',true);
                }
            });
        }
    }

    /**************************
     * Uniform                *
     **************************/
    var initUniform = function() {
        if ($.fn.uniform)
        {
            $(".uniform").uniform();
        }
    }

    /**************************
     * Colorpicker            *
     **************************/
    var initColorpicker = function() {
        if ($.fn.colorpicker)
        {
            $('.color-picker').colorpicker();
        }
    }

    /**************************
     * Datetimepicker         *
     **************************/
    var initDatetimepicker = function() {
        if ($.fn.datetimepicker)
        {
            $('.datetimepicker').each(function() {
                var self = $(this);
                $(self).datetimepicker(self.data());
            });
        }
    }

    /**************************
     * Select2                *
     **************************/
    var initSelect2 = function() {
        if ($.fn.select2)
        {
            $('.select2').each(function() {
                var self = $(this);
                $(self).select2(self.data());
            });
        }
    }

    /**************************
     * Inputlimiter           *
     **************************/
    var initInputlimiter = function() {
        if ($.fn.inputlimiter) {
            $(".limited").each(function(index, value) {
                var limitText = $.fn.inputlimiter.defaults.limitText;
                var data_limit = $(this).data('limit');
                limitText = limitText.replace(/\%n/g, data_limit);
                limitText = limitText.replace(/\%s/g, (data_limit <= 1 ? '' : 's'));

                $(this).parent().find('#'+$(this).attr('box-id')).html(limitText);
                $(this).inputlimiter({
                    limit: data_limit,
                    boxId: $(this).attr('box-id')
                });
            });
        }
    }

    /**************************
     * FileInput              *
     **************************/
    var initFileInput = function() {
        if ($.fn.fileInput) {
            $('[data-style="fileinput"]').each(function () {
                var $input = $(this);
                $input.fileInput($input.data());
            });
        }
    }

    return {
        // main function to initiate all plugins
        init: function () {
            fixedConfigureBoxes(); // fixed configureBoxes
            initValidation(); // Validation
            initUniform(); // Uniform
            initColorpicker(); // Colorpicker
            initDatetimepicker();  // Datetimepicker
            initSelect2(); // Select2
            initInputlimiter(); // Inputlimiter
            initFileInput(); // FileInput
        }
    };

}();<commit_msg>submit handler on validator<commit_after>/*
 * Scripts propios para ejecutar en toda la platafroma
 */

//Insert text in the textarea#areaId where the caret is
function insertAtCaret(obj, string) {
    obj = document.getElementById(obj);
    obj.focus();

    if (typeof (document.selection) != 'undefined') {
        var range = document.selection.createRange();
        if (range.parentElement() != obj) return;
        range.text = string;
        range.select();
    }
    else if (typeof (obj.selectionStart) != 'undefined') {
        var start = obj.selectionStart;
        obj.value = obj.value.substr(0, start) + string + obj.value.substr(obj.selectionEnd, obj.value.length);
        start += string.length;
        obj.setSelectionRange(start, start);
    }
    else {
        obj.value += string;
    }

    obj.focus();
}

var PulsarApp = function() {

    "use strict";

    /******************************
     * FIXED dual list box        *
     ******************************/
    var fixedConfigureBoxes = function() {
        if ($.configureBoxes)
        {
            $('.dual-list-submit').closest('form').submit(function() {
                $('.dual-list-submit option').prop('selected', true);
            });
        }
    }

    /**************************
     * Validation             *
     **************************/
    var initValidation = function() {
        if ($.validator) {
            // Set default options
            $.extend( $.validator.defaults, {
                errorClass: "has-error",
                validClass: "has-success",
                highlight: function(element, errorClass, validClass) {

                    if (element.type === 'radio') {
                        this.findByName(element.name).addClass(errorClass).removeClass(validClass);
                    } else {
                        $(element).addClass(errorClass).removeClass(validClass);
                    }

                    // @see http://support.stammtec.de/discussion/412/form-vertical-validation
                    if ($(element).closest("form").hasClass('form-vertical')) {
                        var class_selector = "*[class^=col-]";
                    } else {
                        var class_selector = ".form-group";
                    }
                    $(element).closest(class_selector).addClass(errorClass).removeClass(validClass);
                },
                unhighlight: function(element, errorClass, validClass) {
                    if (element.type === 'radio') {
                        this.findByName(element.name).removeClass(errorClass).addClass(validClass);
                    } else {
                        $(element).removeClass(errorClass).addClass(validClass);
                    }

                    // @see http://support.stammtec.de/discussion/412/form-vertical-validation
                    if ($(element).closest("form").hasClass('form-vertical')) {
                        var class_selector = "*[class^=col-]";
                    } else {
                        var class_selector = ".form-group";
                    }

                    $(element).closest(class_selector).removeClass(errorClass).addClass(validClass);

                    // Fix for not removing label in BS3
                    $(element).closest(class_selector).find('label[generated="true"]').html('');
                },
                errorPlacement: function(error, element) {
                    if (element.data("error-placement"))
                    {
                        error.insertAfter("#" + element.data("error-placement"));
                    }
                    else if(element.closest(".error-placement-after").length > 0)
                    {
                        error.insertAfter(element.closest(".error-placement-after"));
                    }
                    else
                    {
                        error.insertAfter(element);
                    }
                }
            });

            var _base_resetForm = $.validator.prototype.resetForm;
            $.extend( $.validator.prototype, {
                resetForm: function() {
                    var resetForm_this = this;
                    _base_resetForm.call( this );

                    $(this.currentForm).find('.form-group').each(function () {
                        $(this).removeClass(resetForm_this.settings.errorClass + ' ' + resetForm_this.settings.validClass);
                    });

                    // Removing states from select2-boxes
                    $(this.currentForm).find('.select2-container').removeClass(resetForm_this.settings.errorClass + ' ' + resetForm_this.settings.validClass);

                    $(this.currentForm).find('label[generated="true"]').html('');
                },
                showLabel: function(element, message) {
                    var label = this.errorsFor( element );
                    if ( label.length ) {
                        // refresh error/success class
                        label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass );

                        // check if we have a generated label, replace the message then
                        if ( label.attr("generated") ) {
                            label.html(message);
                        }
                    } else {
                        // create label
                        label = $("<" + this.settings.errorElement + "/>")
                            .attr({"for":  this.idOrName(element), generated: true})
                            .addClass(this.settings.errorClass)
                            .addClass('help-block')
                            .html(message || "");
                        if ( this.settings.wrapper ) {
                            // make sure the element is visible, even in IE
                            // actually showing the wrapped element is handled elsewhere
                            label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
                        }
                        if ( !this.labelContainer.append(label).length ) {
                            if ( this.settings.errorPlacement ) {
                                this.settings.errorPlacement(label, $(element) );
                            } else {
                                label.insertAfter(element);
                            }
                        }
                    }
                    if ( !message && this.settings.success ) {
                        label.text("");
                        if ( typeof this.settings.success === "string" ) {
                            label.addClass( this.settings.success );
                        } else {
                            this.settings.success( label, element );
                        }
                    }
                    this.toShow = this.toShow.add(label);
                }
            });
        }

        // Run validate on forms
        if ($.fn.validate)
        {
            $("form").validate({
                submitHandler: function(form) {
                    $('button[type=submit], input[type=submit]').prop('disabled',true);
                    return true;
                }
            });
        }
    };

    /**************************
     * Uniform                *
     **************************/
    var initUniform = function() {
        if ($.fn.uniform)
        {
            $(".uniform").uniform();
        }
    };

    /**************************
     * Colorpicker            *
     **************************/
    var initColorpicker = function() {
        if ($.fn.colorpicker)
        {
            $('.color-picker').colorpicker();
        }
    };

    /**************************
     * Datetimepicker         *
     **************************/
    var initDatetimepicker = function() {
        if ($.fn.datetimepicker)
        {
            $('.datetimepicker').each(function() {
                var self = $(this);
                $(self).datetimepicker(self.data());
            });
        }
    };

    /**************************
     * Select2                *
     **************************/
    var initSelect2 = function() {
        if ($.fn.select2)
        {
            $('.select2').each(function() {
                var self = $(this);
                $(self).select2(self.data());
            });
        }
    };

    /**************************
     * Inputlimiter           *
     **************************/
    var initInputlimiter = function() {
        if ($.fn.inputlimiter) {
            $(".limited").each(function(index, value) {
                var limitText = $.fn.inputlimiter.defaults.limitText;
                var data_limit = $(this).data('limit');
                limitText = limitText.replace(/\%n/g, data_limit);
                limitText = limitText.replace(/\%s/g, (data_limit <= 1 ? '' : 's'));

                $(this).parent().find('#'+$(this).attr('box-id')).html(limitText);
                $(this).inputlimiter({
                    limit: data_limit,
                    boxId: $(this).attr('box-id')
                });
            });
        }
    };

    /**************************
     * FileInput              *
     **************************/
    var initFileInput = function() {
        if ($.fn.fileInput) {
            $('[data-style="fileinput"]').each(function () {
                var $input = $(this);
                $input.fileInput($input.data());
            });
        }
    };

    return {
        // main function to initiate all plugins
        init: function () {
            fixedConfigureBoxes(); // fixed configureBoxes
            initValidation(); // Validation
            initUniform(); // Uniform
            initColorpicker(); // Colorpicker
            initDatetimepicker();  // Datetimepicker
            initSelect2(); // Select2
            initInputlimiter(); // Inputlimiter
            initFileInput(); // FileInput
        }
    };

}();<|endoftext|>


<commit_before>
var jobStatusTimeout = null;
var requestPendingAllJobs = false;

var jobsManager = {
  jobs: [],
  htmlElements: {
    builder: function (jobs) {
      $(jobs).each(function () {

        currentJob = this;
        var donwloadBtn = currentJob.status === 'completed' || currentJob.status === 'failed';

        var completed = (currentJob.status === 'completed' || currentJob.status === 'failed') ? true : false;
        var msg = currentJob.status === 'failed'
          ? 'Télécharger le rapport d\'erreur de l\'optimisation'
          : 'Télécharger le résultat de l\'optimisation';

        var jobDOM =
          '
'
          + '' + (new Date(currentJob.time)).toLocaleString('fr-FR') + ' : '
          + '' + 'Job N° ' + currentJob.uuid + ' '
          + '<button value=' + currentJob.uuid + ' data-role="delete">'
          + ((currentJob.status === 'queued' || currentJob.status === 'working') ? i18n.killOptim : i18n.deleteOptim)
          + '</button>'
          + ' (Status: ' + currentJob.status + ')'
          + (donwloadBtn ? ' ' + msg + '' : '')
          + '
';

        $('#jobs-list').append(jobDOM);

        if (completed) {
          $('a[data-job-id="' + currentJob.uuid + '"').on('click', function (e) {
            e.preventDefault();
            jobsManager.checkJobStatus({
              job: currentJob,
            }, function (err, job) {
              if (err) {
                if (debug) console.error(err);
                return alert("An error occured");
              }

              if (typeof job === 'string') {
                $('#result').html(job)
                var a = document.createElement('a');
                a.href = 'data:attachment/csv,' + encodeURIComponent(job);
                a.target = '_blank';
                a.download = 'result.csv';
                document.body.appendChild(a);
                a.click();
              }
              else {
                var solution = job.solutions[0];
                $('#result').html(JSON.stringify(solution, null, 4));
                var a = document.createElement('a');
                a.href = 'data:attachment/json,' + encodeURIComponent(JSON.stringify(solution, null, 4));
                a.target = '_blank';
                a.download = 'result.json';
                document.body.appendChild(a);
                a.click();
              }
            });
          })
        }
      });
      $('#jobs-list button').on('click', function () {
        jobsManager.roleDispatcher(this);
      });
    }
  },
  roleDispatcher: function (object) {
    switch ($(object).data('role')) {
      case 'focus':
        //actually in building, create to apply different behavior to the button object restartJob, actually not set. #TODO
        break;
      case 'delete':
        this.ajaxDeleteJob($(object).val());
        break;
    }
  },
  ajaxGetJobs: function (timeinterval) {
    var ajaxload = function () {
      if (!requestPendingAllJobs) {
        requestPendingAllJobs = true;
        $.ajax({
          url: '/0.1/vrp/jobs',
          type: 'get',
          dataType: 'json',
          data: { api_key: getParams()['api_key'] },
          complete: function () { requestPendingAllJobs = false; }
        }).done(function (data) {
          jobsManager.shouldUpdate(data);
        }).fail(function (jqXHR, textStatus, errorThrown) {
          clearInterval(window.AjaxGetRequestInterval);
          if (jqXHR.status == 401) {
            $('#optim-list-status').prepend('
' + i18n.unauthorizedError + '
');
            $('form input, form button').prop('disabled', true);
          }
        });
      }
    };
    if (timeinterval) {
      ajaxload();
      window.AjaxGetRequestInterval = setInterval(ajaxload, 5000);
    } else {
      ajaxload();
    }
  },
  ajaxDeleteJob: function (uuid) {
    $.ajax({
      url: '/0.1/vrp/jobs/' + uuid,
      type: 'delete',
      dataType: 'json',
      data: {
        api_key: getParams()['api_key']
      },
    }).done(function (data) {
      if (debug) { console.log("the uuid has been deleted from the jobs queue & the DB"); }
      $('button[data-role="delete"][value="' + uuid + '"]').fadeOut(500, function () { $(this).closest('.job').remove(); });
    });
  },
  shouldUpdate: function (data) {
    // erase list if no job running
    if (data.length === 0 && jobsManager.jobs.length !== 0) {
      $('#jobs-list').empty();
    }
    //check if chagements occurs in the data api. #TODO, update if more params are needed.
    $(data).each(function (index, object) {
      if (jobsManager.jobs.length > 0) {
        if (object.status != jobsManager.jobs[index].status || jobsManager.jobs.length != data.length) {
          jobsManager.jobs = data;
          $('#jobs-list').empty();
          jobsManager.htmlElements.builder(jobsManager.jobs);
        }
      }
      else {
        jobsManager.jobs = data;
        $('#jobs-list').empty();
        jobsManager.htmlElements.builder(jobsManager.jobs);
      }
    });
  },
  checkJobStatus: function (options, cb) {
    var nbError = 0;
    var requestPendingJobTimeout = false;

    if (options.interval) {
      jobStatusTimeout = setTimeout(requestPendingJob, options.interval);
      return;
    }

    requestPendingJob();

    function requestPendingJob() {
      $.ajax({
        type: 'GET',
        contentType: 'application/json',
        url: '/0.1/vrp/jobs/'
          + (options.job.id || options.job.uuid)
          // + (options.format ? options.format : '')
          + '?api_key=' + getParams()["api_key"],
        success: function (job, _, xhr) {

          if (checkJSONJob(job) || checkCSVJob(xhr)) {
            if (debug) console.log("REQUEST PENDING JOB", checkCSVJob(xhr), checkJSONJob(job));
            requestPendingJobTimeout = true;
          }

          nbError = 0;
          cb(null, job, xhr);
      },
        error: function (xhr, status) {
          ++nbError
          if (nbError > 2) {
            cb({ xhr, status });
            return alert(i18n.failureOptim(nbError, status));
          }
          requestPendingJobTimeout = true;
        },
        complete: function () {
          if (requestPendingJobTimeout) {
            requestPendingJobTimeout = false;

            // interval max: 1mins
            options.interval *= 2;
            if (options.interval > 60000)  {
              options.interval = 60000
            }

            jobStatusTimeout = setTimeout(requestPendingJob, options.interval);
          }
        }
      });
  }

},
  stopJobChecking: function () {
    requestPendingJobTimeout = false;
    clearTimeout(jobStatusTimeout);
  }
};

function checkCSVJob(xhr) {
  if (debug) console.log(xhr, xhr.status);
  return (xhr.status !== 200 && xhr.status !== 202);
}

function checkJSONJob(job) {
  if (debug) console.log("JOB: ", job, (job.job && job.job.status !== 'completed'));
  return ((job.job && job.job.status !== 'completed') && typeof job !== 'string')
}
<commit_msg>Fix missing interval check when retrieving optim failure/result<commit_after>
var jobStatusTimeout = null;
var requestPendingAllJobs = false;

var jobsManager = {
  jobs: [],
  htmlElements: {
    builder: function (jobs) {
      $(jobs).each(function () {

        currentJob = this;
        var donwloadBtn = currentJob.status === 'completed' || currentJob.status === 'failed';

        var completed = (currentJob.status === 'completed' || currentJob.status === 'failed') ? true : false;
        var msg = currentJob.status === 'failed'
          ? 'Télécharger le rapport d\'erreur de l\'optimisation'
          : 'Télécharger le résultat de l\'optimisation';

        var jobDOM =
          '
'
          + '' + (new Date(currentJob.time)).toLocaleString('fr-FR') + ' : '
          + '' + 'Job N° ' + currentJob.uuid + ' '
          + '<button value=' + currentJob.uuid + ' data-role="delete">'
          + ((currentJob.status === 'queued' || currentJob.status === 'working') ? i18n.killOptim : i18n.deleteOptim)
          + '</button>'
          + ' (Status: ' + currentJob.status + ')'
          + (donwloadBtn ? ' ' + msg + '' : '')
          + '
';

        $('#jobs-list').append(jobDOM);

        if (completed) {
          $('a[data-job-id="' + currentJob.uuid + '"').on('click', function (e) {
            e.preventDefault();
            jobsManager.checkJobStatus({
              job: currentJob,
            }, function (err, job) {
              if (err) {
                if (debug) console.error(err);
                return alert("An error occured");
              }

              if (typeof job === 'string') {
                $('#result').html(job)
                var a = document.createElement('a');
                a.href = 'data:attachment/csv,' + encodeURIComponent(job);
                a.target = '_blank';
                a.download = 'result.csv';
                document.body.appendChild(a);
                a.click();
              }
              else {
                var solution = job.solutions[0];
                $('#result').html(JSON.stringify(solution, null, 4));
                var a = document.createElement('a');
                a.href = 'data:attachment/json,' + encodeURIComponent(JSON.stringify(solution, null, 4));
                a.target = '_blank';
                a.download = 'result.json';
                document.body.appendChild(a);
                a.click();
              }
            });
          })
        }
      });
      $('#jobs-list button').on('click', function () {
        jobsManager.roleDispatcher(this);
      });
    }
  },
  roleDispatcher: function (object) {
    switch ($(object).data('role')) {
      case 'focus':
        //actually in building, create to apply different behavior to the button object restartJob, actually not set. #TODO
        break;
      case 'delete':
        this.ajaxDeleteJob($(object).val());
        break;
    }
  },
  ajaxGetJobs: function (timeinterval) {
    var ajaxload = function () {
      if (!requestPendingAllJobs) {
        requestPendingAllJobs = true;
        $.ajax({
          url: '/0.1/vrp/jobs',
          type: 'get',
          dataType: 'json',
          data: { api_key: getParams()['api_key'] },
          complete: function () { requestPendingAllJobs = false; }
        }).done(function (data) {
          jobsManager.shouldUpdate(data);
        }).fail(function (jqXHR, textStatus, errorThrown) {
          clearInterval(window.AjaxGetRequestInterval);
          if (jqXHR.status == 401) {
            $('#optim-list-status').prepend('
' + i18n.unauthorizedError + '
');
            $('form input, form button').prop('disabled', true);
          }
        });
      }
    };
    if (timeinterval) {
      ajaxload();
      window.AjaxGetRequestInterval = setInterval(ajaxload, 5000);
    } else {
      ajaxload();
    }
  },
  ajaxDeleteJob: function (uuid) {
    $.ajax({
      url: '/0.1/vrp/jobs/' + uuid,
      type: 'delete',
      dataType: 'json',
      data: {
        api_key: getParams()['api_key']
      },
    }).done(function (data) {
      if (debug) { console.log("the uuid has been deleted from the jobs queue & the DB"); }
      $('button[data-role="delete"][value="' + uuid + '"]').fadeOut(500, function () { $(this).closest('.job').remove(); });
    });
  },
  shouldUpdate: function (data) {
    // erase list if no job running
    if (data.length === 0 && jobsManager.jobs.length !== 0) {
      $('#jobs-list').empty();
    }
    //check if chagements occurs in the data api. #TODO, update if more params are needed.
    $(data).each(function (index, object) {
      if (jobsManager.jobs.length > 0) {
        if (object.status != jobsManager.jobs[index].status || jobsManager.jobs.length != data.length) {
          jobsManager.jobs = data;
          $('#jobs-list').empty();
          jobsManager.htmlElements.builder(jobsManager.jobs);
        }
      }
      else {
        jobsManager.jobs = data;
        $('#jobs-list').empty();
        jobsManager.htmlElements.builder(jobsManager.jobs);
      }
    });
  },
  checkJobStatus: function (options, cb) {
    var nbError = 0;
    var requestPendingJobTimeout = false;

    if (options.interval) {
      jobStatusTimeout = setTimeout(requestPendingJob, options.interval);
      return;
    }

    requestPendingJob();

    function requestPendingJob() {
      $.ajax({
        type: 'GET',
        contentType: 'application/json',
        url: '/0.1/vrp/jobs/'
          + (options.job.id || options.job.uuid)
          // + (options.format ? options.format : '')
          + '?api_key=' + getParams()["api_key"],
        success: function (job, _, xhr) {

          if (options.interval && (checkJSONJob(job) || checkCSVJob(xhr))) {
            if (debug) console.log("REQUEST PENDING JOB", checkCSVJob(xhr), checkJSONJob(job));
            requestPendingJobTimeout = true;
          }

          nbError = 0;
          cb(null, job, xhr);
      },
        error: function (xhr, status) {
          ++nbError
          if (nbError > 2) {
            cb({ xhr, status });
            return alert(i18n.failureOptim(nbError, status));
          }
          requestPendingJobTimeout = true;
        },
        complete: function () {
          if (requestPendingJobTimeout) {
            requestPendingJobTimeout = false;

            // interval max: 1mins
            options.interval *= 2;
            if (options.interval > 60000)  {
              options.interval = 60000
            }

            jobStatusTimeout = setTimeout(requestPendingJob, options.interval);
          }
        }
      });
  }

},
  stopJobChecking: function () {
    requestPendingJobTimeout = false;
    clearTimeout(jobStatusTimeout);
  }
};

function checkCSVJob(xhr) {
  if (debug) console.log(xhr, xhr.status);
  return (xhr.status !== 200 && xhr.status !== 202);
}

function checkJSONJob(job) {
  if (debug) console.log("JOB: ", job, (job.job && job.job.status !== 'completed'));
  return ((job.job && job.job.status !== 'completed') && typeof job !== 'string')
}
<|endoftext|>


<commit_before>$(document).ready(function() {

  // Fetch and render user reservations
  populateReservations();

  // Add booking widget functionality
  $("#reservation-form").submit(function(event) {
    event.preventDefault();
    var arrivalDate = $("#form--arrival-date").val();
    var nights = $("#form--nights").val();
    var guests = $("#form--guests").val();
    var id = Date.now().toString().substring(3, 11);
    if (!arrivalDate || !nights || !guests) {
      return false;
    }
    addReservation(id, arrivalDate, nights, guests);
    return false;
  });

  $("#offer-notification a").click(function(event) {
    event.preventDefault();
    hideNotificationOffer();
    subscribeUserToNotifications();
  });

  // Periodically check for unconfirmed bookings
  setInterval(checkUnconfirmedReservations, 5000);
});

// Fetches reservations from server and renders them to the page
var populateReservations = function() {
  getReservations().then(renderReservations);
};

// Go over unconfirmed reservations, and verify their status against the server.
var checkUnconfirmedReservations = function() {
  $(".reservation-card--unconfirmed").each(function() {
    $.getJSON("/reservation-details.json", {id: $(this).data("id")}, function(data) {
      updateInObjectStore("reservations", data.id, data);
      updateReservationDisplay(data);
    });
  });
};

var urlBase64ToUint8Array = function(base64String) {
  var padding = "=".repeat((4 - base64String.length % 4) % 4);
  var base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");
  var rawData = window.atob(base64);
  var outputArray = new Uint8Array(rawData.length);
  for (var i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
};

var subscribeUserToNotifications = function() {
  Notification.requestPermission().then(function(permission) {
    if (permission === "granted") {
      var subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          "BKQnRd5V_u942j95_etSdNS6EkYse_HcG-KEbPm_KfvkrGGN_c45G_POcmP8yC_f90SB37ybDoUEcru6Xbr7pTY"
        )
      };
      navigator.serviceWorker.ready.then(function(registration) {
        return registration.pushManager.subscribe(subscribeOptions);
      }).then(function(subscription) {
        var fetchOptions = {
          method: "post",
          headers: new Headers({
            "Content-Type": "application/json"
          }),
          body: JSON.stringify(subscription)
        };
        return fetch("/add-subscription", fetchOptions);
      });
    }
  });
};

var offerNotification = function() {
  if ("Notification" in window &&
      "PushManager" in window &&
      "serviceWorker" in navigator) {
    if (Notification.permission !== "granted") {
      showNotificationOffer();
    } else {
      subscribeUserToNotifications();
    }
  }
};

// Adds a reservation as pending to IndexedDB, the DOM, and the server.
var addReservation = function(id, arrivalDate, nights, guests) {
  var reservationDetails = {
    id:           id,
    arrivalDate:  arrivalDate,
    nights:       nights,
    guests:       guests,
    status:       "Sending"
  };
  addToObjectStore("reservations", reservationDetails);
  renderReservation(reservationDetails);
  if ("serviceWorker" in navigator && "SyncManager" in window) {
    navigator.serviceWorker.ready.then(function(registration) {
      registration.sync.register("sync-reservations");
    });
  } else {
    $.getJSON("/make-reservation", reservationDetails, function(data) {
      updateReservationDisplay(data);
    });
  }
  offerNotification();
};

/* ************************************************************ */
/* The code below this point is used to render to the DOM. It   */
/* completely ignores common sense principles like separation   */
/* of concerns as a trade off for readability.                  */
/* You can ignore it, or you can send angry tweets about it to  */
/* @TalAter                                                     */
/* ************************************************************ */

// Goes over an array of reservations, and renders each of them
var renderReservations = function(reservations) {
  $("div#reservation-loading").hide();
  reservations.forEach(function(reservation) {
    renderReservation(reservation);
  });
};

// Renders a reservation card and adds it to the DOM.
var renderReservation = function(reservation) {
  var newReservation = $(
    "
"+
      ""+
      "
"+
        "
"+
          "Gotham Imperial Hotel"+
          "
1 Imperial Plaza, Gotham.
"+
          "
Check-in: "+reservation["arrivalDate"]+".
"+
          "
"+reservation["nights"]+" nights. "+reservation["guests"]+" guests.
"+
        "
"+
        "
"+
          "
Total price
"+
          "
"+(reservation["price"] ? "§"+reservation["price"]+".99" : "?")+"
"+
        "
"+
      "
"+
      "
"+
        "Modify booking details"+
        "
"+reservation["status"]+"
"+
      "
"+
      "
"+
        "Order number: "+reservation["id"]+""+
        "Booked on: "+(reservation["bookedOn"] ? reservation["bookedOn"] : "n/a")+""+
      "
"+
    "
"
  );
  $("#reservations-container").append(newReservation);
  if (reservation["status"] !== "Confirmed") {
    newReservation.addClass("reservation-card--unconfirmed");
  }

  // Adds an event listener to the modify reservation button.
  $("#reservation-"+reservation["id"]+" a").click(function() {
    var possibleResponses = ["Orders are non-negotiable!", "Not open to discussion!"];
    $(this).text(possibleResponses[Math.floor(Math.random()*possibleResponses.length)]);
    $(this).addClass("reservation-action--error");
    return false;
  });

};

var updateReservationDisplay = function(reservation) {
  var reservationNode = $("#reservation-" + reservation.id);
  $(".reservation-bookedOn", reservationNode).text(reservation.bookedOn);
  $(".reservation-price strong", reservationNode).text("§"+reservation.price+".99");
  $(".reservation-status", reservationNode).text(reservation.status);
  $(".arrivalDate span", reservationNode).text(reservation.arrivalDate);
  if (reservation["status"] !== "Confirmed") {
    reservationNode.addClass("reservation-card--unconfirmed");
  } else {
    reservationNode.removeClass("reservation-card--unconfirmed");
  }
};

var showNotificationOffer = function() {
  $("#offer-notification").removeClass("modal--hide");
};

var hideNotificationOffer = function() {
  $("#offer-notification").addClass("modal--hide");
};

$(document).ready(function() {
  // Prepopulate reservation form from querystring and create reservation
  var url = new URL(window.location);
  var params = url.searchParams;
  if (
    params.has("form--arrival-date") &&
    params.has("form--nights") &&
    params.has("form--guests")
  ) {
    $("#form--arrival-date").val(params.get("form--arrival-date"));
    $("#form--nights").val(params.get("form--nights"));
    $("#form--guests").val(params.get("form--guests"));
    $("form#reservation-form").submit();
    window.history.replaceState(null, "", url.origin + url.pathname);
  }
});
<commit_msg>Reservations are now rendered from latest placed to oldest<commit_after>$(document).ready(function() {

  // Fetch and render user reservations
  populateReservations();

  // Add booking widget functionality
  $("#reservation-form").submit(function(event) {
    event.preventDefault();
    var arrivalDate = $("#form--arrival-date").val();
    var nights = $("#form--nights").val();
    var guests = $("#form--guests").val();
    var id = Date.now().toString().substring(3, 11);
    if (!arrivalDate || !nights || !guests) {
      return false;
    }
    addReservation(id, arrivalDate, nights, guests);
    return false;
  });

  $("#offer-notification a").click(function(event) {
    event.preventDefault();
    hideNotificationOffer();
    subscribeUserToNotifications();
  });

  // Periodically check for unconfirmed bookings
  setInterval(checkUnconfirmedReservations, 5000);
});

// Fetches reservations from server and renders them to the page
var populateReservations = function() {
  getReservations().then(renderReservations);
};

// Go over unconfirmed reservations, and verify their status against the server.
var checkUnconfirmedReservations = function() {
  $(".reservation-card--unconfirmed").each(function() {
    $.getJSON("/reservation-details.json", {id: $(this).data("id")}, function(data) {
      updateInObjectStore("reservations", data.id, data);
      updateReservationDisplay(data);
    });
  });
};

var urlBase64ToUint8Array = function(base64String) {
  var padding = "=".repeat((4 - base64String.length % 4) % 4);
  var base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");
  var rawData = window.atob(base64);
  var outputArray = new Uint8Array(rawData.length);
  for (var i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
};

var subscribeUserToNotifications = function() {
  Notification.requestPermission().then(function(permission) {
    if (permission === "granted") {
      var subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          "BKQnRd5V_u942j95_etSdNS6EkYse_HcG-KEbPm_KfvkrGGN_c45G_POcmP8yC_f90SB37ybDoUEcru6Xbr7pTY"
        )
      };
      navigator.serviceWorker.ready.then(function(registration) {
        return registration.pushManager.subscribe(subscribeOptions);
      }).then(function(subscription) {
        var fetchOptions = {
          method: "post",
          headers: new Headers({
            "Content-Type": "application/json"
          }),
          body: JSON.stringify(subscription)
        };
        return fetch("/add-subscription", fetchOptions);
      });
    }
  });
};

var offerNotification = function() {
  if ("Notification" in window &&
      "PushManager" in window &&
      "serviceWorker" in navigator) {
    if (Notification.permission !== "granted") {
      showNotificationOffer();
    } else {
      subscribeUserToNotifications();
    }
  }
};

// Adds a reservation as pending to IndexedDB, the DOM, and the server.
var addReservation = function(id, arrivalDate, nights, guests) {
  var reservationDetails = {
    id:           id,
    arrivalDate:  arrivalDate,
    nights:       nights,
    guests:       guests,
    status:       "Sending"
  };
  addToObjectStore("reservations", reservationDetails);
  renderReservation(reservationDetails);
  if ("serviceWorker" in navigator && "SyncManager" in window) {
    navigator.serviceWorker.ready.then(function(registration) {
      registration.sync.register("sync-reservations");
    });
  } else {
    $.getJSON("/make-reservation", reservationDetails, function(data) {
      updateReservationDisplay(data);
    });
  }
  offerNotification();
};

/* ************************************************************ */
/* The code below this point is used to render to the DOM. It   */
/* completely ignores common sense principles like separation   */
/* of concerns as a trade off for readability.                  */
/* You can ignore it, or you can send angry tweets about it to  */
/* @TalAter                                                     */
/* ************************************************************ */

// Goes over an array of reservations, and renders each of them
var renderReservations = function(reservations) {
  $("div#reservation-loading").hide();
  reservations.forEach(function(reservation) {
    renderReservation(reservation);
  });
};

// Renders a reservation card and adds it to the DOM.
var renderReservation = function(reservation) {
  var newReservation = $(
    "
"+
      ""+
      "
"+
        "
"+
          "Gotham Imperial Hotel"+
          "
1 Imperial Plaza, Gotham.
"+
          "
Check-in: "+reservation["arrivalDate"]+".
"+
          "
"+reservation["nights"]+" nights. "+reservation["guests"]+" guests.
"+
        "
"+
        "
"+
          "
Total price
"+
          "
"+(reservation["price"] ? "§"+reservation["price"]+".99" : "?")+"
"+
        "
"+
      "
"+
      "
"+
        "Modify booking details"+
        "
"+reservation["status"]+"
"+
      "
"+
      "
"+
        "Order number: "+reservation["id"]+""+
        "Booked on: "+(reservation["bookedOn"] ? reservation["bookedOn"] : "n/a")+""+
      "
"+
    "
"
  );
  $("#reservations-container").prepend(newReservation);
  if (reservation["status"] !== "Confirmed") {
    newReservation.addClass("reservation-card--unconfirmed");
  }

  // Adds an event listener to the modify reservation button.
  $("#reservation-"+reservation["id"]+" a").click(function() {
    var possibleResponses = ["Orders are non-negotiable!", "Not open to discussion!"];
    $(this).text(possibleResponses[Math.floor(Math.random()*possibleResponses.length)]);
    $(this).addClass("reservation-action--error");
    return false;
  });

};

var updateReservationDisplay = function(reservation) {
  var reservationNode = $("#reservation-" + reservation.id);
  $(".reservation-bookedOn", reservationNode).text(reservation.bookedOn);
  $(".reservation-price strong", reservationNode).text("§"+reservation.price+".99");
  $(".reservation-status", reservationNode).text(reservation.status);
  $(".arrivalDate span", reservationNode).text(reservation.arrivalDate);
  if (reservation["status"] !== "Confirmed") {
    reservationNode.addClass("reservation-card--unconfirmed");
  } else {
    reservationNode.removeClass("reservation-card--unconfirmed");
  }
};

var showNotificationOffer = function() {
  $("#offer-notification").removeClass("modal--hide");
};

var hideNotificationOffer = function() {
  $("#offer-notification").addClass("modal--hide");
};

$(document).ready(function() {
  // Prepopulate reservation form from querystring and create reservation
  var url = new URL(window.location);
  var params = url.searchParams;
  if (
    params.has("form--arrival-date") &&
    params.has("form--nights") &&
    params.has("form--guests")
  ) {
    $("#form--arrival-date").val(params.get("form--arrival-date"));
    $("#form--nights").val(params.get("form--nights"));
    $("#form--guests").val(params.get("form--guests"));
    $("form#reservation-form").submit();
    window.history.replaceState(null, "", url.origin + url.pathname);
  }
});
<|endoftext|>


<commit_before><commit_msg>Fixes a bug due to ckconfig<commit_after><|endoftext|>


<commit_before>var brush;
var canvasWrapper;

var CanvasWrapper = function(id, socket) {
  this.id = id;
  this.socket = socket;
  this.canvases = {};
};

CanvasWrapper.prototype.message = function(headline, message) {
  $('#modelWindow h1').text(headline);
  $('#modelWindow p').text(message);
  $('#modelMask').fadeIn(300, function() {
    $('#modelWindow').fadeIn(400);
  });
};

CanvasWrapper.prototype.addView = function(brushId, canvas) {
  // ADD A NODE CANVAS NODE
  // ASSIGN THIS NODE TO CANVAS, SET CTX
  this.canvases[brushId] = canvas;
};

CanvasWrapper.prototype.assign = function(data) {
  this.canvases[data.brushId].calculateMove(data.aX, data.aY, data.aZ, data.color, data.brushSize);
};

var Brush = function() {
  this.socket = {};
  this.id = 0;
  this.color = '#FF0000';
  this.brushSize = 1;
  this.initDevice();
};

Brush.prototype.initDevice = function() {
  $('#drawOptions').show();
};

Brush.prototype.emit = function(event, args) {
  this.socket.emit(event, args);
};

Brush.prototype.message = function(headline, message) {
  $('#modelWindow h1').text(headline);
  $('#modelWindow p').text(message);
  $('#modelMask').fadeIn(300, function() {
    $('#modelWindow').fadeIn(400);
  });
};

var Canvas = function(brushId) {
  this.brushId = brushId;
  this.height = $(window).height();
  this.width = $(window).width();
  this.x = this.width / 2;
  this.y = this.height / 2;
  this.lastX = this.x;
  this.lastY = this.y;
  this.
};

Canvas.prototype.emit = function(event, args) {
  this.socket.emit(events, args);
};

Canvas.prototype.draw = function(brushId, color, brushSize) {
  // clear old data.
  //this.ctx.clearRect(0,0,this.width, this.height);

  this.ctx.beginPath();
  this.ctx.strokeStyle = color;
  this.ctx.lineWidth = brushSize;
  this.ctx.lineJoin = "round";
  this.ctx.moveTo(this.lastX, this.lastY);
  this.ctx.lineTo(this.x, this.y);
  this.ctx.closePath();
  this.ctx.stroke();

  this.lastX = this.x;
  this.lastY = this.y;
};

Canvas.prototype.calculateMove = function(aX, aY, aZ, color, brushSize) {
  this.x += aX;
  this.y += aZ;
  this.draw(color, brushSize);
};

var init = function(server) {
  var socket = io.connect(server);
  if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
    brush = new Brush();
    socket.emit('device', { type: 'brush' });
    brush.socket = socket;
    socket.on('welcome', function (data) {
      brush.id = data.id;
      brush.message('Welcome!', 'Use your phone like you would a paintbrush, and draw on the screen!');
    });

  } else {
    $('#canvasShell').show();
    socket.emit('device', { type: 'canvas' });
    socket.on('welcome', function(data) {
      canvasWrapper = new CanvasWrapper(data.id, socket);
      canvasWrapper.message('Welcome!', "Connect your phone to " + server + " to start drawing!");
    });
  }

  socket.on('brushAdd', function(data) {
    var canvas = new Canvas(data.brushId);
    canvasWrapper.addView(data.brushId, canvas);
  });

  socket.on('draw', function(data) {
    canvasWrapper.assign(data);
  });

};

$(document).ready(function() {

  $('#brushSize').on('touchend', function(e){
    brush.brushSize = this.value;
    brush.emit('brushChange', { brushSize: this.value });
  });

  $('.colorBlock').on('touchstart', function(e) {
    var color = $(this).data('color');
    brush.color = color;
    brush.emit('colorChange', { color: color });
  });

  // geolocation - have not touched this yet
  // var position;
  // var watchId = navigator.geolocation.watchPosition(function(pos) {
  //   position = pos;
  // });

  window.addEventListener('devicemotion', function(event) {
    var aX = Math.floor(event.acceleration.x);
    var aY = Math.floor(event.acceleration.y);
    var aZ = Math.floor(event.acceleration.z);
    brush.emit("paint", {
      brushId: brush.id,
      aX: aX,
      aY: aY,
      aZ: aZ,
      color: brush.color,
      brushSize: brush.brushSize
    });
  }, false);

  $('#modelWindow button').on('click', function(e) {
    e.preventDefault();
    $('#modelWindow').fadeOut(300, function() {
      $('#modelMask').fadeOut(200);
      $('#modelWindow h1').text('');
      $('#modelWindow p').text('');
    });
  });

});
<commit_msg>remove old js files<commit_after><|endoftext|>


<commit_before>$(document).ready(function () {
	var advice = ["That's terrible! You should knock that off!",
				  "Seriously? Why don't you grow the hell up?!",
				  "Aren't you a little old for that crap?",
				  "You are bad and you should feel bad!",
				  "Gross. You are gross.",
				  "Are you going to suck all your life?",
				  "What? Who does that?!",
				  "I thought you were better than that.",
				  "My disapproval is overwhelming.",
				  " ಠ__ಠ ",
				  "Are you freaking kidding me?",
				  "NO! Bad!",
				  "And when do you plan on becoming an adult?",
				  "That is totally unacceptable.",
				  "You should be utterly ashamed.",
				  "Ugh! That's horrible!",
				  "A kitten dies everytime you do that.",
				  "I can't believe you are that disgusting."]

	$('#bad_habit').focus();

	$('form').on('submit', function (event) {
		event.preventDefault();

		var bad_habit = $('#bad_habit').val();

			if (bad_habit == '') {
				bad_habit = "don't fill in forms"
			}

			bad_habit = changePronouns(bad_habit);

		$('#habit_repeater').text(bad_habit + "?");

		$('#advice').text(advice[Math.floor(Math.random() * 18)]);

		$('#bad_habit').val("");
	});

	function changePronouns(phrase) {
		var startWithI = phrase.substr(0, 2).toUpperCase();
		var startWithMy = phrase.substr(0, 3).toUpperCase();
		var newPhrase;

		if (startWithI == "I ")	{
			newPhrase = phrase.replace(/I /gi, "You ");
		} else if ( startWithMy == "MY " ) {
			newPhrase = phrase.replace(/My /gi, "Your ");
		} else {
			newPhrase = "You " + phrase;
		}

		newPhrase = newPhrase.replace(/ I /gi, " you ");
		newPhrase = newPhrase.replace(/ my /gi, " your ");
		newPhrase = newPhrase.replace(/ me /gi, " you ");

		return newPhrase;
	};

});<commit_msg>Add whitespace trim to Tough Love<commit_after>$(document).ready(function () {
	var advice = ["That's terrible! You should knock that off!",
				  "Seriously? Why don't you grow the hell up?!",
				  "Aren't you a little old for that crap?",
				  "You are bad and you should feel bad!",
				  "Gross. You are gross.",
				  "Are you going to suck all your life?",
				  "What? Who does that?!",
				  "I thought you were better than that.",
				  "My disapproval is overwhelming.",
				  " ಠ__ಠ ",
				  "Are you freaking kidding me?",
				  "NO! Bad!",
				  "And when do you plan on becoming an adult?",
				  "That is totally unacceptable.",
				  "You should be utterly ashamed.",
				  "Ugh! That's horrible!",
				  "A kitten dies everytime you do that.",
				  "I can't believe you are that disgusting."]

	$('#bad_habit').focus();

	$('form').on('submit', function (event) {
		event.preventDefault();

		var bad_habit = $('#bad_habit').val().replace(/^\s+|\s+$/g, '');

			if (bad_habit == '') {
				bad_habit = "don't fill in forms"
			}

			bad_habit = changePronouns(bad_habit);

		$('#habit_repeater').text(bad_habit + "?");

		$('#advice').text(advice[Math.floor(Math.random() * 18)]);

		$('#bad_habit').val("");
	});

	function changePronouns(phrase) {
		var startWithI = phrase.substr(0, 2).toUpperCase();
		var startWithMy = phrase.substr(0, 3).toUpperCase();
		var newPhrase;

		if (startWithI == "I ")	{
			newPhrase = phrase.replace(/I /gi, "You ");
		} else if ( startWithMy == "MY " ) {
			newPhrase = phrase.replace(/My /gi, "Your ");
		} else {
			newPhrase = "You " + phrase;
		}

		newPhrase = newPhrase.replace(/ I /gi, " you ");
		newPhrase = newPhrase.replace(/ my /gi, " your ");
		newPhrase = newPhrase.replace(/ me /gi, " you ");

		return newPhrase;
	};

});<|endoftext|>


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

(function() {
	var League = {};

	if (typeof exports !== 'undefined') {
		if (typeof module !== 'undefined' && module.exports) {
              exports = module.exports = League;
        }
		exports.lolapi = League;
	}

	//Private methods and variables
	var _authKey;
	var _baseurl = 'http://prod.api.pvp.net/api';
	var _region = '/na';
	var _championUrl = '/v1.1/champion';
	var _gameUrl = '/v1.1/game/by-summoner/'
	var _leagueUrl = '/v2.1/league/by-summoner/'
	var _statsUrl = '/v1.1/stats/by-summoner/'
	var _summonerUrl = '/v1.1/summoner/'
	var _teamUrl = '/v2.1/team/by-summoner/'

	function _craftUrl(urlType, lolRequired) {
		var base = _baseurl;
		if(lolRequired) base += '/lol'
		return base + _region + urlType + '?api_key=' + _authKey;
	}

	function _makeRequest(url, errmsg, callback) {
		_getRequest(url, function(err, result) {
			if(err) {
				callback(new Error(errmsg + err));
			} else {
				callback(null, result);
			}
		});
	}

	function _getRequest(path, callback) {
		var response;

		http.get(path, function(response) {
			response.on('data', function(chunk) {
				response += chunk;
			});

			response.on('error', function(error) {
				callback(error, null);
			});

			response.on('end', function() {
				response = response.replace("[object Object]", ""); //I don't know why this gets appended, but it is invalid JSON
				response = JSON.parse(response);
				if(response['status'] && response['status']['message'] != 200) {
					callback(response['status']['message'], null);
				} else {
					callback(null, response);
				}
			});
		});
	}

	//Public Methods
	League.Stats = {};
	League.Summoner = {};

	League.init = function(key, region) {
		_authKey = key;
		if(region) _region = '/' + region;
	}

	League.Champions = function(callback) {
		var url = _craftUrl(_championUrl, true);
		_makeRequest(url, 'Error getting champions: ', callback);
	}

	League.Game = function(summonerId, callback) {
		var url = _craftUrl(_gameUrl + summonerId + '/recent', true);
		_makeRequest(url, 'Error getting recent games: ', callback);
	}

	League.League = function(summonerId, callback) {
		var url = _craftUrl(_leagueUrl + summonerId);
		_makeRequest(url, 'Error getting league data: ', callback);
	}

	League.Stats.Summary = function(summonerId, callback) {
		var url = _craftUrl(_statsUrl + summonerId + '/summary', true);
		_makeRequest(url, 'Error getting summary data: ', callback);
	}

	League.Stats.Ranked = function(summonerId, callback) {
		var url = _craftUrl(_statsUrl + summonerId + '/ranked', true);
		_makeRequest(url, 'Error getting ranked data: ', callback);
	}

	League.Summoner.Masteries = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId + '/masteries', true);
		_makeRequest(url, 'Error getting mastery data: ', callback);
	}

	League.Summoner.Runes = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId + '/runes', true);
		_makeRequest(url, 'Error getting rune data: ', callback);
	}

	League.Summoner.ByName= function(name, callback) {
		name = name.split(" ").join("");
		var url = _craftUrl(_summonerUrl + 'by-name/' + name, true);
		_makeRequest(url, 'Error getting summoner data using name: ', callback);
	}

	League.Summoner.ID = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId, true);
		_makeRequest(url, 'Error getting summoner data: ', callback);
	}

	League.Summoner.ListNames = function(ids, callback) {
		var url = _craftUrl(_summonerUrl + ids + '/name', true);
		_makeRequest(url, 'Error getting summoner data using list of ids: ', callback);
	}

	League.Team = function(summonerId, callback) {
		var url = _craftUrl(_teamUrl + summonerId);
		_makeRequest(url, 'Error getting summoner teams info: ', callback);
	}

}).call(this);<commit_msg>update weird looking if statement<commit_after>var http = require('http');

(function() {
	var League = {};

	if (typeof exports !== 'undefined') {
		if (typeof module !== 'undefined' && module.exports) {
              		exports = module.exports = League;
        	}
		exports.lolapi = League;
	}

	//Private methods and variables
	var _authKey;
	var _baseurl = 'http://prod.api.pvp.net/api';
	var _region = '/na';
	var _championUrl = '/v1.1/champion';
	var _gameUrl = '/v1.1/game/by-summoner/'
	var _leagueUrl = '/v2.1/league/by-summoner/'
	var _statsUrl = '/v1.1/stats/by-summoner/'
	var _summonerUrl = '/v1.1/summoner/'
	var _teamUrl = '/v2.1/team/by-summoner/'

	function _craftUrl(urlType, lolRequired) {
		var base = _baseurl;
		if(lolRequired) base += '/lol'
		return base + _region + urlType + '?api_key=' + _authKey;
	}

	function _makeRequest(url, errmsg, callback) {
		_getRequest(url, function(err, result) {
			if(err) {
				callback(new Error(errmsg + err));
			} else {
				callback(null, result);
			}
		});
	}

	function _getRequest(path, callback) {
		var response;

		http.get(path, function(response) {
			response.on('data', function(chunk) {
				response += chunk;
			});

			response.on('error', function(error) {
				callback(error, null);
			});

			response.on('end', function() {
				response = response.replace("[object Object]", ""); //I don't know why this gets appended, but it is invalid JSON
				response = JSON.parse(response);
				if(response['status'] && response['status']['message'] != 200) {
					callback(response['status']['message'], null);
				} else {
					callback(null, response);
				}
			});
		});
	}

	//Public Methods
	League.Stats = {};
	League.Summoner = {};

	League.init = function(key, region) {
		_authKey = key;
		if(region) _region = '/' + region;
	}

	League.Champions = function(callback) {
		var url = _craftUrl(_championUrl, true);
		_makeRequest(url, 'Error getting champions: ', callback);
	}

	League.Game = function(summonerId, callback) {
		var url = _craftUrl(_gameUrl + summonerId + '/recent', true);
		_makeRequest(url, 'Error getting recent games: ', callback);
	}

	League.League = function(summonerId, callback) {
		var url = _craftUrl(_leagueUrl + summonerId);
		_makeRequest(url, 'Error getting league data: ', callback);
	}

	League.Stats.Summary = function(summonerId, callback) {
		var url = _craftUrl(_statsUrl + summonerId + '/summary', true);
		_makeRequest(url, 'Error getting summary data: ', callback);
	}

	League.Stats.Ranked = function(summonerId, callback) {
		var url = _craftUrl(_statsUrl + summonerId + '/ranked', true);
		_makeRequest(url, 'Error getting ranked data: ', callback);
	}

	League.Summoner.Masteries = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId + '/masteries', true);
		_makeRequest(url, 'Error getting mastery data: ', callback);
	}

	League.Summoner.Runes = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId + '/runes', true);
		_makeRequest(url, 'Error getting rune data: ', callback);
	}

	League.Summoner.ByName= function(name, callback) {
		name = name.split(" ").join("");
		var url = _craftUrl(_summonerUrl + 'by-name/' + name, true);
		_makeRequest(url, 'Error getting summoner data using name: ', callback);
	}

	League.Summoner.ID = function(summonerId, callback) {
		var url = _craftUrl(_summonerUrl + summonerId, true);
		_makeRequest(url, 'Error getting summoner data: ', callback);
	}

	League.Summoner.ListNames = function(ids, callback) {
		var url = _craftUrl(_summonerUrl + ids + '/name', true);
		_makeRequest(url, 'Error getting summoner data using list of ids: ', callback);
	}

	League.Team = function(summonerId, callback) {
		var url = _craftUrl(_teamUrl + summonerId);
		_makeRequest(url, 'Error getting summoner teams info: ', callback);
	}

}).call(this);
<|endoftext|>


<commit_before>define([
    'underscore',
    'backbone',
    'models/core/BaseModel',
    'models/core/UserModel',
    'models/project/ProjectModel',
    'collections/project/ProjectsCollection'
], function(_, Backbone, BaseModel, UserModel, ProjectModel, ProjectsCollection) {

    var SessionModel = BaseModel.extend({

      	defaults: {
      		logged_in : false,
            user_id: 0
      	},

      	initialize: function (options) {
            _.bindAll(this, 'onProjectPositioned', 'updateSessionUser', 'projectRemoved', 'updateSessionProject', 'checkAuth', 'login', 'logout', 'signup');

            // Singleton user object
            // Access or listen on this throughout any module with app.session.user
            this.user = new UserModel({});
            this.project = new ProjectModel({});

            Backbone.on('project:position', this.onProjectPositioned);
      	},

        onProjectPositioned: function(objects) {
            if (typeof objects !== typeof Array || objects.length === 0) {
                this.projectRemoved();
            } else {
                this.set('project_id', objects[0].id);
            }
        },

        url: function() {
            return 'api/auth';
        },

        // Fxn to update user attributes after receiving API response
        updateSessionUser: function( userData ){
            this.user.set(_.pick(userData, _.keys(this.user.defaults)));
        },

        projectRemoved: function() {
            this.unset('project_id');
            this.unset('project');
        },

        updateSessionProject: function(project) {
            if (typeof project === typeof undefined) {
                this.projectRemoved();
            } else {
                var projectId = parseInt(project.id);
                if (projectId !== this.get('project_id')) {
                    this.set('project_id', project.id);
                    this.set('project', project);
                }
            }
        },

        /*
         * Check for session from API
         * The API will parse client cookies using its secret token
         * and return a user object if authenticated
         */
        checkAuth: function(callback, args) {
            var self = this;
            this.fetch({
                wait: true,
                success: function(mod, res, options){
                    if(!res.error && res.id){
                        self.updateSessionUser(res);
                        self.set({ 'logged_in' : true });
                        self.set({ 'user_id': parseInt(res.id) });
                        var project_id = options.xhr.getResponseHeader('X-NESTORQA-PROJECT-ID');
                        if (typeof project_id !== typeof undefined && project_id !== null && project_id != self.get('project_id')) {
                            self.set('project_id', project_id);
                            new ProjectsCollection().position(project_id, false);
                        }
                        if('success' in callback) callback.success(mod, res, options);
                    } else {
                        self.set({ 'logged_in': false });
                        self.set({ 'user_id': 0 });
                        self.set({ 'project_id': 0 });
                        if('error' in callback) callback.error(mod, res, options);
                    }
                }, error:function(mod, res){
                    self.set({ 'logged_in': false });
                    self.set({ 'user_id': 0 });
                    self.set({ 'project_id': 0 });
                    if('error' in callback) callback.error(mod, res);
                }
            }).complete( function(){
                if('complete' in callback) callback.complete();
            });
        },

        login: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'login' }), callback);
        },

        logout: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'logout' }), callback);
        },

        signup: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'signup' }), callback);
        }

    });

    return SessionModel;

});
<commit_msg>Assure we use an int for project ID<commit_after>define([
    'underscore',
    'backbone',
    'models/core/BaseModel',
    'models/core/UserModel',
    'models/project/ProjectModel',
    'collections/project/ProjectsCollection'
], function(_, Backbone, BaseModel, UserModel, ProjectModel, ProjectsCollection) {

    var SessionModel = BaseModel.extend({

      	defaults: {
      		logged_in : false,
            user_id: 0
      	},

      	initialize: function (options) {
            _.bindAll(this, 'onProjectPositioned', 'updateSessionUser', 'projectRemoved', 'updateSessionProject', 'checkAuth', 'login', 'logout', 'signup');

            // Singleton user object
            // Access or listen on this throughout any module with app.session.user
            this.user = new UserModel({});
            this.project = new ProjectModel({});

            Backbone.on('project:position', this.onProjectPositioned);
      	},

        onProjectPositioned: function(objects) {
            if (typeof objects !== typeof Array || objects.length === 0) {
                this.projectRemoved();
            } else {
                this.set('project_id', parseInt(objects[0].id));
            }
        },

        url: function() {
            return 'api/auth';
        },

        // Fxn to update user attributes after receiving API response
        updateSessionUser: function( userData ){
            this.user.set(_.pick(userData, _.keys(this.user.defaults)));
        },

        projectRemoved: function() {
            this.unset('project_id');
            this.unset('project');
        },

        updateSessionProject: function(project) {
            if (typeof project === typeof undefined) {
                this.projectRemoved();
            } else {
                var projectId = parseInt(project.id);
                if (projectId !== this.get('project_id')) {
                    this.set('project_id', parseInt(project.id));
                    this.set('project', project);
                }
            }
        },

        /*
         * Check for session from API
         * The API will parse client cookies using its secret token
         * and return a user object if authenticated
         */
        checkAuth: function(callback, args) {
            var self = this;
            this.fetch({
                wait: true,
                success: function(mod, res, options){
                    if(!res.error && res.id){
                        self.updateSessionUser(res);
                        self.set({ 'logged_in' : true });
                        self.set({ 'user_id': parseInt(res.id) });
                        var project_id = options.xhr.getResponseHeader('X-NESTORQA-PROJECT-ID');
                        if (typeof project_id !== typeof undefined && project_id !== null && project_id != self.get('project_id')) {
                            self.set('project_id', parseInt(project_id));
                            new ProjectsCollection().position(project_id, false);
                        }
                        if('success' in callback) callback.success(mod, res, options);
                    } else {
                        self.set({ 'logged_in': false });
                        self.set({ 'user_id': 0 });
                        self.set({ 'project_id': 0 });
                        if('error' in callback) callback.error(mod, res, options);
                    }
                }, error:function(mod, res){
                    self.set({ 'logged_in': false });
                    self.set({ 'user_id': 0 });
                    self.set({ 'project_id': 0 });
                    if('error' in callback) callback.error(mod, res);
                }
            }).complete( function(){
                if('complete' in callback) callback.complete();
            });
        },

        login: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'login' }), callback);
        },

        logout: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'logout' }), callback);
        },

        signup: function(opts, callback, args){
            this.postAuth(_.extend(opts, { method: 'signup' }), callback);
        }

    });

    return SessionModel;

});
<|endoftext|>


<commit_before>$(document).ready(function () {
    loadETFFunds();
    //ajaxIndex();
});

function ajaxIndex() {
    var getUrl = '/person/etf-fund-analysis/ajax-index';
    var getData = {
        "params": $('#formSearch').serializeObject()
    };
    var method = 'get';
    var successFunc = function (result) {
        if (typeof result.data != "undefined") {
            $('#eft_fund_analysis_chart').highcharts({
                chart: {
                    zoomType: 'x'
                },
                title: {
                    text: 'ETF Fund Analysis'
                },
                subtitle: {
                    text: document.ontouchstart === undefined ?
                        '鼠标拖动可以进行缩放' : '手势操作进行缩放'
                },
                xAxis: {
                    type: 'datetime',
                    dateTimeLabelFormats: {
                        millisecond: '%H:%M:%S.%L',
                        second: '%H:%M:%S',
                        minute: '%H:%M',
                        hour: '%H:%M',
                        day: '%m-%d',
                        week: '%m-%d',
                        month: '%Y-%m',
                        year: '%Y'
                    }
                },
                yAxis: {
                    min: 0,
                    title: {
                        text: 'Net Value'
                    }
                },
                tooltip: {
                    tooltip: {
                        dateTimeLabelFormats: {
                            millisecond: '%H:%M:%S.%L',
                            second: '%H:%M:%S',
                            minute: '%H:%M',
                            hour: '%H:%M',
                            day: '%Y-%m-%d',
                            week: '%m-%d',
                            month: '%Y-%m',
                            year: '%Y'
                        }
                    }
                },
                plotOptions: {
                    area: {
                        fillColor: {
                            linearGradient: {
                                x1: 0,
                                y1: 0,
                                x2: 0,
                                y2: 1
                            },
                            stops: [
                                [0, Highcharts.getOptions().colors[0]],
                                [1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
                            ]
                        },
                        marker: {
                            radius: 2
                        },
                        lineWidth: 1,
                        states: {
                            hover: {
                                lineWidth: 1
                            }
                        },
                        threshold: null
                    }
                },
                series: [
                    {
                        type: 'area',
                        name: 'Unit Net Value',
                        data: result.data.unitNetData
                    },
                    {
                        type: 'area',
                        name: 'Accum Net Value',
                        data: result.data.accumNetData
                    }
                ]
            });
            $('#startDate').val(result.searchData.startDate);
            $('#endDate').val(result.searchData.endDate);
        } else {
            alert(result.error.message);
        }
    };
    jAjaxWidget.additionFunc(getUrl, getData, successFunc, method);
}

$('#btn_search').on('click', function (event) {
    event.preventDefault();
    ajaxIndex();
});

function loadETFFunds() {
    var getUrl = '/person/etf-fund/get-fund-list';
    var getData = {
        "params": {}
    };
    var method = 'get';
    var successFunc = function (result) {
        if (typeof result.data != "undefined") {
            $('#fuid').empty().append('<option value="0">请选择ETF基金</option>');
            for (var i = 0; i < result.data.currentItemCount; i++) {
                $('#fuid').append($('<option>', {
                    value: result.data.items[i]['fuid'],
                    text: result.data.items[i]['name']
                }));
            }
        } else {
            alert(result.error.message);
        }
    };
    jAjaxWidget.additionFunc(getUrl, getData, successFunc, method);
}
/*  --------------------------------------------------------------------------------------------------------  */<commit_msg>+ update etf fund analysis js code<commit_after>$(document).ready(function () {
    loadETFFunds();
    //ajaxIndex();
});

function ajaxIndex() {
    var getUrl = '/person/etf-fund-analysis/ajax-index';
    var getData = {
        "params": $('#formSearch').serializeObject()
    };
    var method = 'get';
    var successFunc = function (result) {
        if (typeof result.data != "undefined") {
            $('#eft_fund_analysis_chart').highcharts({
                chart: {
                    zoomType: 'x',
                    type: 'area'
                },
                title: {
                    text: 'ETF Fund Analysis'
                },
                subtitle: {
                    text: document.ontouchstart === undefined ?
                        '鼠标拖动可以进行缩放' : '手势操作进行缩放'
                },
                xAxis: {
                    type: 'datetime',
                    dateTimeLabelFormats: {
                        millisecond: '%H:%M:%S.%L',
                        second: '%H:%M:%S',
                        minute: '%H:%M',
                        hour: '%H:%M',
                        day: '%m-%d',
                        week: '%m-%d',
                        month: '%Y-%m',
                        year: '%Y'
                    }
                },
                yAxis: {
                    min: 0,
                    title: {
                        text: 'Net Value'
                    }
                },
                tooltip: {
                    tooltip: {
                        dateTimeLabelFormats: {
                            millisecond: '%H:%M:%S.%L',
                            second: '%H:%M:%S',
                            minute: '%H:%M',
                            hour: '%H:%M',
                            day: '%Y-%m-%d',
                            week: '%m-%d',
                            month: '%Y-%m',
                            year: '%Y'
                        }
                    },
                    shared: true
                },
                plotOptions: {
                    area: {
                        fillColor: {
                            linearGradient: {
                                x1: 0,
                                y1: 0,
                                x2: 0,
                                y2: 1
                            },
                            stops: [
                                [0, Highcharts.getOptions().colors[0]],
                                [1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
                            ]
                        },
                        marker: {
                            radius: 2
                        },
                        lineWidth: 1,
                        states: {
                            hover: {
                                lineWidth: 1
                            }
                        },
                        threshold: null
                    },
                    areaspline: {
                        fillOpacity: 0.5
                    }
                },
                series: [
                    {
                        name: 'Unit Net Value',
                        data: result.data.unitNetData
                    },
                    {
                        name: 'Accum Net Value',
                        data: result.data.accumNetData
                    }
                ]
            });
            $('#startDate').val(result.searchData.startDate);
            $('#endDate').val(result.searchData.endDate);
        } else {
            alert(result.error.message);
        }
    };
    jAjaxWidget.additionFunc(getUrl, getData, successFunc, method);
}

$('#btn_search').on('click', function (event) {
    event.preventDefault();
    ajaxIndex();
});

function loadETFFunds() {
    var getUrl = '/person/etf-fund/get-fund-list';
    var getData = {
        "params": {}
    };
    var method = 'get';
    var successFunc = function (result) {
        if (typeof result.data != "undefined") {
            $('#fuid').empty().append('<option value="0">请选择ETF基金</option>');
            for (var i = 0; i < result.data.currentItemCount; i++) {
                $('#fuid').append($('<option>', {
                    value: result.data.items[i]['fuid'],
                    text: result.data.items[i]['name']
                }));
            }
        } else {
            alert(result.error.message);
        }
    };
    jAjaxWidget.additionFunc(getUrl, getData, successFunc, method);
}
/*  --------------------------------------------------------------------------------------------------------  */<|endoftext|>


<commit_before>var Etcd = require('node-etcd');
var fs = require('fs');
var Handlebars = require('handlebars');
var debounce = require('debounce');
var hash = require('object-hash');

var spawn = require('child_process').spawn

var config = '/data/luotsi/haproxy.cfg';
var haproxy_bin = '/usr/sbin/haproxy-systemd-wrapper';

var etcd = new Etcd(process.env.HOST_IP, process.env.ETCD_PORT);

var Haproxy = function (config_path) {
	var self = this;
	this.config_path = config_path;
	this.pid = -1;
	this.pid_file = '/run/haproxy.pid';
	this.start = function () {
		self.haprocess = spawn(haproxy_bin, ['-f', self.config_path, '-p', self.pid_file]);
		self.pid = self.haprocess.pid;
		self.haprocess.on('close', function (code) {
			process.exit(code);
		});
	};
	this.running = function (cb) {
			fs.exists('/proc/' + self.pid, function (exists) {
				return cb(null, exists)
			});
	};
	this.reload = function () {
		process.kill(self.pid, 'SIGUSR1');
	};
	this.terminate = function () {
		process.kill(self.pid, 'SIGKILL');
	};

	return this;
}

var haproxy = new Haproxy(config);
process.on('SIGTERM', function() {
	haproxy.terminate();
	process.exit(code);
});

/*
* LOG COLLECTOR
*/
var PORT = 514;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
	var address = server.address();
	console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
	client.send(message, 0, message.length, 5145, 'monitor-in.slush.xyz');
});

server.bind(PORT, HOST);

//etcd.set("/services/testservice/10.10.0.1", "10.10.0.1:4555");
//etcd.set("/services/testservice/10.10.0.2", "10.10.0.2:4555");
//etcd.set("/services/testservice/10.10.0.3", "10.10.0.3:4555");
//etcd.set("/services/testservice2/10.10.0.1", "10.10.0.2:4556");

var hostnames = {};

var metadata = {};

var tservices = "";
var hash_meta = "";

hostname_watcher = etcd.watcher("/hostnames", null, {recursive: true});
hostname_watcher.on("change", debounce(fetchHostnames, 200));
hostname_watcher.on('error', console.log);

service_watcher = etcd.watcher("/services", null, {recursive: true});
service_watcher.on("change", debounce(fetchServices, 200));
service_watcher.on('error', console.log);

service_watcher = etcd.watcher("/metadata", null, {recursive: true});
service_watcher.on("change", debounce(fetchMetadata, 200));
service_watcher.on('error', console.log);

fetchHostnames();
fetchMetadata();

fetchServices(1);

function fetchServices (cascade) {
	etcd.get("/services", {recursive:true}, function (err, listing) {
		if (err) { console.error(err); return;}
		var services = [];
		try {
			services = listing.node.nodes.map(handleService);
		} catch (e) {console.log(e, e.stack);}
		services.sort(function (a, b) {
			return (a.name > b.name) ? -1 : 1;
		});
		if (tservices != hash(services) || cascade == 1) {
			console.log("CAPTAIN: rewriting loadbalancer");
			writeHaproxyConfig(services);
		}
		tservices = hash(services);
	});
}

function fetchHostnames () {
	etcd.get("/hostnames", function (err, kvs) {
		var hnames = [];
		try {
			kvs.node.nodes.forEach(function (hostname) {
				var key = hostname.key.replace('/hostnames/', '');
				hnames[key] = hostname.value;
			});
		} catch (e) {}
		hostnames = hnames;
		fetchServices(1);
	});
}

function fetchMetadata () {
	etcd.get("/metadata", function (err, kvs) {
		var metas = [];
		try {
			kvs.node.nodes.forEach(function (metadata) {
				var key = metadata.key.replace('/metadata/', '');
				metas[key] = JSON.parse(metadata.value);
			});
		} catch (e) {}
		metadata = metas;
		if(hash(metadata) !== hash_meta) {
			fetchServices(1);
		}
		hash_meta = hash(metadata);
	});
}

function writeHaproxyConfig (services) {
	fs.readFile('haproxy.template', {encoding:'utf-8'}, function (err, content) {
		var template = Handlebars.compile(content);
		var output = template({services: services});
		//console.log(output, services);
		fs.writeFile('haproxy.cfg', output, function (err) {
			haproxy.running(function (err, running) {
				if (running) {
					console.log("LB is running... Reloading!");
					haproxy.reload();
				} else {
					console.log("LB is NOT running. Starting!");
					haproxy.start();
				}
			});
		});
	});
}
/*
function writeFleetdConfig (template, service) {
	fs.readFile(template, {encoding: 'utf-8'}, function (err, content) {
		var template = Handlebars.compile(content);
		var output = template({service: service});
		console.log(output);
	});
}
function writeServiceConfig (service) {
	writeFleetdConfig('discovery.tpl', service);
	writeFleetdConfig('webservice.tpl', service);
}
*/

function handleService (service_item) {
	var service = {};
	service.name = service_item.key.replace('/services/', '');
	service.host = hostnames[service.name] ? hostnames[service.name] : service.name+".slush.org";
	service.meta = metadata[service.name] ? metadata[service.name] : {};
	service.servers = [];
	try {
		service.servers = service_item.nodes
		.map(handleServers)
		.sort(function (a, b) {
			return (a.name > b.name) ? -1 : 1;
		});
	} catch(e) {
		console.log("CAPTAIN: No backend for " + service.name);
	}
	return service;
}

function handleServers (server_item) {
	var server = {};
	var ks = server_item.key.split('/');
	server.name = ip2hex(ks[ks.length-1]);
	server.address = server_item.value;
	return server;
}

function ip2hex (ip) {
	var parts = ip.split('.');
	ints = parts.map(function(part){return parseInt(part);});
	var k = new Buffer(ints).toString('hex');
	return k;
}
<commit_msg>Must send USR2<commit_after>var Etcd = require('node-etcd');
var fs = require('fs');
var Handlebars = require('handlebars');
var debounce = require('debounce');
var hash = require('object-hash');

var spawn = require('child_process').spawn

var config = '/data/luotsi/haproxy.cfg';
var haproxy_bin = '/usr/sbin/haproxy-systemd-wrapper';

var etcd = new Etcd(process.env.HOST_IP, process.env.ETCD_PORT);

var Haproxy = function (config_path) {
	var self = this;
	this.config_path = config_path;
	this.pid = -1;
	this.pid_file = '/run/haproxy.pid';
	this.start = function () {
		self.haprocess = spawn(haproxy_bin, ['-f', self.config_path, '-p', self.pid_file]);
		self.pid = self.haprocess.pid;
		self.haprocess.on('close', function (code) {
			process.exit(code);
		});
	};
	this.running = function (cb) {
			fs.exists('/proc/' + self.pid, function (exists) {
				return cb(null, exists)
			});
	};
	this.reload = function () {
		process.kill(self.pid, 'SIGUSR2');
	};
	this.terminate = function () {
		process.kill(self.pid, 'SIGKILL');
	};

	return this;
}

var haproxy = new Haproxy(config);
process.on('SIGTERM', function() {
	haproxy.terminate();
	process.exit(code);
});

/*
* LOG COLLECTOR
*/
var PORT = 514;
var HOST = '127.0.0.1';

var dgram = require('dgram');
var server = dgram.createSocket('udp4');

server.on('listening', function () {
	var address = server.address();
	console.log('UDP Server listening on ' + address.address + ":" + address.port);
});

server.on('message', function (message, remote) {
	client.send(message, 0, message.length, 5145, 'monitor-in.slush.xyz');
});

server.bind(PORT, HOST);

//etcd.set("/services/testservice/10.10.0.1", "10.10.0.1:4555");
//etcd.set("/services/testservice/10.10.0.2", "10.10.0.2:4555");
//etcd.set("/services/testservice/10.10.0.3", "10.10.0.3:4555");
//etcd.set("/services/testservice2/10.10.0.1", "10.10.0.2:4556");

var hostnames = {};

var metadata = {};

var tservices = "";
var hash_meta = "";

hostname_watcher = etcd.watcher("/hostnames", null, {recursive: true});
hostname_watcher.on("change", debounce(fetchHostnames, 200));
hostname_watcher.on('error', console.log);

service_watcher = etcd.watcher("/services", null, {recursive: true});
service_watcher.on("change", debounce(fetchServices, 200));
service_watcher.on('error', console.log);

service_watcher = etcd.watcher("/metadata", null, {recursive: true});
service_watcher.on("change", debounce(fetchMetadata, 200));
service_watcher.on('error', console.log);

fetchHostnames();
fetchMetadata();

fetchServices(1);

function fetchServices (cascade) {
	etcd.get("/services", {recursive:true}, function (err, listing) {
		if (err) { console.error(err); return;}
		var services = [];
		try {
			services = listing.node.nodes.map(handleService);
		} catch (e) {console.log(e, e.stack);}
		services.sort(function (a, b) {
			return (a.name > b.name) ? -1 : 1;
		});
		if (tservices != hash(services) || cascade == 1) {
			console.log("CAPTAIN: rewriting loadbalancer");
			writeHaproxyConfig(services);
		}
		tservices = hash(services);
	});
}

function fetchHostnames () {
	etcd.get("/hostnames", function (err, kvs) {
		var hnames = [];
		try {
			kvs.node.nodes.forEach(function (hostname) {
				var key = hostname.key.replace('/hostnames/', '');
				hnames[key] = hostname.value;
			});
		} catch (e) {}
		hostnames = hnames;
		fetchServices(1);
	});
}

function fetchMetadata () {
	etcd.get("/metadata", function (err, kvs) {
		var metas = [];
		try {
			kvs.node.nodes.forEach(function (metadata) {
				var key = metadata.key.replace('/metadata/', '');
				metas[key] = JSON.parse(metadata.value);
			});
		} catch (e) {}
		metadata = metas;
		if(hash(metadata) !== hash_meta) {
			fetchServices(1);
		}
		hash_meta = hash(metadata);
	});
}

function writeHaproxyConfig (services) {
	fs.readFile('haproxy.template', {encoding:'utf-8'}, function (err, content) {
		var template = Handlebars.compile(content);
		var output = template({services: services});
		//console.log(output, services);
		fs.writeFile('haproxy.cfg', output, function (err) {
			haproxy.running(function (err, running) {
				if (running) {
					console.log("LB is running... Reloading!");
					haproxy.reload();
				} else {
					console.log("LB is NOT running. Starting!");
					haproxy.start();
				}
			});
		});
	});
}
/*
function writeFleetdConfig (template, service) {
	fs.readFile(template, {encoding: 'utf-8'}, function (err, content) {
		var template = Handlebars.compile(content);
		var output = template({service: service});
		console.log(output);
	});
}
function writeServiceConfig (service) {
	writeFleetdConfig('discovery.tpl', service);
	writeFleetdConfig('webservice.tpl', service);
}
*/

function handleService (service_item) {
	var service = {};
	service.name = service_item.key.replace('/services/', '');
	service.host = hostnames[service.name] ? hostnames[service.name] : service.name+".slush.org";
	service.meta = metadata[service.name] ? metadata[service.name] : {};
	service.servers = [];
	try {
		service.servers = service_item.nodes
		.map(handleServers)
		.sort(function (a, b) {
			return (a.name > b.name) ? -1 : 1;
		});
	} catch(e) {
		console.log("CAPTAIN: No backend for " + service.name);
	}
	return service;
}

function handleServers (server_item) {
	var server = {};
	var ks = server_item.key.split('/');
	server.name = ip2hex(ks[ks.length-1]);
	server.address = server_item.value;
	return server;
}

function ip2hex (ip) {
	var parts = ip.split('.');
	ints = parts.map(function(part){return parseInt(part);});
	var k = new Buffer(ints).toString('hex');
	return k;
}
<|endoftext|>