<commit_before>(function () {
  'use strict';
  // Polls controller
  angular
    .module('polls')
    .controller('PollInputController', PollInputController);

  PollInputController.$inject = [
    '$rootScope',
    '$scope',
    '$state',
    '$window',
    'Authentication',
    'pollResolve',
    'notifResolve',
    'OptsService',
    'Socket',
    'CategorysService',
    'toastr',
    'Action',
    'ngDialog'
  ];

  function PollInputController(
    $rootScope,
    $scope,
    $state,
    $window,
    Authentication,
    poll,
    notif,
    Opts,
    Socket,
    Categorys,
    toast,
    Action,
    dialog
  ) {
    var vm = this;

    vm.authentication = Authentication;
    vm.isLogged = vm.authentication.user ? true : false;
    vm.poll = poll;
    vm.poll.close = vm.poll.close ? moment(vm.poll.close) : vm.poll.close;
    vm.isClosed = moment(vm.poll.close).isAfter(new moment());
    vm.categorys = Categorys.query();
    vm.bk_poll = _.clone(poll);
    vm.form = {};
    vm.opts = vm.poll.opts || [];

    vm.optionToggle = -1;

    $scope.sharedDate = new Date(new Date().setMinutes(0, 0));
    function init() {
      if (vm.poll._id) {
        initSocket();
      }
      analysic_nofif();
    }

    init();
    // Init Socket
    function initSocket() {
      if (!Socket.socket) {
        Socket.connect();
      }
      Socket.emit('subscribe_poll', {
        pollId: vm.poll._id,
        userId: vm.authentication.user._id
      });
      Socket.on('poll_delete', res => {
        toast.error('This poll has been deleted.', 'Error!');
        $state.go('polls.list');
      });
      Socket.on('opts_request', res => {
        console.log('Has option request');
        Action.get_poll(vm.poll._id)
          .then(_poll => {
            vm.poll = _poll;
            vm.opts = vm.poll.opts;
            $scope.$apply();
          }, err => {
            toast.error(err.message, 'Error!');
          });
      });
      $scope.$on('$destroy', function () {
        Socket.emit('unsubscribe', {
          pollId: vm.poll._id,
          userId: vm.authentication.user._id
        });
        Socket.removeListener('poll_delete');
        Socket.removeListener('opts_request');
      });
    }

    function analysic_nofif() {
      if (notif) {
        if (notif.status === 0) {
          notif.status = 1;
          notif.$update(() => {
            $rootScope.$emit('changeNotif');
          });
        }
      }
    }

    function isCanUpdate() {
      return true;
      // const update = moment(vm.poll.updated);
      // const now = moment(new Date());
      // var duration = moment.duration(now.diff(update)).asHours();
      // return duration >= 1;
    }
    // Function
    vm.remove = () => {
      $scope.message_title = 'Delete poll!';
      $scope.message_content = 'Are you sure you want to delete?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'Delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.poll.$remove(() => {
          Socket.emit('poll_delete', { pollId: vm.poll._id });
          $state.go('polls.list');
        });
      }
    };

    vm.save = () => {
      if (!vm.validateBody() || !vm.validateCategory() || !vm.validateTitle() || !vm.validateCloseDate()) {
        toast.error('You have not entered enough information.', 'Error!');
        return;
      }
      if (!vm.poll.isPublic) {
        $scope.message_title = 'Save poll!';
        $scope.message_content = 'You want to save a private poll?';
        $scope.dialog_type = 1;
        $scope.buton_label = 'Save';
        dialog.openConfirm({
          scope: $scope,
          templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
        }).then(confirm => {
          handle_save();
        }, reject => {
        });
      } else {
        handle_save();
      }
      function handle_save() {
        vm.poll.opts = vm.opts;
        Action.save_poll(vm.poll)
          .then(res => {
            $state.go('polls.view', { pollId: res._id });
          })
          .catch(err => {
            toast.error(err.message, 'Error!');
          });
      }
    };

    vm.validateCategory = () => {
      return (vm.poll.category) ? true : false;
    };
    vm.validateTitle = () => {
      return (vm.poll.title) ? true : false;
    };
    vm.validateBody = () => {
      return (vm.poll.body) ? true : false;
    };
    vm.validateCloseDate = () => {
      if (!vm.poll.close) {
        return true;
      }
      return moment(vm.poll.close).isAfter(new moment());
    };
    vm.discard = () => {
      if (angular.equals(vm.poll, vm.bk_poll)) {
        handle_discard();
      } else {
        $scope.message_title = 'Discard poll!';
        $scope.message_content = 'Are you sure you want to discard?';
        $scope.dialog_type = 2;
        $scope.buton_label = 'Discard';
        dialog.openConfirm({
          scope: $scope,
          templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
        }).then(confirm => {
          handle_discard();
        }, reject => {
        });
      }
    };
    function handle_discard() {
      if (vm.poll._id) {
        $state.go('polls.view', { pollId: vm.poll._id });
      } else {
        $state.go('polls.list');
      }
    }

    // OPTIONS
    vm.tmp_opt = {};
    vm.show_colorpicker = false;
     var opt_aside = {};//$bsAside({
    //   scope: $scope,
    //   controllerAs: vm,
    //   templateUrl: 'modules/polls/client/views/new-opt.client.view.html',
    //   title: 'Add new option',
    //   placement: 'bottom',
    //   animation: 'am-fade-and-slide-bottom',
    //   show: false
    // });
    vm.opt_class = opt => {
      var classStr = 'custom-panel ';
      classStr += opt.status === 1 ? 'public' : '';
      classStr += opt.status === 2 ? 'waiting' : '';
      return classStr;
    };
    vm.input_opt = opt => {
      vm.tmp_opt = (!opt) ? { poll: vm.poll._id, title: '', body: '', status: 1 } : opt;
      opt_aside.$promise.then(opt_aside.show);
    };
    vm.remove_opt = opt => {
      $scope.message_title = 'Delete option!';
      $scope.message_content = 'Are you sure you want to delete this option?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'Delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.opts = _.without(vm.opts, opt);
        if (opt._id) {
          var _opt = new Opts(opt);
          _opt.$remove(() => {
            Socket.emit('opts_update', { pollId: vm.poll._id });
          });
        }
      }
    };
    vm.approve_opt = opt => {
      $scope.message_title = 'Approve option!';
      $scope.message_content = 'Are you sure you want to approve this option?';
      $scope.dialog_type = 1;
      $scope.buton_label = 'Approve';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_approve();
      }, reject => {
      });
      function handle_approve() {
        opt.status = 1;
        var _opt = new Opts(opt);
        _opt.$update(() => {
          Socket.emit('opts_update', { pollId: vm.poll._id });
        });
      }
    };
    vm.reject_opt = opt => {
      $scope.message_title = 'Reject option!';
      $scope.message_content = 'Are you sure you want to reject this option?';
      $scope.dialog_type = 2;
      $scope.buton_label = 'Reject';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_reject();
      }, reject => {
      });
      function handle_reject() {
        opt.status = 3;
        var _opt = new Opts(opt);
        _opt.$update(() => {
        });
      }
    };
    vm.save_opt = isValid => {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.optForm');
        return false;
      }
      if (!vm.tmp_opt._id && !_.contains(vm.opts, vm.tmp_opt)) {
        vm.opts.push(vm.tmp_opt);
      }
      opt_aside.$promise.then(opt_aside.hide);
    };
  }
})();
<commit_msg>Commited from lenh at Thu, Aug 17, 2017  7:40:56 PM<commit_after>(function () {
  'use strict';
  // Polls controller
  angular
    .module('polls')
    .controller('PollInputController', PollInputController);

  PollInputController.$inject = [
    '$rootScope',
    '$scope',
    '$state',
    '$window',
    'Authentication',
    'pollResolve',
    'notifResolve',
    'OptsService',
    'Socket',
    'CategorysService',
    'toastr',
    'Action',
    'ngDialog'
  ];

  function PollInputController(
    $rootScope,
    $scope,
    $state,
    $window,
    Authentication,
    poll,
    notif,
    Opts,
    Socket,
    Categorys,
    toast,
    Action,
    dialog
  ) {
    var vm = this;

    vm.authentication = Authentication;
    vm.isLogged = vm.authentication.user ? true : false;
    vm.poll = poll;
    vm.poll.close = vm.poll.close ? moment(vm.poll.close) : vm.poll.close;
    vm.isClosed = moment(vm.poll.close).isAfter(new moment());
    vm.categorys = Categorys.query();
    vm.bk_poll = _.clone(poll);
    vm.form = {};
    vm.opts = vm.poll.opts || [];

    vm.optionToggle = -1;

    $scope.sharedDate = new Date(new Date().setMinutes(0, 0));
    function init() {
      if (vm.poll._id) {
        initSocket();
      }
      analysic_nofif();
    }

    init();
    // Init Socket
    function initSocket() {
      if (!Socket.socket) {
        Socket.connect();
      }
      Socket.emit('subscribe_poll', {
        pollId: vm.poll._id,
        userId: vm.authentication.user._id
      });
      Socket.on('poll_delete', res => {
        toast.error('This poll has been deleted.', 'Error!');
        $state.go('polls.list');
      });
      Socket.on('opts_request', res => {
        console.log('Has option request');
        Action.get_poll(vm.poll._id)
          .then(_poll => {
            vm.poll = _poll;
            vm.opts = vm.poll.opts;
            $scope.$apply();
          }, err => {
            toast.error(err.message, 'Error!');
          });
      });
      $scope.$on('$destroy', function () {
        Socket.emit('unsubscribe', {
          pollId: vm.poll._id,
          userId: vm.authentication.user._id
        });
        Socket.removeListener('poll_delete');
        Socket.removeListener('opts_request');
      });
    }

    function analysic_nofif() {
      if (notif) {
        if (notif.status === 0) {
          notif.status = 1;
          notif.$update(() => {
            $rootScope.$emit('changeNotif');
          });
        }
      }
    }

    function isCanUpdate() {
      return true;
      // const update = moment(vm.poll.updated);
      // const now = moment(new Date());
      // var duration = moment.duration(now.diff(update)).asHours();
      // return duration >= 1;
    }
    // Function
    vm.remove = () => {
      $scope.message_title = 'Delete poll!';
      $scope.message_content = 'Are you sure you want to delete?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'Delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.poll.$remove(() => {
          Socket.emit('poll_delete', { pollId: vm.poll._id });
          $state.go('polls.list');
        });
      }
    };

    vm.save = () => {
      if (!vm.validateBody() || !vm.validateCategory() || !vm.validateTitle() || !vm.validateCloseDate()) {
        toast.error('You have not entered enough information.', 'Error!');
        return;
      }
      if (!vm.poll.isPublic) {
        $scope.message_title = 'Save poll!';
        $scope.message_content = 'You want to save a private poll?';
        $scope.dialog_type = 1;
        $scope.buton_label = 'Save';
        dialog.openConfirm({
          scope: $scope,
          templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
        }).then(confirm => {
          handle_save();
        }, reject => {
        });
      } else {
        handle_save();
      }
      function handle_save() {
        vm.poll.opts = vm.opts;
        Action.save_poll(vm.poll)
          .then(res => {
            $state.go('polls.view', { pollId: res._id });
          })
          .catch(err => {
            toast.error(err.message, 'Error!');
          });
      }
    };

    vm.validateCategory = () => {
      return (vm.poll.category) ? true : false;
    };
    vm.validateTitle = () => {
      return (vm.poll.title) ? true : false;
    };
    vm.validateBody = () => {
      return (vm.poll.body) ? true : false;
    };
    vm.validateCloseDate = () => {
      if (!vm.poll.close) {
        return true;
      }
      return moment(vm.poll.close).isAfter(new moment());
    };
    vm.discard = () => {
      if (angular.equals(vm.poll, vm.bk_poll)) {
        handle_discard();
      } else {
        $scope.message_title = 'Discard poll!';
        $scope.message_content = 'Are you sure you want to discard?';
        $scope.dialog_type = 2;
        $scope.buton_label = 'Discard';
        dialog.openConfirm({
          scope: $scope,
          templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
        }).then(confirm => {
          handle_discard();
        }, reject => {
        });
      }
    };
    function handle_discard() {
      if (vm.poll._id) {
        $state.go('polls.view', { pollId: vm.poll._id });
      } else {
        $state.go('polls.list');
      }
    }

    // OPTIONS
    vm.tmp_opt = {};
    vm.show_colorpicker = false;
    var opt_aside = {};//$bsAside({
    //   scope: $scope,
    //   controllerAs: vm,
    //   templateUrl: 'modules/polls/client/views/new-opt.client.view.html',
    //   title: 'Add new option',
    //   placement: 'bottom',
    //   animation: 'am-fade-and-slide-bottom',
    //   show: false
    // });
    vm.opt_class = opt => {
      var classStr = 'custom-panel ';
      classStr += opt.status === 1 ? 'public' : '';
      classStr += opt.status === 2 ? 'waiting' : '';
      return classStr;
    };
    vm.input_opt = opt => {
      vm.tmp_opt = (!opt) ? { poll: vm.poll._id, title: '', body: '', status: 1 } : opt;
      opt_aside.$promise.then(opt_aside.show);
    };
    vm.remove_opt = opt => {
      $scope.message_title = 'Delete option!';
      $scope.message_content = 'Are you sure you want to delete this option?';
      $scope.dialog_type = 3;
      $scope.buton_label = 'Delete';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_delete();
      }, reject => {
      });
      function handle_delete() {
        vm.opts = _.without(vm.opts, opt);
        if (opt._id) {
          var _opt = new Opts(opt);
          _opt.$remove(() => {
            Socket.emit('opts_update', { pollId: vm.poll._id });
          });
        }
      }
    };
    vm.approve_opt = opt => {
      $scope.message_title = 'Approve option!';
      $scope.message_content = 'Are you sure you want to approve this option?';
      $scope.dialog_type = 1;
      $scope.buton_label = 'Approve';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_approve();
      }, reject => {
      });
      function handle_approve() {
        opt.status = 1;
        var _opt = new Opts(opt);
        _opt.$update(() => {
          Socket.emit('opts_update', { pollId: vm.poll._id });
        });
      }
    };
    vm.reject_opt = opt => {
      $scope.message_title = 'Reject option!';
      $scope.message_content = 'Are you sure you want to reject this option?';
      $scope.dialog_type = 2;
      $scope.buton_label = 'Reject';
      dialog.openConfirm({
        scope: $scope,
        templateUrl: 'modules/core/client/views/templates/confirm.dialog.template.html'
      }).then(confirm => {
        handle_reject();
      }, reject => {
      });
      function handle_reject() {
        opt.status = 3;
        var _opt = new Opts(opt);
        _opt.$update(() => {
        });
      }
    };
    vm.save_opt = isValid => {
      if (!isValid) {
        $scope.$broadcast('show-errors-check-validity', 'vm.form.optForm');
        return false;
      }
      if (!vm.tmp_opt._id && !_.contains(vm.opts, vm.tmp_opt)) {
        vm.opts.push(vm.tmp_opt);
      }
      opt_aside.$promise.then(opt_aside.hide);
    };
  }
})();
<|endoftext|>


<commit_before>/**************JQUERY FUNCTIONS******************/
jQuery.fn.center = function () {
    this.css("position","absolute");
    this.css("top", (($(window).height() - this.outerHeight()) / 2) + $(window).scrollTop() + "px");
    this.css("left", (( $(window).width() - this.outerWidth()) / 2) + $(window).scrollLeft() + "px");

    return this;
};

jQuery.fn.inside_center = function () {
    var top = ((this.parent().height() - this.outerHeight()) / 2);
    var left = (( this.parent().width() - this.outerWidth() ) / 2);
    this.parent().css("position","relative");
    this.css("position","absolute");
    this.css("top", top + "px");
    this.css("left", left + "px");
    return this;
};

jQuery.fn.verticalCenter = function(){
    this.each(function() {
        var parent_height = $(this).parent().height();
        var height = $(this).outerHeight();

        $(this).removeClass('verticalCenter');
        if(height < parent_height){
            var top_margin = (parent_height - height)/2;
            $(this).css("marginTop",top_margin+'px');
        }
    });
}

jQuery.fn.equalHeights = function(minHeight, maxHeight, callback) {
    if (typeof minHeight !== 'function') {tallest = (minHeight) ? minHeight : 0;}

    this.each(function() {
        if($(this).innerHeight() > tallest) {
                tallest = $(this).innerHeight();
        }
    });
    if((maxHeight) && typeof maxHeight !== 'function' && tallest < maxHeight) tallest = maxHeight;

    this.each(function() {
        padding = $(this).innerHeight() - $(this).height();
        if(padding > 0){$(this).height(tallest-padding);}
        else{$(this).height(tallest);}

        if($(this).css('overflow') === 'visible'){
            $(this).css("overflow","auto");
        }
    });

    // Callback function
    if (typeof minHeight === 'function') {
        minHeight();
    }else if (typeof maxHeight === 'function') {
        maxHeight();
    }else if (typeof callback === 'function') {
        callback();
    }
    return true;
}

jQuery.fn.squared = function(){
    this.each(function() {
        var w = $(this).width();
        $(this).height(w);
    });
}

jQuery.fn.reset = function () {
  $(this).each (function() { this.reset(); });
}

jQuery.fn.print = function(){
    var frame_name = ("printer-" + (new Date()).getTime());
    var frame_html = $( "" );
    frame_html.css({
        "width":"1px",
        "height":"1px",
        "position":"absolute",
        "left":"-9999px"
    }).appendTo( $("body:first"));
    var frame = window.frames[ frame_name ];
    var frame_doc = frame.document;
    var style = $( "
" ).append($( "style" ).clone());

    frame_doc.open();
    frame_doc.write( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><title>" );
    frame_doc.write( document.title );
    frame_doc.write( "</title><link href=\"/css/style.css\" rel=\"stylesheet\" type=\"text/css\" />" );
    frame_doc.write( "
" );
    frame_doc.write( this.html() );
    frame_doc.write( "
" );
    frame_doc.close();
    frame.focus();
    frame.print();

    setTimeout(function(){
        frame_html.remove();
    },(60 * 1000));
}

jQuery.fn.export_excel = function(name,path_export_file){
    $("body:first").append(' <form method="post" lang="es" target="_blank" action="'+path_export_file+'" id="export_excel" style="display:none;"><input name="name" type="hidden" value="" /><input name="data" type="hidden" value="" /></form>');
    $('#export_excel input[name="name"]').val(name);
    $('#export_excel input[name="data"]').val( $("
").append( $(this).clone()).html());
    $("#export_excel").submit().remove();
}

jQuery.fn.export_csv = function(name,path_export_file) {
  var data = $(this).first();
  var csvData = [];
  var tmpArr = [];
  var tmpStr = '';

  if(_bhl.split('-')[0] === 'es'){var list_delimiter = ';';}
  else{var list_delimiter = ',';}

  data.find("tr").each(function() {
      if($(this).find("th").length) {
          $(this).find("th").each(function() {
            tmpStr = $(this).text().replace(/"/g, '""');
            tmpArr.push('"' + tmpStr + '"');
          });
          csvData.push(tmpArr.join(list_delimiter));
      } else {
          tmpArr = [];
             $(this).find("td").each(function() {
                  if($(this).text().match(/^-{0,1}\d*\.{0,1}\d+$/)) {
                      tmpArr.push(parseFloat($(this).text()));
                  } else {
                      tmpStr = $(this).text().replace(/"/g, '""');
                      tmpArr.push('"' + tmpStr + '"');
                  }
             });
          csvData.push(tmpArr.join(list_delimiter));
      }
  });
  var output = csvData.join('\n');
  var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(output);

  $("body:first").append('<form method="post" lang="es" target="_blank" action="'+path_export_file+'" id="export_excel" style="display:none;"><input name="name" type="hidden" value="" /><input name="data" type="hidden" value="" /></form>');
    $('#export_excel input[name="name"]').val(name);
    $('#export_excel input[name="data"]').val(output);
    $("#export_excel").submit().remove();
}

var valid = true;
var emailreg = /^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$/;
var msgi = "";
var msgi_c = "INCOMPLETOS:\r\n";
var msgc = "";
var msgc_c = "\r\nINCORRECTOS:\r\n";
var msg = "";

jQuery.fn.Vform = function() {
    $(this).find('.Vtext').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).find('.Vnumber').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).find('.Vemail').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).bind("submit",function(){
        valid = true;
        msgi = "";
        msgc = "";
        msg="";
        var label_present = false;
        var tmp = "";
        var index = 0;
        if($(this).find('input[name=itm_label_in_value]').val() == "1"){
            label_present = true;
        }
        $(this).find('.Vtext').each(function(){
            if($(this).val() == $(this).attr('alt') || $(this).attr('alt') == undefined){
                msgi = msgi+$(this).attr('name')+": no ha sido completado. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vselect').each(function(){
            if($(this).val() == "0"){
                msgi = msgi+$(this).attr('name')+": no se ha seleccionado ningún valor. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vemail').each(function(){
            tmp = $(this).val();
            if(label_present == true){
                index = tmp.indexOf(":");
                tmp = tmp.substr(index+1);
                tmp = $.trim(tmp);
            }
            if( !emailreg.test(tmp) ){
                msgc = msgc+$(this).attr('name')+": no es una dirección de email válida. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vnumber').each(function(){
            tmp = $(this).val();
            if(label_present){
                index = tmp.indexOf(":");
                tmp = tmp.substr(index+1);
                tmp = $.trim(tmp);
            }
            if( isNaN(parseInt(tmp)) || tmp.length == 0){
                msgc = msgc+$(this).attr('name')+": no es correcto, solo debe contener números. \r\n";
                valid = false;
            }
        });
        if(msgi.length != 0){
            msg = msg+msgi_c+msgi;
        }
        if(msgc.length != 0){
            msg = msg+msgc_c+msgc;
        }
        if(valid == false){
            alert(msg);
            return false;
        }
    });
}
/*******************FUNCTIONS**********************/
function controlViewport(){
    if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
        var viewportmeta = document.querySelector('meta[name="viewport"]');
        if (viewportmeta) {
            viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
            document.body.addEventListener('gesturestart', function () {
                viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
            }, false);
        }
    }
}

function clock(){
    $('.clock').each(function(){
        var h = parseInt($(this).find('.h').html(),10);
        var m = parseInt($(this).find('.m').html(),10);
        var s = parseInt($(this).find('.s').html(),10);

        s++;
        if(s == 60){
            s=0;
            m++;
        }
        if(m == 60){
            m=0;
            h++;
        }
        if(h == 24){
            h=0;
        }

        if(s < 10){
            $(this).find('.s').html('0'+s);
        }else{
            $(this).find('.s').html(s);
        }
        if(m < 10){
            $(this).find('.m').html('0'+m);
        }else{
            $(this).find('.m').html(m);
        }
        if(h < 10){
            $(this).find('.h').html('0'+h);
        }else{
            $(this).find('.h').html(h);
        }
    });
}

function full_screen(element) {
  if(element.requestFullscreen) {
    element.requestFullscreen();
  } else if(element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if(element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  } else if(element.msRequestFullscreen) {
    element.msRequestFullscreen();
  }
}

function exit_full_screen() {
  if(document.exitFullscreen) {
    document.exitFullscreen();
  } else if(document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if(document.webkitExitFullscreen) {
    document.webkitExitFullscreen();
  }
}

function toggle_full_screen(element){
    if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {
        full_screen(element);
    }else{
        exit_full_screen();
    }
}

function genQuickNav(){
    quick_nav = {};
    quick_nav['Home'] = '/';
    $('.quick-nav').each(function(){
        var a = $(this).find('a');
        a.each(function(){
            quick_nav[$(this).html()] = $(this).attr('href');
        });
    });
    return quick_nav;
}

function createQuickNav(target){
    quick_nav = genQuickNav();
    nav_html = '

Menú de navegación
';
    for(var link in quick_nav){
        nav_html += ''+link+'';
    }
    nav_html += '
';
    target.prepend(nav_html);
}

/**********************TRIGGERS**********************/

$(document).on('click','#btn_quick_nav',function(){
    if($('#quick_nav').is(":visible")){
        $('#quick_nav').hide('fast');
    }else{
        $('#quick_nav').show('fast');
    }
});

$(document).on('click','#quick_nav a',function(){
    $('#quick_nav').hide('fast');
});

$(window).load(function(){
    $('.equalHeights').equalHeights();
});<commit_msg>Added control viewport in document ready.<commit_after>/**************JQUERY FUNCTIONS******************/
jQuery.fn.center = function () {
    this.css("position","absolute");
    this.css("top", (($(window).height() - this.outerHeight()) / 2) + $(window).scrollTop() + "px");
    this.css("left", (( $(window).width() - this.outerWidth()) / 2) + $(window).scrollLeft() + "px");

    return this;
};

jQuery.fn.inside_center = function () {
    var top = ((this.parent().height() - this.outerHeight()) / 2);
    var left = (( this.parent().width() - this.outerWidth() ) / 2);
    this.parent().css("position","relative");
    this.css("position","absolute");
    this.css("top", top + "px");
    this.css("left", left + "px");
    return this;
};

jQuery.fn.verticalCenter = function(){
    this.each(function() {
        var parent_height = $(this).parent().height();
        var height = $(this).outerHeight();

        $(this).removeClass('verticalCenter');
        if(height < parent_height){
            var top_margin = (parent_height - height)/2;
            $(this).css("marginTop",top_margin+'px');
        }
    });
}

jQuery.fn.equalHeights = function(minHeight, maxHeight, callback) {
    if (typeof minHeight !== 'function') {tallest = (minHeight) ? minHeight : 0;}

    this.each(function() {
        if($(this).innerHeight() > tallest) {
                tallest = $(this).innerHeight();
        }
    });
    if((maxHeight) && typeof maxHeight !== 'function' && tallest < maxHeight) tallest = maxHeight;

    this.each(function() {
        padding = $(this).innerHeight() - $(this).height();
        if(padding > 0){$(this).height(tallest-padding);}
        else{$(this).height(tallest);}

        if($(this).css('overflow') === 'visible'){
            $(this).css("overflow","auto");
        }
    });

    // Callback function
    if (typeof minHeight === 'function') {
        minHeight();
    }else if (typeof maxHeight === 'function') {
        maxHeight();
    }else if (typeof callback === 'function') {
        callback();
    }
    return true;
}

jQuery.fn.squared = function(){
    this.each(function() {
        var w = $(this).width();
        $(this).height(w);
    });
}

jQuery.fn.reset = function () {
  $(this).each (function() { this.reset(); });
}

jQuery.fn.print = function(){
    var frame_name = ("printer-" + (new Date()).getTime());
    var frame_html = $( "" );
    frame_html.css({
        "width":"1px",
        "height":"1px",
        "position":"absolute",
        "left":"-9999px"
    }).appendTo( $("body:first"));
    var frame = window.frames[ frame_name ];
    var frame_doc = frame.document;
    var style = $( "
" ).append($( "style" ).clone());

    frame_doc.open();
    frame_doc.write( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><title>" );
    frame_doc.write( document.title );
    frame_doc.write( "</title><link href=\"/css/style.css\" rel=\"stylesheet\" type=\"text/css\" />" );
    frame_doc.write( "
" );
    frame_doc.write( this.html() );
    frame_doc.write( "
" );
    frame_doc.close();
    frame.focus();
    frame.print();

    setTimeout(function(){
        frame_html.remove();
    },(60 * 1000));
}

jQuery.fn.export_excel = function(name,path_export_file){
    $("body:first").append(' <form method="post" lang="es" target="_blank" action="'+path_export_file+'" id="export_excel" style="display:none;"><input name="name" type="hidden" value="" /><input name="data" type="hidden" value="" /></form>');
    $('#export_excel input[name="name"]').val(name);
    $('#export_excel input[name="data"]').val( $("
").append( $(this).clone()).html());
    $("#export_excel").submit().remove();
}

jQuery.fn.export_csv = function(name,path_export_file) {
  var data = $(this).first();
  var csvData = [];
  var tmpArr = [];
  var tmpStr = '';

  if(_bhl.split('-')[0] === 'es'){var list_delimiter = ';';}
  else{var list_delimiter = ',';}

  data.find("tr").each(function() {
      if($(this).find("th").length) {
          $(this).find("th").each(function() {
            tmpStr = $(this).text().replace(/"/g, '""');
            tmpArr.push('"' + tmpStr + '"');
          });
          csvData.push(tmpArr.join(list_delimiter));
      } else {
          tmpArr = [];
             $(this).find("td").each(function() {
                  if($(this).text().match(/^-{0,1}\d*\.{0,1}\d+$/)) {
                      tmpArr.push(parseFloat($(this).text()));
                  } else {
                      tmpStr = $(this).text().replace(/"/g, '""');
                      tmpArr.push('"' + tmpStr + '"');
                  }
             });
          csvData.push(tmpArr.join(list_delimiter));
      }
  });
  var output = csvData.join('\n');
  var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(output);

  $("body:first").append('<form method="post" lang="es" target="_blank" action="'+path_export_file+'" id="export_excel" style="display:none;"><input name="name" type="hidden" value="" /><input name="data" type="hidden" value="" /></form>');
    $('#export_excel input[name="name"]').val(name);
    $('#export_excel input[name="data"]').val(output);
    $("#export_excel").submit().remove();
}

var valid = true;
var emailreg = /^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$/;
var msgi = "";
var msgi_c = "INCOMPLETOS:\r\n";
var msgc = "";
var msgc_c = "\r\nINCORRECTOS:\r\n";
var msg = "";

jQuery.fn.Vform = function() {
    $(this).find('.Vtext').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).find('.Vnumber').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).find('.Vemail').each(function(){
        $(this).attr('alt',$(this).val());
    });

    $(this).bind("submit",function(){
        valid = true;
        msgi = "";
        msgc = "";
        msg="";
        var label_present = false;
        var tmp = "";
        var index = 0;
        if($(this).find('input[name=itm_label_in_value]').val() == "1"){
            label_present = true;
        }
        $(this).find('.Vtext').each(function(){
            if($(this).val() == $(this).attr('alt') || $(this).attr('alt') == undefined){
                msgi = msgi+$(this).attr('name')+": no ha sido completado. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vselect').each(function(){
            if($(this).val() == "0"){
                msgi = msgi+$(this).attr('name')+": no se ha seleccionado ningún valor. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vemail').each(function(){
            tmp = $(this).val();
            if(label_present == true){
                index = tmp.indexOf(":");
                tmp = tmp.substr(index+1);
                tmp = $.trim(tmp);
            }
            if( !emailreg.test(tmp) ){
                msgc = msgc+$(this).attr('name')+": no es una dirección de email válida. \r\n";
                valid = false;
            }
        });
        $(this).find('.Vnumber').each(function(){
            tmp = $(this).val();
            if(label_present){
                index = tmp.indexOf(":");
                tmp = tmp.substr(index+1);
                tmp = $.trim(tmp);
            }
            if( isNaN(parseInt(tmp)) || tmp.length == 0){
                msgc = msgc+$(this).attr('name')+": no es correcto, solo debe contener números. \r\n";
                valid = false;
            }
        });
        if(msgi.length != 0){
            msg = msg+msgi_c+msgi;
        }
        if(msgc.length != 0){
            msg = msg+msgc_c+msgc;
        }
        if(valid == false){
            alert(msg);
            return false;
        }
    });
}
/*******************FUNCTIONS**********************/
function controlViewport(){
    if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)) {
        var viewportmeta = document.querySelector('meta[name="viewport"]');
        if (viewportmeta) {
            viewportmeta.content = 'width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0';
            document.body.addEventListener('gesturestart', function () {
                viewportmeta.content = 'width=device-width, minimum-scale=0.25, maximum-scale=1.6';
            }, false);
        }
    }
}

function clock(){
    $('.clock').each(function(){
        var h = parseInt($(this).find('.h').html(),10);
        var m = parseInt($(this).find('.m').html(),10);
        var s = parseInt($(this).find('.s').html(),10);

        s++;
        if(s == 60){
            s=0;
            m++;
        }
        if(m == 60){
            m=0;
            h++;
        }
        if(h == 24){
            h=0;
        }

        if(s < 10){
            $(this).find('.s').html('0'+s);
        }else{
            $(this).find('.s').html(s);
        }
        if(m < 10){
            $(this).find('.m').html('0'+m);
        }else{
            $(this).find('.m').html(m);
        }
        if(h < 10){
            $(this).find('.h').html('0'+h);
        }else{
            $(this).find('.h').html(h);
        }
    });
}

function full_screen(element) {
  if(element.requestFullscreen) {
    element.requestFullscreen();
  } else if(element.mozRequestFullScreen) {
    element.mozRequestFullScreen();
  } else if(element.webkitRequestFullscreen) {
    element.webkitRequestFullscreen();
  } else if(element.msRequestFullscreen) {
    element.msRequestFullscreen();
  }
}

function exit_full_screen() {
  if(document.exitFullscreen) {
    document.exitFullscreen();
  } else if(document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if(document.webkitExitFullscreen) {
    document.webkitExitFullscreen();
  }
}

function toggle_full_screen(element){
    if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) {
        full_screen(element);
    }else{
        exit_full_screen();
    }
}

function genQuickNav(){
    quick_nav = {};
    quick_nav['Home'] = '/';
    $('.quick-nav').each(function(){
        var a = $(this).find('a');
        a.each(function(){
            quick_nav[$(this).html()] = $(this).attr('href');
        });
    });
    return quick_nav;
}

function createQuickNav(target){
    quick_nav = genQuickNav();
    nav_html = '

Menú de navegación
';
    for(var link in quick_nav){
        nav_html += ''+link+'';
    }
    nav_html += '
';
    target.prepend(nav_html);
}

/**********************TRIGGERS**********************/

$(document).on('click','#btn_quick_nav',function(){
    if($('#quick_nav').is(":visible")){
        $('#quick_nav').hide('fast');
    }else{
        $('#quick_nav').show('fast');
    }
});

$(document).on('click','#quick_nav a',function(){
    $('#quick_nav').hide('fast');
});

$(document).ready(function(){
    controlViewport();
});

$(window).load(function(){
    $('.equalHeights').equalHeights();
});<|endoftext|>


<commit_before>// This is the entry file for the whole project.
// When executed it boots up the game server.

require('newrelic');
require('./server/bootstrap');
<commit_msg>Change to only register New Relic in production<commit_after>// This is the entry file for the whole project.
// When executed it boots up the game server.

if (process.env.NODE_ENV === 'production') {
  require('newrelic');
}

require('./server/bootstrap');
<|endoftext|>


<commit_before>/**
 *
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
 * Copyright (C) 2011 - 2017 SalesAgility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 */

(function ($) {
  $.fn.EmailsComposeViewModal = function (options) {
    "use strict";
    var self = this;
    var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults, options);

    self.handleClick = function (e) {
      "use strict";
      var self = this;
      self.emailComposeView = null;
      var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults);
      var composeBox = $('

').appendTo(opts.contentSelector);
      composeBox.messageBox({"showHeader": false, "showFooter": false, "size": 'lg'});
      composeBox.setBody('

');
      composeBox.show();
      $.ajax({
        type: "GET",
        cache: false,
        url: 'index.php?module=Emails&action=ComposeView&in_popup=1'
      }).done(function (data) {
        if (data.length === 0) {
          console.error("Unable to display ComposeView");
          composeBox.setBody(SUGAR.language.translate('', 'ERR_AJAX_LOAD'));
          return;
        }
        composeBox.setBody(data);
        self.emailComposeView = composeBox.controls.modal.body.find('.compose-view').EmailsComposeView();
        $(self.emailComposeView).on('sentEmail', function (event, composeView) {
          composeBox.hide();
          composeBox.remove();
        });
        $(self.emailComposeView).on('disregardDraft', function (event, composeView) {
          if (typeof messageBox !== "undefined") {
            var mb = messageBox({size: 'lg'});
            mb.setTitle(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_TITLE'));
            mb.setBody(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_BODY'));
            mb.on('ok', function () {
              mb.remove();
              composeBox.hide();
              composeBox.remove();
            });
            mb.on('cancel', function () {
              mb.remove();
            });
            mb.show();
          } else {
            if (confirm(self.translatedErrorMessage)) {
              composeBox.hide();
              composeBox.remove();
            }
          }
        });

        composeBox.on('cancel', function () {
          composeBox.remove();
        });
        composeBox.on('hide.bs.modal', function () {
          composeBox.remove();
        });
      }).fail(function (data) {
        composeBox.controls.modal.content.html(SUGAR.language.translate('', 'LBL_EMAIL_ERROR_GENERAL_TITLE'));
      });
      return $(self);
    };

    self.construct = function () {
      "use strict";
      $(opts.buttonSelector).click(self.handleClick)
    };
    self.destruct = function () {
    };
    self.construct();
    return $(self);
  };

  $.fn.openComposeViewModal = function (source) {
    "use strict";

    var self = this;
    self.emailComposeView = null;
    var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults);
    var composeBox = $('

').appendTo(opts.contentSelector);
    composeBox.messageBox({"showHeader": false, "showFooter": false, "size": 'lg'});
    composeBox.setBody('

');
    composeBox.show();
    var ids = '&ids=';
    if ($(source).attr('data-record-id') !== '') {
      ids = ids + $(source).attr('data-record-id');
    }
    else{
      var inputs = document.MassUpdate.elements;
      for (i = 0; i < inputs.length; i++) {
        if (inputs[i].name === 'mass[]' && inputs[i].checked) {
          ids = ids + inputs[i].value + ',';
        }
      }
    }
    var url = 'index.php?module=Emails&action=ComposeView&in_popup=1&targetModule=' + currentModule + ids;
    $.ajax({
      type: "GET",
      cache: false,
      url: url
    }).done(function (data) {
      if (data.length === 0) {
        console.error("Unable to display ComposeView");
        composeBox.setBody(SUGAR.language.translate('', 'ERR_AJAX_LOAD'));
        return;
      }
      composeBox.setBody(data);
      self.emailComposeView = composeBox.controls.modal.body.find('.compose-view').EmailsComposeView();

      // Populate fields
      var targetCount = 0;
      var targetList = '';
      var populateModuleName = '';
      var populateEmailAddress = '';
      var populateModule = '';
      var populateModuleRecord = '';
      $('.email-compose-view-to-list').each(function () {
        populateModuleName = $(this).attr('data-record-name');
        populateEmailAddress = $(this).attr('data-record-email');
        populateModule = $(this).attr('data-record-module');
        populateModuleRecord = $(this).attr('data-record-id');
        if (targetCount > 0) {
          targetList = targetList + ',';
        }
        if (populateModuleName == '') {
          populateModuleName = populateEmailAddress;
        }
        targetList = targetList + populateModuleName + ' <' + populateEmailAddress + '>';
        targetCount++;
      });
      if (targetCount > 0) {
        $(self.emailComposeView).find('#to_addrs_names').val(targetList);
        if (targetCount < 2) {
          $(self.emailComposeView).find('#parent_type').val(populateModule);
          $(self.emailComposeView).find('#parent_name').val(populateModuleName);
          $(self.emailComposeView).find('#parent_id').val(populateModuleRecord);
        }
      }

      $(self.emailComposeView).on('sentEmail', function (event, composeView) {
        composeBox.hide();
        composeBox.remove();
      });
      $(self.emailComposeView).on('disregardDraft', function (event, composeView) {
        if (typeof messageBox !== "undefined") {
          var mb = messageBox({size: 'lg'});
          mb.setTitle(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_TITLE'));
          mb.setBody(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_BODY'));
          mb.on('ok', function () {
            mb.remove();
            composeBox.hide();
            composeBox.remove();
          });
          mb.on('cancel', function () {
            mb.remove();
          });
          mb.show();
        } else {
          if (confirm(self.translatedErrorMessage)) {
            composeBox.hide();
            composeBox.remove();
          }
        }
      });

      composeBox.on('cancel', function () {
        composeBox.remove();
      });
      composeBox.on('hide.bs.modal', function () {
        composeBox.remove();
      });
    }).fail(function (data) {
      composeBox.controls.modal.content.html(SUGAR.language.translate('', 'LBL_EMAIL_ERROR_GENERAL_TITLE'));
    });
    return $(self);
  };

  $.fn.EmailsComposeViewModal.defaults = {
    'selected': 'INBOX',
    'buttonSelector': '[data-action=emails-show-compose-modal]',
    'contentSelector': '#content'
  };
}(jQuery));<commit_msg>Fix bulk action email.<commit_after>/**
 *
 * SugarCRM Community Edition is a customer relationship management program developed by
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
 *
 * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
 * Copyright (C) 2011 - 2017 SalesAgility Ltd.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by the
 * Free Software Foundation with the addition of the following permission added
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along with
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 *
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
 * these Appropriate Legal Notices must retain the display of the "Powered by
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
 */

(function ($) {
  $.fn.EmailsComposeViewModal = function (options) {
    "use strict";
    var self = this;
    var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults, options);

    self.handleClick = function (e) {
      "use strict";
      var self = this;
      self.emailComposeView = null;
      var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults);
      var composeBox = $('

').appendTo(opts.contentSelector);
      composeBox.messageBox({"showHeader": false, "showFooter": false, "size": 'lg'});
      composeBox.setBody('

');
      composeBox.show();
      $.ajax({
        type: "GET",
        cache: false,
        url: 'index.php?module=Emails&action=ComposeView&in_popup=1'
      }).done(function (data) {
        if (data.length === 0) {
          console.error("Unable to display ComposeView");
          composeBox.setBody(SUGAR.language.translate('', 'ERR_AJAX_LOAD'));
          return;
        }
        composeBox.setBody(data);
        self.emailComposeView = composeBox.controls.modal.body.find('.compose-view').EmailsComposeView();
        $(self.emailComposeView).on('sentEmail', function (event, composeView) {
          composeBox.hide();
          composeBox.remove();
        });
        $(self.emailComposeView).on('disregardDraft', function (event, composeView) {
          if (typeof messageBox !== "undefined") {
            var mb = messageBox({size: 'lg'});
            mb.setTitle(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_TITLE'));
            mb.setBody(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_BODY'));
            mb.on('ok', function () {
              mb.remove();
              composeBox.hide();
              composeBox.remove();
            });
            mb.on('cancel', function () {
              mb.remove();
            });
            mb.show();
          } else {
            if (confirm(self.translatedErrorMessage)) {
              composeBox.hide();
              composeBox.remove();
            }
          }
        });

        composeBox.on('cancel', function () {
          composeBox.remove();
        });
        composeBox.on('hide.bs.modal', function () {
          composeBox.remove();
        });
      }).fail(function (data) {
        composeBox.controls.modal.content.html(SUGAR.language.translate('', 'LBL_EMAIL_ERROR_GENERAL_TITLE'));
      });
      return $(self);
    };

    self.construct = function () {
      "use strict";
      $(opts.buttonSelector).click(self.handleClick)
    };
    self.destruct = function () {
    };
    self.construct();
    return $(self);
  };

  $.fn.openComposeViewModal = function (source) {
    "use strict";

    var self = this;
    self.emailComposeView = null;
    var opts = $.extend({}, $.fn.EmailsComposeViewModal.defaults);
    var composeBox = $('

').appendTo(opts.contentSelector);
    composeBox.messageBox({"showHeader": false, "showFooter": false, "size": 'lg'});
    composeBox.setBody('

');
    composeBox.show();
    var ids = '&ids=';
    if ($(source).attr('data-record-id') !== '') {
      ids = ids + $(source).attr('data-record-id');
    }
    else{
      var inputs = document.MassUpdate.elements;
      for (var i = 0; i < inputs.length; i++) {
        if (inputs[i].name === 'mass[]' && inputs[i].checked) {
          ids = ids + inputs[i].value + ',';
        }
      }
    }
    var url = 'index.php?module=Emails&action=ComposeView&in_popup=1&targetModule=' + currentModule + ids;
    $.ajax({
      type: "GET",
      cache: false,
      url: url
    }).done(function (data) {
      if (data.length === 0) {
        console.error("Unable to display ComposeView");
        composeBox.setBody(SUGAR.language.translate('', 'ERR_AJAX_LOAD'));
        return;
      }
      composeBox.setBody(data);
      self.emailComposeView = composeBox.controls.modal.body.find('.compose-view').EmailsComposeView();

      // Populate fields
      var targetCount = 0;
      var targetList = '';
      var populateModuleName = '';
      var populateEmailAddress = '';
      var populateModule = '';
      var populateModuleRecord = '';
      $('.email-compose-view-to-list').each(function () {
        populateModuleName = $(this).attr('data-record-name');
        populateEmailAddress = $(this).attr('data-record-email');
        populateModule = $(this).attr('data-record-module');
        populateModuleRecord = $(this).attr('data-record-id');
        if (targetCount > 0) {
          targetList = targetList + ',';
        }
        if (populateModuleName == '') {
          populateModuleName = populateEmailAddress;
        }
        targetList = targetList + populateModuleName + ' <' + populateEmailAddress + '>';
        targetCount++;
      });
      if (targetCount > 0) {
        $(self.emailComposeView).find('#to_addrs_names').val(targetList);
        if (targetCount < 2) {
          $(self.emailComposeView).find('#parent_type').val(populateModule);
          $(self.emailComposeView).find('#parent_name').val(populateModuleName);
          $(self.emailComposeView).find('#parent_id').val(populateModuleRecord);
        }
      }

      $(self.emailComposeView).on('sentEmail', function (event, composeView) {
        composeBox.hide();
        composeBox.remove();
      });
      $(self.emailComposeView).on('disregardDraft', function (event, composeView) {
        if (typeof messageBox !== "undefined") {
          var mb = messageBox({size: 'lg'});
          mb.setTitle(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_TITLE'));
          mb.setBody(SUGAR.language.translate('', 'LBL_CONFIRM_DISREGARD_DRAFT_BODY'));
          mb.on('ok', function () {
            mb.remove();
            composeBox.hide();
            composeBox.remove();
          });
          mb.on('cancel', function () {
            mb.remove();
          });
          mb.show();
        } else {
          if (confirm(self.translatedErrorMessage)) {
            composeBox.hide();
            composeBox.remove();
          }
        }
      });

      composeBox.on('cancel', function () {
        composeBox.remove();
      });
      composeBox.on('hide.bs.modal', function () {
        composeBox.remove();
      });
    }).fail(function (data) {
      composeBox.controls.modal.content.html(SUGAR.language.translate('', 'LBL_EMAIL_ERROR_GENERAL_TITLE'));
    });
    return $(self);
  };

  $.fn.EmailsComposeViewModal.defaults = {
    'selected': 'INBOX',
    'buttonSelector': '[data-action=emails-show-compose-modal]',
    'contentSelector': '#content'
  };
}(jQuery));<|endoftext|>


<commit_before>/// type Array-like
//
// Defines the common structural type of objects that "look like arrays".
//
// :: { "length": UInt32 }

/// function Array
//
// Constructs `Array` objects, which are sparse list/vector-like data
// structures.
//
// :: new (Number) -> [a]
// :: new (a...) -> [a]
//
// Arrays provide a way to manage ordered collections of values, like a
// List or Vector would in another language. Arrays in JavaScript are
// slightly different from most other languages in that they are sparse
// (that is, not necessarily all of the keys in an Array will be
// associated with a value).
//
// The Array function and its prototype provide several functions to
// manipulate both actual Arrays and `Array-like` objects.
function Array(length) { }
function Array(n1, n2 /*, ... */) { }

//// -- Predicates -----------------------------------------------------

///// function isArray
//
// Verifies if the given object is an actual Array (not an Array-like).
//
// :: a -> Bool
Array.isArray = function(a) { }

//// -- Instances ------------------------------------------------------

///// {} prototype
//
// Properties shared by instances of Array.
//
// :: Object.prototype <| Array.prototype

///// -- Reflection ----------------------------------------------------

////// data length
//
// The size of the Array.
//
// :: @Array-like => UInt32
//
// **Warning**
// > Note that due to Arrays being sparse in JavaScript, this **does not**
// > reflect the number of actual elements in the array. Its value can be
// > tought as the largest index defined in the array + 1.

///// -- Modifying array elements --------------------------------------

////// function push
//
// Adds one or more elements to the end of an Array, and returns the new
// length of the Array.
//
// :: @Array-like* => a... -> Number
Array.prototype.push = function(a1, a2 /*, ... */) { }

////// function pop
//
// Removes the element at the last index from an Array, and returns it.
//
// If the Array is empty, no changes are made and the operation returns
// `undefined`.
//
// ::@Array-like* => () -> Maybe a
Array.prototype.pop = function() { }

////// function unshift
//
// Adds one or more elements to the beginning of an Array, and returns
// the new length of the Array.
//
// :: @Array-like* => a... -> Number
Array.prototype.unshift = function(a1, a2 /*, ... */) { }

////// function shift
//
// Removes the first element from the array, and returns it.
//
// If the Array is empty, no changes are made and the operation returns
// `undefined`.
//
// ::@Array-like* => () -> Maybe a
Array.prototype.shift = function() { }

////// function splice
//
// Adds and/or removes elements from the array at specific indexes.
//
// The function will remove `size` elements starting from `index`, then
// add all the given elements at `index` in the array.
//
// :: @Array-like* => index:Number, size:Number, a... -> this
Array.prototype.splice = function(index, size, a1, a2 /*, ... */) { }

///// -- Sorting and ordering ------------------------------------------

////// function reverse
//
// Inverts the order of elements in the Array.
//
// :: @Array-like* => () -> [a]
Array.prototype.reverse = function() { }

////// function sort
//
// Sorts the Array according to the specified criteria.
//
// If no criteria is specified, the items will be sorted
// lexicographically. If a criteria is specified, the function should
// return a Number defining what to do with the pair of elements the
// sorting algorithm is considering:
//
//   - Negative: consider element `a` lower than element `b`. Nothing is
//     done.
//   - Zero: consider both elements equal, result is unspecified.
//   - Positive: consider element `a` greater than element `b`. They're
//     swapped.
//
// The specification does not gurantee that the sorting algorithm will
// be stable, nor what implementations should do if the criteria
// function returns 0.
//
// :: @Array-like* => (a, a -> Number) -> [a]
Array.prototype.sort = function(criteria) { }

///// -- Constructing arrays -------------------------------------------

////// function concat
//
// Concatenates one or more arrays together.
//
// :: @Array-like => [a]... -> [a]
Array.prototype.concat = function(as1, as2 /*, ... */) { }

////// function slice
//
// Returns a portion of the array between the given indexes.
//
// If the `starting` index is omitted, it's assumed to be the beginning
// of the Array; if the `ending` index is omitted, it's assumed to be
// the end of the Array.
//
// If any of the indexes have a negative value, the index is considered
// to be relative to the end of the Array.
//
// :: @Array-like* => starting:Number?, ending:Number? -> [a]
Array.prototype.slice = function(starting, ending) { }

///// -- Acessing elements ---------------------------------------------

////// function indexOf
//
// Returns the index of the first element to match the given one, by a
// strict equality check.
//
// If no element matches the given one, it returns -1.
//
// :: @Array-like => a -> Number
Array.prototype.indexOf = function(a) { }

////// function lastIndexOf
//
// Returns the index of the last element to match the given one, by a
// strict equality check.
//
// If no element matches the given one, it returns -1.
//
// :: @Array-like => a -> Number
Array.prototype.lastIndexOf = function(a) { }

///// -- Transforming --------------------------------------------------

////// function join
//
// Join the elements in a String, separated by the given separator.
//
// If no separator is given, `", "` is assumed.
//
// :: @Array-like => String? -> String
Array.prototype.join = function(separator) { }

////// function toString
//
// Returns a string representation of the Array.
//
// (Same as `array.join()`)
//
// :: @Array-like => () -> String
Array.prototype.toString = function() { }

///// -- Iteration -----------------------------------------------------

////// function forEach
//
// Applies a function to every item in the list, in ascending order.
//
// :: @Array-like => (a, Number, [a] -> ()), Object? -> ()
Array.prototype.forEach = function(callback, thisObject) { }

///// -- Folds ---------------------------------------------------------

////// function reduce
//
// Computes a value out of a list of values, from left to right.
//
// If no initial value is specified, the function uses the first element
// of the Array and reduces on the rest of the list.
//
// :: @Array-like => (b, a, Number, [a] -> b), b? -> b
Array.prototype.reduce = function(callback, initialValue) { }

////// function reduceRight
//
// Computes a value out of a list of values, from right to left.
//
// If no initial value is specified, the function uses the last element
// of the Array and reduces on the rest of the list.
//
// :: @Array-like => (b, a, Number, [a] -> b), b? -> b
Array.prototype.reduceRight = function(callback, initialValue) { }<commit_msg>Finishes draft documentation for Arrays.<commit_after>/// type Array-like
//
// Defines the common structural type of objects that "look like arrays".
//
// :: { "length": UInt32 }

/// function Array
//
// Constructs `Array` objects, which are sparse list/vector-like data
// structures.
//
// :: new (Number) -> [a]
// :: new (a...) -> [a]
//
// Arrays provide a way to manage ordered collections of values, like a
// List or Vector would in another language. Arrays in JavaScript are
// slightly different from most other languages in that they are sparse
// (that is, not necessarily all of the keys in an Array will be
// associated with a value).
//
// The Array function and its prototype provide several functions to
// manipulate both actual Arrays and `Array-like` objects.
function Array(length) { }
function Array(n1, n2 /*, ... */) { }

//// -- Predicates -----------------------------------------------------

///// function isArray
//
// Verifies if the given object is an actual Array (not an Array-like).
//
// :: a -> Bool
Array.isArray = function(a) { }

//// -- Instances ------------------------------------------------------

///// {} prototype
//
// Properties shared by instances of Array.
//
// :: Object.prototype <| Array.prototype

///// -- Reflection ----------------------------------------------------

////// data length
//
// The size of the Array.
//
// :: @Array-like => UInt32
//
// **Warning**
// > Note that due to Arrays being sparse in JavaScript, this **does not**
// > reflect the number of actual elements in the array. Its value can be
// > tought as the largest index defined in the array + 1.

///// -- Modifying array elements --------------------------------------

////// function push
//
// Adds one or more elements to the end of an Array, and returns the new
// length of the Array.
//
// :: @Array-like* => a... -> Number
Array.prototype.push = function(a1, a2 /*, ... */) { }

////// function pop
//
// Removes the element at the last index from an Array, and returns it.
//
// If the Array is empty, no changes are made and the operation returns
// `undefined`.
//
// ::@Array-like* => () -> Maybe a
Array.prototype.pop = function() { }

////// function unshift
//
// Adds one or more elements to the beginning of an Array, and returns
// the new length of the Array.
//
// :: @Array-like* => a... -> Number
Array.prototype.unshift = function(a1, a2 /*, ... */) { }

////// function shift
//
// Removes the first element from the array, and returns it.
//
// If the Array is empty, no changes are made and the operation returns
// `undefined`.
//
// ::@Array-like* => () -> Maybe a
Array.prototype.shift = function() { }

////// function splice
//
// Adds and/or removes elements from the array at specific indexes.
//
// The function will remove `size` elements starting from `index`, then
// add all the given elements at `index` in the array.
//
// :: @Array-like* => index:Number, size:Number, a... -> this
Array.prototype.splice = function(index, size, a1, a2 /*, ... */) { }

///// -- Sorting and ordering ------------------------------------------

////// function reverse
//
// Inverts the order of elements in the Array.
//
// :: @Array-like* => () -> [a]
Array.prototype.reverse = function() { }

////// function sort
//
// Sorts the Array according to the specified criteria.
//
// If no criteria is specified, the items will be sorted
// lexicographically. If a criteria is specified, the function should
// return a Number defining what to do with the pair of elements the
// sorting algorithm is considering:
//
//   - Negative: consider element `a` lower than element `b`. Nothing is
//     done.
//   - Zero: consider both elements equal, result is unspecified.
//   - Positive: consider element `a` greater than element `b`. They're
//     swapped.
//
// The specification does not gurantee that the sorting algorithm will
// be stable, nor what implementations should do if the criteria
// function returns 0.
//
// :: @Array-like* => (a, a -> Number) -> [a]
Array.prototype.sort = function(criteria) { }

///// -- Constructing arrays -------------------------------------------

////// function concat
//
// Concatenates one or more arrays together.
//
// :: @Array-like => [a]... -> [a]
Array.prototype.concat = function(as1, as2 /*, ... */) { }

////// function slice
//
// Returns a portion of the array between the given indexes.
//
// If the `starting` index is omitted, it's assumed to be the beginning
// of the Array; if the `ending` index is omitted, it's assumed to be
// the end of the Array.
//
// If any of the indexes have a negative value, the index is considered
// to be relative to the end of the Array.
//
// :: @Array-like* => starting:Number?, ending:Number? -> [a]
Array.prototype.slice = function(starting, ending) { }

///// -- Acessing elements ---------------------------------------------

////// function indexOf
//
// Returns the index of the first element to match the given one, by a
// strict equality check.
//
// If no element matches the given one, it returns -1.
//
// :: @Array-like => a -> Number
Array.prototype.indexOf = function(a) { }

////// function lastIndexOf
//
// Returns the index of the last element to match the given one, by a
// strict equality check.
//
// If no element matches the given one, it returns -1.
//
// :: @Array-like => a -> Number
Array.prototype.lastIndexOf = function(a) { }

///// -- Transforming --------------------------------------------------

////// function join
//
// Join the elements in a String, separated by the given separator.
//
// If no separator is given, `", "` is assumed.
//
// :: @Array-like => String? -> String
Array.prototype.join = function(separator) { }

////// function toString
//
// Returns a string representation of the Array.
//
// (Same as `array.join()`)
//
// :: @Array-like => () -> String
Array.prototype.toString = function() { }

///// -- Iteration -----------------------------------------------------

////// function forEach
//
// Applies a function to every item in the list, in ascending order.
//
// :: @Array-like => (a, Number, [a] -> ()), Object? -> ()
Array.prototype.forEach = function(callback, thisObject) { }

///// -- Folds ---------------------------------------------------------

////// function reduce
//
// Computes a value out of a list of values, from left to right.
//
// If no initial value is specified, the function uses the first element
// of the Array and reduces on the rest of the list.
//
// :: @Array-like => (b, a, Number, [a] -> b), b? -> b
Array.prototype.reduce = function(callback, initialValue) { }

////// function reduceRight
//
// Computes a value out of a list of values, from right to left.
//
// If no initial value is specified, the function uses the last element
// of the Array and reduces on the rest of the list.
//
// :: @Array-like => (b, a, Number, [a] -> b), b? -> b
Array.prototype.reduceRight = function(callback, initialValue) { }

////// function map
//
// Transforms each item in the Array by a function.
//
// Basically, this means that `[1, 2, 3].map(inc)` is the same as
// `[inc(1), inc(2), inc(3)]`.
//
// :: (a, Number, [a] -> b), Object? -> [b]
Array.prototype.map = function(callback, thisObject) { }

////// function filter
//
// Returns a new array that contains only the items that pass the given
// predicate.
//
// :: (a, Number, [a] -> Bool), Object? -> [a]
Array.prototype.filter = function(callback, thisObject) { }

////// function every
//
// Returns true if all objects pass the given predicate, false if any
// doesn't.
//
// :: (a, Number, [a] -> Bool), Object? -> Bool
Array.prototype.every = function(callback, thisObject) { }

////// function some
//
// Returns true if any object pass the given predicate, false if none
// passes.
//
// :: (a, Number, [a] -> Bool), Object? -> Bool
Array.prototype.some = function(callback, thisObject) { }
<|endoftext|>


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

  truncAll();

  $('.i2').on('keyup', _.debounce(function (e) {
    trunc($(this));
  }, 200));

  $('.i1, .i2').on('mouseover', function(e){
    $(this).addClass('hover');
  });

  $('.i1, .i2').on('mouseout', function(e){
    $(this).removeClass('hover');
  });

});

function trunc(e){
  if (e.val().length > 40){
    e.val( e.val().substring(0, 39)+"..." );
  } /*else {
      e.val( e.val().replace(/...$/, '') );
    }*/
}

function truncAll(){
  $('.i2').each(function(k,v){
    trunc($(this));
  });
}
<commit_msg>Fix caret jumping to end of line on truncation.<commit_after>$( document ).ready(function() {

  truncAll();

  $('.i2').on('keyup', function(e) {
    if ([16, 37, 38, 39, 40].indexOf(e.which) >= 0) {
      return;
    }

    trunc($(this));
  });

  $('.i1, .i2').on('mouseover', function(e){
    $(this).addClass('hover');
  });

  $('.i1, .i2').on('mouseout', function(e){
    $(this).removeClass('hover');
  });

});

function trunc(e){
  if (e.val().length > 40){
    var selectionStart = e.get(0).selectionStart;

    e.val( e.val().substring(0, 39)+"..." );

    e.get(0).setSelectionRange(selectionStart, selectionStart);
  }
}

function truncAll(){
  $('.i2').each(function(k,v){
    trunc($(this));
  });
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 by Greg Reimer <gregreimer@gmail.com>
 * MIT License. See mit-license.txt for more info.
 */

import Request from './request'
import Response from './response'
import streams from './streams'
import awate from 'await'
import mkdirp from 'mkdirp'
import _ from 'lodash'
import { Server as DocRootServer } from 'node-static'
import http from 'http'
import https from 'https'
import url from 'url'
import fs from 'fs'
import util from 'util'
import pathTools from 'path'
import zlib from 'zlib'
import { EventEmitter } from 'events'
import co from 'co'
import adapt from 'ugly-adapter'
import wait from './wait'
import task from './task'
import UrlPath from './url-path'

let staticServer = (() => {

  let getStatic = (() => {
    let statics = {}
    return docroot => {
      let stat = statics[docroot]
      if (!stat) {
        stat = statics[docroot] = new DocRootServer(docroot)
      }
      return stat
    }
  })()

  // Start up the server and serve out of various docroots.
  let server = http.createServer((req, resp) => {
    let docroot = req.headers['x-hoxy-static-docroot']
    let pDocroot = new UrlPath(docroot)
    let stat = getStatic(pDocroot.toSystemPath())
    stat.serve(req, resp)
  }).listen(0, 'localhost')

  return server
})()

class ProvisionableRequest {

  constructor(opts) {
    this._respProm = task()
    let h = (/https/i).test(opts.protocol) ? https : http
    if (opts.proxy) {
      let proxyInfo = url.parse(opts.proxy)
        , proxyPort = proxyInfo.port
        , proxyHostname = proxyInfo.hostname
        , proxyPath = 'http://' + opts.hostname + (opts.port ? ':' + opts.port : '') + opts.path
      opts.hostname = proxyHostname
      opts.port = proxyPort
      opts.path = proxyPath
    }
    this._writable = h.request(opts, this._respProm.resolve)
  }

  send(readable) {
    return new Promise((resolve, reject) => {
      this._writable.on('error', reject)
      if (!readable || typeof readable === 'string') {
        this._writable.end(readable || '', resolve)
      } else {
        readable.on('error', reject)
        readable.on('end', resolve)
        readable.pipe(this._writable)
      }
    })
  }

  receive() {
    return this._respProm
  }
}

/*
 * This check() function made me scratch my head when I came back to
 * it months later. It simply does too many things. It still isn't perfect,
 * but hopefully now this beast is slightly easier to follow. It returns
 * a promise on a boolean indicating whether or not the passed file was
 * created. IF the strategy is NOT 'mirror' it resolves false since 'mirror'
 * is the only strategy that creates files. Otherwise if the file exists
 * it resolves false. Otherwise it has a side effect of creating the file
 * by requesting out to the remote server and writing the result to the
 * file, then resolves true.
 */
function check(strategy, file, req, upstreamProxy) {
  let parsed = url.parse(file)
  file = parsed.pathname // stripped of query string.

  return co(function*() {
    if (strategy !== 'mirror') {
      return false
    }
    try {
      yield adapt(fs.stat, file)
      return false
    } catch(ex) {
      // file does not exist, so continue
    }
    // TODO: test coverage for mkdirp
    yield adapt(mkdirp, pathTools.dirname(file))
    let provReq = new ProvisionableRequest({
      protocol: req.protocol,
      proxy: upstreamProxy,
      method: 'GET',
      hostname: req.hostname,
      port: req.port,
      path: req.url,
    })
    provReq.send()
    let mirrResp = yield provReq.receive()
    if (mirrResp.statusCode !== 200) {
      throw new Error(`mirroring failed: ${req.fullUrl()} => ${mirrResp.statusCode}`)
    }
    let writeToFile = fs.createWriteStream(file)
    if (mirrResp.headers['content-encoding'] === 'gzip') {
      // TODO: test coverage
      let gunzip = zlib.createGunzip()
      mirrResp = mirrResp.pipe(gunzip)
    }
    yield new Promise((resolve, reject) => {
      mirrResp.pipe(writeToFile)
      mirrResp.on('end', resolve)
      writeToFile.on('error', reject)
    })
  })
}

// ---------------------------

export default class Cycle extends EventEmitter {

  constructor(proxy) {
    super()
    this._proxy = proxy
    this._request = new Request()
    this._response = new Response()
    this._request.on('log', log => this.emit('log', log))
    this._response.on('log', log => this.emit('log', log))
  }

  data(name, val) {
    if (!this._userData) {
      this._userData = {}
    }
    if (arguments.length === 2) {
      this._userData[name] = val
    }
    return this._userData[name]
  }

  serve(opts) {

    return co.call(this, function*() {

      // First, get all our ducks in a row WRT to
      // options, setting variables, etc.
      let req = this._request
      let resp = this._response
      if (typeof opts === 'string') {
        opts = { path: opts }
      }
      opts = _.extend({
        docroot: pathTools.sep,
        path: url.parse(req.url).pathname,
        strategy: 'replace',
      }, opts)
      let { docroot, path, strategy } = opts
      let headers = _.extend({
        'x-hoxy-static-docroot': docroot,
      }, req.headers)
      delete headers['if-none-match']
      delete headers['if-modified-since']

      // Now call the static file service.
      let pDocroot = new UrlPath(docroot)
        , pPath = new UrlPath(path)
        , pFullPath = pPath.rootTo(pDocroot)
        , fullSysPath = pFullPath.toSystemPath()
      let created = yield check(strategy, fullSysPath, req, this._proxy._upstreamProxy)
      if (created) {
        this.emit('log', {
          level: 'info',
          message: 'copied ' + req.fullUrl() + ' to ' + fullSysPath,
        })
      }
      let staticResp = yield new Promise((resolve, reject) => {
        let addr = staticServer.address()
        http.get({
          hostname: addr.address,
          port: addr.port,
          headers: headers,
          path: pPath.toUrlPath(),
        }, resolve)
        .on('error', reject)
      })
      let code = staticResp.statusCode
        , useResponse
        , isError
      if (/^2\d\d$/.test(code)) {
        useResponse = true
      } else if (/^4\d\d$/.test(code)) {
        if (strategy === 'replace') {
          useResponse = true
        } else if (strategy === 'mirror') {
          isError = true
        }
      } else {
        isError = true // nope
      }
      if (isError) {
        let message = util.format(
          'Failed to serve static file: %s => %s. Static server returned %d. Strategy: %s',
          req.fullUrl(),
          fullSysPath,
          staticResp.statusCode,
          strategy
        )
        throw new Error(message)
      } else if (useResponse) {
        resp._setHttpSource(staticResp)
      }
    })
  }

  _setPhase(phase) {
    this._phase = this._request.phase = this._response.phase = phase
  }

  /*
   * This returns a promise on a partially fulfilled request
   * (an instance of class ProvisionableRequest). At the time
   * the promise is fulfilled, the request is in a state where
   * it's been fully piped out, but nothing received. It's up
   * to the caller of this function to call receive() on it, thus
   * getting a promise on the serverResponse object. That enables
   * hoxy to implement the 'request-sent' phase.
   */
  _sendToServer() {
    let req = this._request._finalize()
      , resp = this._response
      , upstreamProxy = this._proxy._upstreamProxy
      , source = req._source
      , pSlow = this._proxy._slow || {}
      , rSlow = req.slow() || {}
      , latency = rSlow.latency || 0
    if (resp._populated) {
      return Promise.resolve(undefined)
    }
    return co.call(this, function*() { // return the outer promise
      let provisionableReq = new ProvisionableRequest({
        protocol: req.protocol,
        proxy: upstreamProxy,
        hostname: req.hostname,
        port: req.port || req._getDefaultPort(),
        method: req.method,
        path: req.url,
        headers: req.headers,
      })
      if (latency > 0) {
        yield wait(latency)
      }
      if (rSlow.rate > 0) {
        let brake = streams.brake(rSlow.rate)
        source = source.pipe(brake)
      }
      if (pSlow.rate) {
        let groupedBrake = pSlow.rate.throttle()
        source = source.pipe(groupedBrake)
      }
      if (pSlow.up) {
        let groupedBrake = pSlow.up.throttle()
        source = source.pipe(groupedBrake)
      }
      req._tees().forEach(writable => source.pipe(writable))
      yield provisionableReq.send(source) // wait for it all to pipe out
      return provisionableReq
    })
  }

  _sendToClient(outResp) {
    let resp = this._response._finalize()
      , source = resp._source
      , rSlow = resp.slow() || {}
      , pSlow = this._proxy._slow || {}
      , rLatency = rSlow.latency || 0
      , pLatency = pSlow.latency || 0
      , latency = Math.max(pLatency, rLatency)
    return co.call(this, function*() {
      if (latency > 0) {
        yield wait(latency)
      }
      outResp.writeHead(resp.statusCode, resp.headers)
      if (rSlow.rate > 0) {
        let brake = streams.brake(rSlow.rate)
        source = source.pipe(brake)
      }
      if (pSlow.rate) {
        let groupedBrake = pSlow.rate.throttle()
        source = source.pipe(groupedBrake)
      }
      if (pSlow.down) {
        let groupedBrake = pSlow.down.throttle()
        source = source.pipe(groupedBrake)
      }
      let tees = resp._tees()
      tees.forEach(writable => source.pipe(writable))
      yield new Promise((resolve, reject) => {
        source.on('error', reject)
        source.on('end', resolve)
        source.pipe(outResp)
      })
    })
  }

  _start() {
    // for now, an immediately-kept promise
    return awate('started').keep('started')
  }
}
<commit_msg>save error for later rejection<commit_after>/*
 * Copyright (c) 2015 by Greg Reimer <gregreimer@gmail.com>
 * MIT License. See mit-license.txt for more info.
 */

import Request from './request'
import Response from './response'
import streams from './streams'
import awate from 'await'
import mkdirp from 'mkdirp'
import _ from 'lodash'
import { Server as DocRootServer } from 'node-static'
import http from 'http'
import https from 'https'
import url from 'url'
import fs from 'fs'
import util from 'util'
import pathTools from 'path'
import zlib from 'zlib'
import { EventEmitter } from 'events'
import co from 'co'
import adapt from 'ugly-adapter'
import wait from './wait'
import task from './task'
import UrlPath from './url-path'

let staticServer = (() => {

  let getStatic = (() => {
    let statics = {}
    return docroot => {
      let stat = statics[docroot]
      if (!stat) {
        stat = statics[docroot] = new DocRootServer(docroot)
      }
      return stat
    }
  })()

  // Start up the server and serve out of various docroots.
  let server = http.createServer((req, resp) => {
    let docroot = req.headers['x-hoxy-static-docroot']
    let pDocroot = new UrlPath(docroot)
    let stat = getStatic(pDocroot.toSystemPath())
    stat.serve(req, resp)
  }).listen(0, 'localhost')

  return server
})()

class ProvisionableRequest {

  constructor(opts) {
    this._respProm = task()
    let h = (/https/i).test(opts.protocol) ? https : http
    if (opts.proxy) {
      let proxyInfo = url.parse(opts.proxy)
        , proxyPort = proxyInfo.port
        , proxyHostname = proxyInfo.hostname
        , proxyPath = 'http://' + opts.hostname + (opts.port ? ':' + opts.port : '') + opts.path
      opts.hostname = proxyHostname
      opts.port = proxyPort
      opts.path = proxyPath
    }
    this._writable = h.request(opts, this._respProm.resolve)
    this._writable.on('error', err => this._writableError = err);
  }

  send(readable) {
    return new Promise((resolve, reject) => {
      if (this._writableError) {
        reject(this._writableError);
        return;
      }
      this._writable.on('error', reject)
      if (!readable || typeof readable === 'string') {
        this._writable.end(readable || '', resolve)
      } else {
        readable.on('error', reject)
        readable.on('end', resolve)
        readable.pipe(this._writable)
      }
    })
  }

  receive() {
    return this._respProm
  }
}

/*
 * This check() function made me scratch my head when I came back to
 * it months later. It simply does too many things. It still isn't perfect,
 * but hopefully now this beast is slightly easier to follow. It returns
 * a promise on a boolean indicating whether or not the passed file was
 * created. IF the strategy is NOT 'mirror' it resolves false since 'mirror'
 * is the only strategy that creates files. Otherwise if the file exists
 * it resolves false. Otherwise it has a side effect of creating the file
 * by requesting out to the remote server and writing the result to the
 * file, then resolves true.
 */
function check(strategy, file, req, upstreamProxy) {
  let parsed = url.parse(file)
  file = parsed.pathname // stripped of query string.

  return co(function*() {
    if (strategy !== 'mirror') {
      return false
    }
    try {
      yield adapt(fs.stat, file)
      return false
    } catch(ex) {
      // file does not exist, so continue
    }
    // TODO: test coverage for mkdirp
    yield adapt(mkdirp, pathTools.dirname(file))
    let provReq = new ProvisionableRequest({
      protocol: req.protocol,
      proxy: upstreamProxy,
      method: 'GET',
      hostname: req.hostname,
      port: req.port,
      path: req.url,
    })
    provReq.send()
    let mirrResp = yield provReq.receive()
    if (mirrResp.statusCode !== 200) {
      throw new Error(`mirroring failed: ${req.fullUrl()} => ${mirrResp.statusCode}`)
    }
    let writeToFile = fs.createWriteStream(file)
    if (mirrResp.headers['content-encoding'] === 'gzip') {
      // TODO: test coverage
      let gunzip = zlib.createGunzip()
      mirrResp = mirrResp.pipe(gunzip)
    }
    yield new Promise((resolve, reject) => {
      mirrResp.pipe(writeToFile)
      mirrResp.on('end', resolve)
      writeToFile.on('error', reject)
    })
  })
}

// ---------------------------

export default class Cycle extends EventEmitter {

  constructor(proxy) {
    super()
    this._proxy = proxy
    this._request = new Request()
    this._response = new Response()
    this._request.on('log', log => this.emit('log', log))
    this._response.on('log', log => this.emit('log', log))
  }

  data(name, val) {
    if (!this._userData) {
      this._userData = {}
    }
    if (arguments.length === 2) {
      this._userData[name] = val
    }
    return this._userData[name]
  }

  serve(opts) {

    return co.call(this, function*() {

      // First, get all our ducks in a row WRT to
      // options, setting variables, etc.
      let req = this._request
      let resp = this._response
      if (typeof opts === 'string') {
        opts = { path: opts }
      }
      opts = _.extend({
        docroot: pathTools.sep,
        path: url.parse(req.url).pathname,
        strategy: 'replace',
      }, opts)
      let { docroot, path, strategy } = opts
      let headers = _.extend({
        'x-hoxy-static-docroot': docroot,
      }, req.headers)
      delete headers['if-none-match']
      delete headers['if-modified-since']

      // Now call the static file service.
      let pDocroot = new UrlPath(docroot)
        , pPath = new UrlPath(path)
        , pFullPath = pPath.rootTo(pDocroot)
        , fullSysPath = pFullPath.toSystemPath()
      let created = yield check(strategy, fullSysPath, req, this._proxy._upstreamProxy)
      if (created) {
        this.emit('log', {
          level: 'info',
          message: 'copied ' + req.fullUrl() + ' to ' + fullSysPath,
        })
      }
      let staticResp = yield new Promise((resolve, reject) => {
        let addr = staticServer.address()
        http.get({
          hostname: addr.address,
          port: addr.port,
          headers: headers,
          path: pPath.toUrlPath(),
        }, resolve)
        .on('error', reject)
      })
      let code = staticResp.statusCode
        , useResponse
        , isError
      if (/^2\d\d$/.test(code)) {
        useResponse = true
      } else if (/^4\d\d$/.test(code)) {
        if (strategy === 'replace') {
          useResponse = true
        } else if (strategy === 'mirror') {
          isError = true
        }
      } else {
        isError = true // nope
      }
      if (isError) {
        let message = util.format(
          'Failed to serve static file: %s => %s. Static server returned %d. Strategy: %s',
          req.fullUrl(),
          fullSysPath,
          staticResp.statusCode,
          strategy
        )
        throw new Error(message)
      } else if (useResponse) {
        resp._setHttpSource(staticResp)
      }
    })
  }

  _setPhase(phase) {
    this._phase = this._request.phase = this._response.phase = phase
  }

  /*
   * This returns a promise on a partially fulfilled request
   * (an instance of class ProvisionableRequest). At the time
   * the promise is fulfilled, the request is in a state where
   * it's been fully piped out, but nothing received. It's up
   * to the caller of this function to call receive() on it, thus
   * getting a promise on the serverResponse object. That enables
   * hoxy to implement the 'request-sent' phase.
   */
  _sendToServer() {
    let req = this._request._finalize()
      , resp = this._response
      , upstreamProxy = this._proxy._upstreamProxy
      , source = req._source
      , pSlow = this._proxy._slow || {}
      , rSlow = req.slow() || {}
      , latency = rSlow.latency || 0
    if (resp._populated) {
      return Promise.resolve(undefined)
    }
    return co.call(this, function*() { // return the outer promise
      let provisionableReq = new ProvisionableRequest({
        protocol: req.protocol,
        proxy: upstreamProxy,
        hostname: req.hostname,
        port: req.port || req._getDefaultPort(),
        method: req.method,
        path: req.url,
        headers: req.headers,
      })
      if (latency > 0) {
        yield wait(latency)
      }
      if (rSlow.rate > 0) {
        let brake = streams.brake(rSlow.rate)
        source = source.pipe(brake)
      }
      if (pSlow.rate) {
        let groupedBrake = pSlow.rate.throttle()
        source = source.pipe(groupedBrake)
      }
      if (pSlow.up) {
        let groupedBrake = pSlow.up.throttle()
        source = source.pipe(groupedBrake)
      }
      req._tees().forEach(writable => source.pipe(writable))
      yield provisionableReq.send(source) // wait for it all to pipe out
      return provisionableReq
    })
  }

  _sendToClient(outResp) {
    let resp = this._response._finalize()
      , source = resp._source
      , rSlow = resp.slow() || {}
      , pSlow = this._proxy._slow || {}
      , rLatency = rSlow.latency || 0
      , pLatency = pSlow.latency || 0
      , latency = Math.max(pLatency, rLatency)
    return co.call(this, function*() {
      if (latency > 0) {
        yield wait(latency)
      }
      outResp.writeHead(resp.statusCode, resp.headers)
      if (rSlow.rate > 0) {
        let brake = streams.brake(rSlow.rate)
        source = source.pipe(brake)
      }
      if (pSlow.rate) {
        let groupedBrake = pSlow.rate.throttle()
        source = source.pipe(groupedBrake)
      }
      if (pSlow.down) {
        let groupedBrake = pSlow.down.throttle()
        source = source.pipe(groupedBrake)
      }
      let tees = resp._tees()
      tees.forEach(writable => source.pipe(writable))
      yield new Promise((resolve, reject) => {
        source.on('error', reject)
        source.on('end', resolve)
        source.pipe(outResp)
      })
    })
  }

  _start() {
    // for now, an immediately-kept promise
    return awate('started').keep('started')
  }
}
<|endoftext|>