<commit_before>var templates = {
  processItem: function(data) {
    var template = Handlebars.compile($('#process-item-template').html());
    return template(data);
  },
  processContent: function(data) {
    var template = Handlebars.compile($('#process-content-template').html());
    return template(data);
  },
  processContentItem: function(data) {
    var template = Handlebars.compile($('#process-content-item-template').html());
    return template(data);
  }
};

var REST = {
  remove: function($e, callback) {
    var slug = $e.attr('data-slug');
    var model = $e.attr('data-model');

    if (typeof slug === 'undefined' || slug.length <= 0) {
      console.log('Varning: Inget slug!');
      return;
    }

    $.ajax({
      type: 'DELETE',
      url: '/api/' + model + 's/' + slug
    }).done(function(msg) {
      setLastSaved(new Date());
      callback(null, msg);
    }).fail(function(err, status) {
      if (err) alert(err);
      callback(err, status);
    });
    return true;
  },

  update: function($e, updated, callback) {
    var slug = $e.attr('data-slug');
    var model = $e.attr('data-model');

    if (typeof slug === 'undefined' || slug.length <= 0) {
      console.log('Varning: Inget slug!');
      return;
    }

    var data = {};
    if (typeof updated === 'object') {
      data = updated;
    } else {
      data[$e.attr('data-field')] = updated;
    }

    $.ajax({
      type: 'GET',
      url: '/api/update/' + model + '/' + slug,
      data: data
    }).done(function(msg) {
      setLastSaved(new Date());

      if (model === 'Operation' && typeof window.history.pushState !== 'undefined') {
        window.history.pushState(msg.title, document.title, '/edit/' + msg.slug);
      }

      $('[data-model="' + model + '"][data-slug="' + slug + '"]').attr('data-slug', msg.slug);
      callback(null, msg);
    }).fail(function(err, status) {
      if (err) alert(err);
      callback(err, status);
    });
    return true;
  },

  create: function(model, data, callback) {
    $.ajax({
      type: 'POST',
      url: '/api/' + model + 's',
      data: data
    }).done(function(msg) {
      setLastSaved(new Date());
      callback(null, msg);
    }).fail(function(err, status) {
      if (err) alert(err);
      callback(err, status);
    });
  }
};

var initializeSpecialitetSelect = function() {
  $(".specialitet-select").select2({
    ajax: {
      type: 'GET',
      url: '/api/search/Specialitet/',
      dataType: 'json',
      delay: 250,
      data: function(params) {
        return {
          // search term
          text: params.term,
          all: 1
        };
      },
      processResults: function(data) {
        return {
          results: $.map(data, function(item) {
            return {
              text: item.name, id: item._id
            }
          })
        };
      },
      cache: true
    },
    escapeMarkup: function(markup) {
      return markup;
    },

    // let our custom formatter work
    minimumInputLength: 0
  });
};

var initializeTagInput = function() {
  $('.tags').tagsInput({
    width: 'auto',
    defaultText: 'Lägg till synonym',
    removeWithBackspace: false,
    height: '40px',
    'onChange': function($input, tag) {
      if (typeof tag === 'undefined') return;
      var value = $($input).val();
      if (value.length) {
        REST.update($($input), value, function(err, msg) {
        });
      }
    }
  });
};

var initializeWysiwygElement = function($e) {
  if ($e.find('.mce-tinymce').length) return;

  $e.find('textarea:not(.wysiwyg)').addClass('wysiwyg').tinymce({
    plugins: 'advlist autoresize charmap contextmenu image ' +
    'media print anchor link paste tabfocus textcolor ' +
    'autolink insertdatetime lists searchreplace table ' +
    'wordcount imageupload',
    toolbar1: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify |  | bullist numlist outdent indent | link image imageupload',
    imageupload_url: '/api/upload',
    setup: function(editor) {
      var onChange = function(e) {
        REST.update($(e.target.targetElm), $(e.target.targetElm).val(), function(err, msg) {
        });
      };
      editor.on('change', onChange);
    }
  });
};

var setLastSaved = function(date) {
  $('.lastSave .time').text(new Date(date).format());
};

var changeWidth = function(i, e) {
  var $this = typeof e === 'undefined' ? $(this) : $(e);
  $this.width(($this.val().length + 1) * 8);
};

var initializeSortable = function() {
  $(".process-content").sortable({
    cancel: 'input,.mce-tinymce',
    stop: function(e, ui) {
      var $this = $(this);
      console.log($this);
      $this.find('.process-content-item').each(function(i, e) {
        var $e = $(e);
        REST.update($e, {order: $('.process-content .process-content-item').index(e)}, function(err, msg) {
        });
      });
    }
  });

  $('.nav-pills').sortable({
    cancel: '.newProcess',
    stop: function(e, ui) {
      var $this = $(this);
      $this.find('.process-item').each(function(i, e) {
        var $e = $(e);
        var $input = $e.find('input');
        REST.update($input, {order: $('.nav-pills .process-item').index(e)}, function(err, msg) {
        });
      });
    }
  });
};

var refreshSortables = function() {
  $(".process-content").sortable('refresh');
  $('.nav-pills').sortable('refresh');
};

var attachUpdateListeners = function() {

  initializeSortable();

  var sendToPreview = function(e) {
    e.preventDefault();
    REST.update($(this), $(this).attr('data-val'), function(err, msg) {
      if (err) {
        alert('Kunde inte skicka handboken till granskning.');
        return;
      }
      window.location = '/info/' + msg.slug;
    });
  };
  $('body')
    .on('change', 'input[data-update="true"][data-slug],select[data-update="true"]', function() {
      if ($(this).val().length <= 0) {
        $(this).val($(this).attr('data-saved'));
        return;
      }
      $(this).attr('data-saved', $(this).val());
      REST.update($(this), $(this).val(), function(err, data) {
      });
    })
    .on('click', '.toReview[data-update="true"]', sendToPreview);
};

var attachCreateListeners = function() {

  var newProcessContentItem = function() {
    var $this = $(this);
    var $parent = $this.parents().eq(2);
    $(templates.processContentItem({noData: 1})).appendTo($parent);

    REST.create('Processinnehall', {
      title: $(this).val(),
      process: $parent.attr('data-id'),
      order: $('.process-content .process-content-item').length
    }, function(err, msg) {
      if (err) {
        console.log(err);
        return;
      }
      initializeWysiwygElement($this.parent().parent());
      $this.parent().parent().attr('data-slug', msg.slug).find('[data-model="Processinnehall"]').attr('data-slug', msg.slug);
      refreshSortables();
    });
  };

  var newProcess = function(e) {
    e.preventDefault();
    var value = $('.newProcess input').val();
    REST.create('Processteg', {
      title: value,
      operation: $('form.operationForm').attr('data-id'),
      order: $('.process-item').length
    }, function(err, msg) {
      if (err) {
        console.log(err);
        return;
      }
      var $content = $(templates.processContent(msg)).insertAfter($('.process-content').last());
      $(templates.processContentItem({noData: 1})).appendTo($content);
      $(templates.processItem(msg)).insertBefore($('.newProcess input').parent()).find('input').click();
      $('.newProcess input').val('');
      $('input.process').each(changeWidth);
      initializeSortable();
      refreshSortables();
    });
  };

  var onEnter = function(event) {
    if ((event.keyCode || event.which) === 13) newProcess(event);
  };

  $('body')
    .on('keyup', '.process-content-item:last-child .rubrik', newProcessContentItem)
    .on('keyup', '.newProcess input', onEnter)
    .on('click', '.newProcess .glyphicon-plus', newProcess);
};

var attachRemoveListeners = function() {
  var removeProcessContentItem = function() {
    if (!confirm('Vill du verkligen ta bort denna rubrik?')) return;
    var $item = $(this).parent();
    var slug = $item.attr('data-slug');
    REST.remove($item, function(err, msg) {
      console.log(err);
      console.log(msg);
      $('[data-slug="' + slug + '"]').remove();
    });
  };

  var removeProcess = function() {
    if (!confirm('Vill du verkligen ta bort denna process?')) return;
    var $item = $(this);
    var slug = $item.attr('data-slug');
    REST.remove($item, function(err, msg) {
      console.log(err);
      console.log(msg);
      $('[data-slug="' + slug + '"]').remove();
    });
  };

  $('body')
    .on('click', '.process-content-item .glyphicon-remove', removeProcessContentItem)
    .on('click', '.process-item .glyphicon-remove', removeProcess);
};

var attachViewListeners = function() {
  var navClick = function() {
    $('.nav-pills li.process-item .active').removeClass('active');
    var $this = $(this).addClass('active');
    $(".process-content").hide();
    if ($(this).attr('data-id') == 'checklist') {
      $('#contentchecklist').show();
    } else {
      $('.process-content[data-slug="' + $(this).attr('data-slug') + '"]').show();
    }
  };

  $('input.process').each(changeWidth);

  $('body')
    .on('keyup', 'input.process', changeWidth)
    .on('click', '.navbar-btn', navClick);
};

$(document).ready(function() {
  initializeSpecialitetSelect();
  initializeTagInput();

  $('.process-content:not(.hidden) .process-content-item:not(:last-child)').each(function(i, e) {
    initializeWysiwygElement($(e));
  });

  attachUpdateListeners();
  attachCreateListeners();
  attachRemoveListeners();

  attachViewListeners();
});
<commit_msg>Now initializes wysiwyg-editor on click for better performance<commit_after>var templates = {
  processItem: function (data) {
    var template = Handlebars.compile($('#process-item-template').html());
    return template(data);
  },
  processContent: function (data) {
    var template = Handlebars.compile($('#process-content-template').html());
    return template(data);
  },
  processContentItem: function (data) {
    var template = Handlebars.compile($('#process-content-item-template').html());
    return template(data);
  }
};

var REST = {
  remove: function ($e, callback) {
    var slug = $e.attr('data-slug');
    var model = $e.attr('data-model');

    if (typeof slug === 'undefined' || slug.length <= 0) {
      console.log('Varning: Inget slug!');
      return;
    }

    $.ajax({
      type: 'DELETE',
      url: '/api/' + model + 's/' + slug
    }).done(function (msg) {
      setLastSaved(new Date());
      callback(null, msg);
    }).fail(function (err, status) {
      if (err) alert(err);
      callback(err, status);
    });
    return true;
  },

  update: function ($e, updated, callback) {
    var slug = $e.attr('data-slug');
    var model = $e.attr('data-model');

    if (typeof slug === 'undefined' || slug.length <= 0) {
      console.log('Varning: Inget slug!');
      return;
    }

    var data = {};
    if (typeof updated === 'object') {
      data = updated;
    } else {
      data[$e.attr('data-field')] = updated;
    }

    $.ajax({
      type: 'GET',
      url: '/api/update/' + model + '/' + slug,
      data: data
    }).done(function (msg) {
      setLastSaved(new Date());

      if (model === 'Operation' && typeof window.history.pushState !== 'undefined') {
        window.history.pushState(msg.title, document.title, '/edit/' + msg.slug);
      }

      $('[data-model="' + model + '"][data-slug="' + slug + '"]').attr('data-slug', msg.slug);
      callback(null, msg);
    }).fail(function (err, status) {
      if (err) alert(err);
      callback(err, status);
    });
    return true;
  },

  create: function (model, data, callback) {
    $.ajax({
      type: 'POST',
      url: '/api/' + model + 's',
      data: data
    }).done(function (msg) {
      setLastSaved(new Date());
      callback(null, msg);
    }).fail(function (err, status) {
      if (err) alert(err);
      callback(err, status);
    });
  }
};

var initializeSpecialitetSelect = function () {
  $(".specialitet-select").select2({
    ajax: {
      type: 'GET',
      url: '/api/search/Specialitet/',
      dataType: 'json',
      delay: 250,
      data: function (params) {
        return {
          // search term
          text: params.term,
          all: 1
        };
      },
      processResults: function (data) {
        return {
          results: $.map(data, function (item) {
            return {
              text: item.name, id: item._id
            }
          })
        };
      },
      cache: true
    },
    escapeMarkup: function (markup) {
      return markup;
    },

    // let our custom formatter work
    minimumInputLength: 0
  });
};

var initializeTagInput = function () {
  $('.tags').tagsInput({
    width: 'auto',
    defaultText: 'Lägg till synonym',
    removeWithBackspace: false,
    height: '40px',
    'onChange': function ($input, tag) {
      if (typeof tag === 'undefined') return;
      var value = $($input).val();
      if (value.length) {
        REST.update($($input), value, function (err, msg) {
        });
      }
    }
  });
};

var initializeWysiwygElement = function ($e) {
  if ($e.find('.mce-tinymce').length) return;

  $e.find('textarea:not(.wysiwyg)').addClass('wysiwyg').tinymce({
    plugins: 'advlist autoresize charmap contextmenu image ' +
    'media print anchor link paste tabfocus textcolor ' +
    'autolink insertdatetime lists searchreplace table ' +
    'wordcount imageupload',
    toolbar1: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify |  | bullist numlist outdent indent | link image imageupload',
    imageupload_url: '/api/upload',
    setup: function (editor) {
      var onChange = function (e) {
        REST.update($(e.target.targetElm), $(e.target.targetElm).val(), function (err, msg) {
        });
      };
      editor.on('change', onChange);
    }
  });
};

var setLastSaved = function (date) {
  $('.lastSave .time').text(new Date(date).format());
};

var changeWidth = function (i, e) {
  var $this = typeof e === 'undefined' ? $(this) : $(e);
  $this.width(($this.val().length + 1) * 8);
};

var initializeSortable = function () {
  $(".process-content").sortable({
    cancel: 'input,.mce-tinymce',
    stop: function (e, ui) {
      var $this = $(this);
      console.log($this);
      $this.find('.process-content-item').each(function (i, e) {
        var $e = $(e);
        REST.update($e, {order: $('.process-content .process-content-item').index(e)}, function (err, msg) {
        });
      });
    }
  });

  $('.nav-pills').sortable({
    cancel: '.newProcess',
    stop: function (e, ui) {
      var $this = $(this);
      $this.find('.process-item').each(function (i, e) {
        var $e = $(e);
        var $input = $e.find('input');
        REST.update($input, {order: $('.nav-pills .process-item').index(e)}, function (err, msg) {
        });
      });
    }
  });
};

var refreshSortables = function () {
  $(".process-content").sortable('refresh');
  $('.nav-pills').sortable('refresh');
};

var attachUpdateListeners = function () {

  initializeSortable();

  var sendToPreview = function (e) {
    e.preventDefault();
    REST.update($(this), $(this).attr('data-val'), function (err, msg) {
      if (err) {
        alert('Kunde inte skicka handboken till granskning.');
        return;
      }
      window.location = '/info/' + msg.slug;
    });
  };
  $('body')
    .on('change', 'input[data-update="true"][data-slug],select[data-update="true"]', function () {
      if ($(this).val().length <= 0) {
        $(this).val($(this).attr('data-saved'));
        return;
      }
      $(this).attr('data-saved', $(this).val());
      REST.update($(this), $(this).val(), function (err, data) {
      });
    })
    .on('click', '.toReview[data-update="true"]', sendToPreview);
};

var attachCreateListeners = function () {

  var newProcessContentItem = function () {
    var $this = $(this);
    var $parent = $this.parents().eq(2);
    $(templates.processContentItem({noData: 1})).appendTo($parent);

    REST.create('Processinnehall', {
      title: $(this).val(),
      process: $parent.attr('data-id'),
      order: $('.process-content .process-content-item').length
    }, function (err, msg) {
      if (err) {
        console.log(err);
        return;
      }
      $this.parent().parent().find('textarea').removeAttr('disabled');
      $this.parent().parent().attr('data-slug', msg.slug).find('[data-model="Processinnehall"]').attr('data-slug', msg.slug);
      refreshSortables();
    });
  };

  var newProcess = function (e) {
    e.preventDefault();
    var value = $('.newProcess input').val();
    REST.create('Processteg', {
      title: value,
      operation: $('form.operationForm').attr('data-id'),
      order: $('.process-item').length
    }, function (err, msg) {
      if (err) {
        console.log(err);
        return;
      }
      var $content = $(templates.processContent(msg)).insertAfter($('.process-content').last());
      $(templates.processContentItem({noData: 1})).appendTo($content);
      $(templates.processItem(msg)).insertBefore($('.newProcess input').parent()).find('input').click();
      $('.newProcess input').val('');
      $('input.process').each(changeWidth);
      initializeSortable();
      refreshSortables();
    });
  };

  var onEnter = function (event) {
    if ((event.keyCode || event.which) === 13) newProcess(event);
  };

  $('body')
    .on('keyup', '.process-content-item:last-child .rubrik', newProcessContentItem)
    .on('keyup', '.newProcess input', onEnter)
    .on('click', '.newProcess .glyphicon-plus', newProcess);
};

var attachRemoveListeners = function () {
  var removeProcessContentItem = function () {
    if (!confirm('Vill du verkligen ta bort denna rubrik?')) return;
    var $item = $(this).parent();
    var slug = $item.attr('data-slug');
    REST.remove($item, function (err, msg) {
      console.log(err);
      console.log(msg);
      $('[data-slug="' + slug + '"]').remove();
    });
  };

  var removeProcess = function () {
    if (!confirm('Vill du verkligen ta bort denna process?')) return;
    var $item = $(this);
    var slug = $item.attr('data-slug');
    REST.remove($item, function (err, msg) {
      console.log(err);
      console.log(msg);
      $('[data-slug="' + slug + '"]').remove();
    });
  };

  $('body')
    .on('click', '.process-content-item .glyphicon-remove', removeProcessContentItem)
    .on('click', '.process-item .glyphicon-remove', removeProcess);
};

var attachViewListeners = function () {
  var navClick = function () {
    $('.nav-pills li.process-item .active').removeClass('active');
    var $this = $(this).addClass('active');
    $(".process-content").hide();
    if ($(this).attr('data-id') == 'checklist') {
      $('#contentchecklist').show();
    } else {
      $('.process-content[data-slug="' + $(this).attr('data-slug') + '"]').show();
    }
  };

  $('input.process').each(changeWidth);

  $('body')
    .on('keyup', 'input.process', changeWidth)
    .on('click', '.navbar-btn', navClick)
    .on('click', '.process-content:not(.hidden) .process-content-item:not(:last-child) textarea', function () {
      initializeWysiwygElement($(this).parentsUntil('.process-content-item'));
    });
};

$(document).ready(function () {
  initializeSpecialitetSelect();
  initializeTagInput();

  $('.process-content:not(.hidden) .process-content-item:not(:last-child) textarea').each(function (i, e) {
    $(this).removeAttr('disabled');
  });

  attachUpdateListeners();
  attachCreateListeners();
  attachRemoveListeners();

  attachViewListeners();
});
<|endoftext|>


<commit_before>var
  fs          = require('fs'),
  Component   = require('../models').Component,
  config      = require('../config'),
  utils       = require('../lib/utils'),
  handleError = require('../lib/handleError');

var
  RETURN_FIELDS = [
    'name', 'repo', 'main', 'description', 'scripts',
    'dependencies', 'keywords', 'updated', 'type',
    'twitter', 'license', 'version', 'stars'
  ].join(' ');

// GET /components
// GET /components/?=query
// TODO Probably can cache the entire result
exports.index = function (req, res, next) {
  var query = req.query.q;
  Component[query ? 'search' : 'find'](query || {}, RETURN_FIELDS, function (err, components) {
    if (!err) {
      res.json(components);
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name
exports.show = function (req, res, next) {
  var name = req.params.owner + '/' + req.params.name;
  Component.findOne({ repo: name }, RETURN_FIELDS, function (err, pkg) {
    if (!err && pkg) {
      res.json(pkg);
    } else if (!err && !pkg) {
      res.status(400).json({});
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name/script.js
exports.getScript = function (req, res, next) {
  var
    repo = req.params.owner + '/' + req.params.name,
    path = utils.getInstallDir(repo);
  Component.findOne({ repo: repo }, 'main', function (err, pkg) {
    if (!err && pkg) {
      fs.readFile(path + pkg.main, function (err, buffer) {
        if (!err) {
          res.send(buffer);
        } else {
          handleError(err, next);
        }
      });
    } else if (!err) {
      res.status(400).send();
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name/build.js
exports.getBuild = function (req, res, next) {
  var repo = req.params.owner + '/' + req.params.name;
  fs.readFile(utils.getBuildScriptPath(repo), function (err, buffer) {
    if (!err) {
      res.send(buffer);
    } else {
      // Install of handling error, assume that it was just an invalid repo
      res.status(400).send();
      // handleError(err, next);
    }
  });
};
<commit_msg>Send author attribute in all requests<commit_after>var
  fs          = require('fs'),
  Component   = require('../models').Component,
  config      = require('../config'),
  utils       = require('../lib/utils'),
  handleError = require('../lib/handleError');

var
  RETURN_FIELDS = [
    'name', 'repo', 'main', 'description', 'scripts',
    'dependencies', 'keywords', 'updated', 'type',
    'twitter', 'license', 'version', 'stars', 'author'
  ].join(' ');

// GET /components
// GET /components/?=query
// TODO Probably can cache the entire result
exports.index = function (req, res, next) {
  var query = req.query.q;
  Component[query ? 'search' : 'find'](query || {}, RETURN_FIELDS, function (err, components) {
    if (!err) {
      res.json(components);
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name
exports.show = function (req, res, next) {
  var name = req.params.owner + '/' + req.params.name;
  Component.findOne({ repo: name }, RETURN_FIELDS, function (err, pkg) {
    if (!err && pkg) {
      res.json(pkg);
    } else if (!err && !pkg) {
      res.status(400).json({});
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name/script.js
exports.getScript = function (req, res, next) {
  var
    repo = req.params.owner + '/' + req.params.name,
    path = utils.getInstallDir(repo);
  Component.findOne({ repo: repo }, 'main', function (err, pkg) {
    if (!err && pkg) {
      fs.readFile(path + pkg.main, function (err, buffer) {
        if (!err) {
          res.send(buffer);
        } else {
          handleError(err, next);
        }
      });
    } else if (!err) {
      res.status(400).send();
    } else {
      handleError(err, next);
    }
  });
};

// GET /components/:owner/:name/build.js
exports.getBuild = function (req, res, next) {
  var repo = req.params.owner + '/' + req.params.name;
  fs.readFile(utils.getBuildScriptPath(repo), function (err, buffer) {
    if (!err) {
      res.send(buffer);
    } else {
      // Install of handling error, assume that it was just an invalid repo
      res.status(400).send();
      // handleError(err, next);
    }
  });
};
<|endoftext|>


<commit_before>angular.module('ngFabForm')
    .factory('ngFabFormValidationsDirective', ['ngFabForm', '$compile', '$templateRequest', '$rootScope', '$timeout', function (ngFabForm, $compile, $templateRequest, $rootScope, $timeout)
    {
        'use strict';

        function insertValidationMsgs(params)
        {
            var el = params.el;
            var cfg = params.cfg;
            var formCtrl = params.formCtrl;
            var ngModelCtrl = params.ngModelCtrl;
            var attrs = params.attrs;

            // remove error tpl if any
            if (params.currentValidationVars.tpl && (Object.keys(params.currentValidationVars.tpl).length !== 0)) {
                angular.element(params.currentValidationVars.tpl).remove();
            }

            // load validation directive template
            $templateRequest(cfg.validationsTemplate)
                .then(function processTemplate(html)
                {
                    // create new scope for validation messages
                    var privateScope = $rootScope.$new(true);
                    // assign to currentValidationVars to be destroyed later
                    params.currentValidationVars.privateScope = privateScope;

                    // add custom (attr) validations
                    html = ngFabForm.addCustomValidations(html, attrs);

                    privateScope.attrs = attrs;
                    privateScope.form = formCtrl;
                    privateScope.field = ngModelCtrl;

                    // compile and insert messages
                    var compiledAlert = $compile(html.children())(privateScope);
                    params.currentValidationVars.tpl = compiledAlert[0];

                    ngFabForm.insertErrorTpl(compiledAlert[0], el, attrs);
                });
        }

        function setAsteriskForLabel(el, attrs, cfg)
        {
            var labels = document.querySelectorAll('label[for="' + attrs.name + '"]');
            // if nothing is found check previous element
            if (!labels || labels.length < 1) {
                var elBefore = el[0].previousElementSibling;
                if (elBefore && elBefore.tagName === 'LABEL') {
                    labels = [elBefore];
                }
            }

            // set asterisk for match(es)
            if (labels && labels.length > 0 && attrs.type !== 'radio' && attrs.type !== 'checkbox') {
                for (var i = 0; i < labels.length; i++) {
                    var label = labels[i];
                    // don't append twice if multiple inputs with the same name
                    if (label.textContent.slice(-cfg.asteriskStr.length) !== cfg.asteriskStr) {
                        label.textContent = label.textContent + cfg.asteriskStr;
                    }
                }
            }
        }

        // return factory
        return {
            restrict: 'E',
            require: '?ngModel',
            compile: function (el, attrs)
            {
                // don't execute for buttons
                if (attrs.type) {
                    if (attrs.type.toLowerCase() === 'submit' || attrs.type.toLowerCase() === 'button') {
                        return;
                    }
                }

                // only execute if ng-model is present and
                // no name attr is set already
                // NOTE: needs to be set in $compile-function for the validation too work
                if (ngFabForm.config.setNamesByNgModel && attrs.ngModel && !attrs.name) {
                    // set name attribute if none is set
                    el.attr('name', attrs.ngModel);
                    attrs.name = attrs.ngModel;
                }

                // Linking function
                return function (scope, el, attrs, ngModelCtrl)
                {

                    var cfg;
                    // assigned via element.controller
                    var formCtrl;
                    var configChangeWatcher;
                    var formCtrlWatcher;
                    var currentValidationVars = {
                        // is in object to be passed by reference
                        tpl: undefined,
                        privateScope: undefined
                    };

                    function ngFabFormCycle(oldCfg)
                    {
                        // apply validation messages
                        // only if required controllers and validators are set
                        if (ngModelCtrl && cfg.validationsTemplate && ((Object.keys(ngModelCtrl.$validators).length !== 0) || (Object.keys(ngModelCtrl.$asyncValidators).length !== 0)) && (!oldCfg || cfg.validationsTemplate !== oldCfg.validationsTemplate)) {
                            insertValidationMsgs({
                                el: el,
                                cfg: cfg,
                                formCtrl: formCtrl,
                                ngModelCtrl: ngModelCtrl,
                                attrs: attrs,
                                currentValidationVars: currentValidationVars
                            });

                            // otherwise remove if a tpl was set before
                        } else if (!cfg.validationsTemplate && currentValidationVars.tpl && (Object.keys(currentValidationVars.tpl).length !== 0)) {
                            // don't forget to destroy the scope
                            currentValidationVars.privateScope.$destroy();
                            angular.element(currentValidationVars.tpl).remove();
                        }

                        // set asterisk for labels
                        if (cfg.setAsteriskForRequiredLabel && attrs.required === true && (!oldCfg || cfg.setAsteriskForRequiredLabel !== oldCfg.setAsteriskForRequiredLabel || cfg.asteriskStr !== oldCfg.asteriskStr)) {
                            setAsteriskForLabel(el, attrs, cfg);
                        }
                    }

                    function init()
                    {
                        $timeout(function ()
                        {
                            // if controller is not accessible via require
                            // get it from the element
                            formCtrl = el.controller('form');

                            // only execute if formCtrl is set
                            if (formCtrl && ngModelCtrl) {
                                // get configuration from parent form
                                if (!cfg) {
                                    cfg = formCtrl.ngFabFormConfig;
                                }

                                // overwrite email-validation
                                if (cfg.emailRegex && attrs.type === 'email') {
                                    ngModelCtrl.$validators.email = function (value)
                                    {
                                        return ngModelCtrl.$isEmpty(value) || cfg.emailRegex.test(value);
                                    };
                                }

                                if (ngFabForm.customValidators) {
                                    ngFabForm.customValidators(ngModelCtrl, attrs);
                                }

                                ngFabFormCycle();

                                // watch for config changes
                                configChangeWatcher = scope.$on(ngFabForm.formChangeEvent, function (ev, newCfg, oldCfg)
                                {
                                    cfg = newCfg;
                                    ngFabFormCycle(oldCfg);
                                });
                            }
                        }, 0);
                    }

                    // INIT
                    // after formCtrl should be ready
                    if (ngFabForm.config.watchForFormCtrl) {
                        formCtrlWatcher = scope.$watch(function ()
                        {
                            return el.controller('form');
                        }, function (newVal)
                        {
                            if (newVal) {
                                formCtrlWatcher();
                                init();
                            }
                        });
                    } else {
                        init();
                    }

                    scope.$on('$destroy', function ()
                    {
                        // destroy private scope set for validations
                        currentValidationVars.privateScope.$destroy();
                    });
                };
            }
        };
    }]);
<commit_msg>only destroy validation template scope if it was set<commit_after>angular.module('ngFabForm')
    .factory('ngFabFormValidationsDirective', ['ngFabForm', '$compile', '$templateRequest', '$rootScope', '$timeout', function (ngFabForm, $compile, $templateRequest, $rootScope, $timeout)
    {
        'use strict';

        function insertValidationMsgs(params)
        {
            var el = params.el;
            var cfg = params.cfg;
            var formCtrl = params.formCtrl;
            var ngModelCtrl = params.ngModelCtrl;
            var attrs = params.attrs;

            // remove error tpl if any
            if (params.currentValidationVars.tpl && (Object.keys(params.currentValidationVars.tpl).length !== 0)) {
                angular.element(params.currentValidationVars.tpl).remove();
            }

            // load validation directive template
            $templateRequest(cfg.validationsTemplate)
                .then(function processTemplate(html)
                {
                    // create new scope for validation messages
                    var privateScope = $rootScope.$new(true);
                    // assign to currentValidationVars to be destroyed later
                    params.currentValidationVars.privateScope = privateScope;

                    // add custom (attr) validations
                    html = ngFabForm.addCustomValidations(html, attrs);

                    privateScope.attrs = attrs;
                    privateScope.form = formCtrl;
                    privateScope.field = ngModelCtrl;

                    // compile and insert messages
                    var compiledAlert = $compile(html.children())(privateScope);
                    params.currentValidationVars.tpl = compiledAlert[0];

                    ngFabForm.insertErrorTpl(compiledAlert[0], el, attrs);
                });
        }

        function setAsteriskForLabel(el, attrs, cfg)
        {
            var labels = document.querySelectorAll('label[for="' + attrs.name + '"]');
            // if nothing is found check previous element
            if (!labels || labels.length < 1) {
                var elBefore = el[0].previousElementSibling;
                if (elBefore && elBefore.tagName === 'LABEL') {
                    labels = [elBefore];
                }
            }

            // set asterisk for match(es)
            if (labels && labels.length > 0 && attrs.type !== 'radio' && attrs.type !== 'checkbox') {
                for (var i = 0; i < labels.length; i++) {
                    var label = labels[i];
                    // don't append twice if multiple inputs with the same name
                    if (label.textContent.slice(-cfg.asteriskStr.length) !== cfg.asteriskStr) {
                        label.textContent = label.textContent + cfg.asteriskStr;
                    }
                }
            }
        }

        // return factory
        return {
            restrict: 'E',
            require: '?ngModel',
            compile: function (el, attrs)
            {
                // don't execute for buttons
                if (attrs.type) {
                    if (attrs.type.toLowerCase() === 'submit' || attrs.type.toLowerCase() === 'button') {
                        return;
                    }
                }

                // only execute if ng-model is present and
                // no name attr is set already
                // NOTE: needs to be set in $compile-function for the validation too work
                if (ngFabForm.config.setNamesByNgModel && attrs.ngModel && !attrs.name) {
                    // set name attribute if none is set
                    el.attr('name', attrs.ngModel);
                    attrs.name = attrs.ngModel;
                }

                // Linking function
                return function (scope, el, attrs, ngModelCtrl)
                {

                    var cfg;
                    // assigned via element.controller
                    var formCtrl;
                    var configChangeWatcher;
                    var formCtrlWatcher;
                    var currentValidationVars = {
                        // is in object to be passed by reference
                        tpl: undefined,
                        privateScope: undefined
                    };

                    function ngFabFormCycle(oldCfg)
                    {
                        // apply validation messages
                        // only if required controllers and validators are set
                        if (ngModelCtrl && cfg.validationsTemplate && ((Object.keys(ngModelCtrl.$validators).length !== 0) || (Object.keys(ngModelCtrl.$asyncValidators).length !== 0)) && (!oldCfg || cfg.validationsTemplate !== oldCfg.validationsTemplate)) {
                            insertValidationMsgs({
                                el: el,
                                cfg: cfg,
                                formCtrl: formCtrl,
                                ngModelCtrl: ngModelCtrl,
                                attrs: attrs,
                                currentValidationVars: currentValidationVars
                            });

                            // otherwise remove if a tpl was set before
                        } else if (!cfg.validationsTemplate && currentValidationVars.tpl && (Object.keys(currentValidationVars.tpl).length !== 0)) {
                            // don't forget to destroy the scope
                            currentValidationVars.privateScope.$destroy();
                            angular.element(currentValidationVars.tpl).remove();
                        }

                        // set asterisk for labels
                        if (cfg.setAsteriskForRequiredLabel && attrs.required === true && (!oldCfg || cfg.setAsteriskForRequiredLabel !== oldCfg.setAsteriskForRequiredLabel || cfg.asteriskStr !== oldCfg.asteriskStr)) {
                            setAsteriskForLabel(el, attrs, cfg);
                        }
                    }

                    function init()
                    {
                        $timeout(function ()
                        {
                            // if controller is not accessible via require
                            // get it from the element
                            formCtrl = el.controller('form');

                            // only execute if formCtrl is set
                            if (formCtrl && ngModelCtrl) {
                                // get configuration from parent form
                                if (!cfg) {
                                    cfg = formCtrl.ngFabFormConfig;
                                }

                                // overwrite email-validation
                                if (cfg.emailRegex && attrs.type === 'email') {
                                    ngModelCtrl.$validators.email = function (value)
                                    {
                                        return ngModelCtrl.$isEmpty(value) || cfg.emailRegex.test(value);
                                    };
                                }

                                if (ngFabForm.customValidators) {
                                    ngFabForm.customValidators(ngModelCtrl, attrs);
                                }

                                ngFabFormCycle();

                                // watch for config changes
                                configChangeWatcher = scope.$on(ngFabForm.formChangeEvent, function (ev, newCfg, oldCfg)
                                {
                                    cfg = newCfg;
                                    ngFabFormCycle(oldCfg);
                                });
                            }
                        }, 0);
                    }

                    // INIT
                    // after formCtrl should be ready
                    if (ngFabForm.config.watchForFormCtrl) {
                        formCtrlWatcher = scope.$watch(function ()
                        {
                            return el.controller('form');
                        }, function (newVal)
                        {
                            if (newVal) {
                                formCtrlWatcher();
                                init();
                            }
                        });
                    } else {
                        init();
                    }

                    scope.$on('$destroy', function ()
                    {
                        // destroy private scope set for validations if it was set
                        if (currentValidationVars && currentValidationVars.privateScope) {
                            currentValidationVars.privateScope.$destroy();
                        }
                    });
                };
            }
        };
    }]);
<|endoftext|>


<commit_before>angular.module('n52.core.map', [])
        .factory('mapService', ['$rootScope', 'leafletBoundsHelpers', 'interfaceService', 'statusService', 'settingsService', '$translate',
            function ($rootScope, leafletBoundsHelpers, interfaceService, statusService, settingsService, $translate) {
                var stationMarkerIcon = settingsService.stationIconOptions ? settingsService.stationIconOptions : {};
                var baselayer = settingsService.baselayer ? settingsService.baselayer : {
                    osm: {
                        name: 'Open Street Map',
                        type: 'xyz',
                        url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        layerOptions: {
                            showOnSelector: true
                        }
                    }
                };
                var overlays = angular.extend(settingsService.overlays,
                        {
                            cluster: {
                                name: 'Stations',
                                type: 'markercluster',
                                visible: true,
                                layerOptions: {
                                    showOnSelector: false
                                }
                            }
                        });
                var map = {};
                var aggregateCounter;
                var aggregateBounds;

                var init = function () {
                    map.markers = {};
                    map.paths = {};
                    map.popup = {};
                    map.bounds = {};
                    map.center = {};
                    map.layers = {
                        baselayers: baselayer,
                        overlays: overlays
                    };

                    $rootScope.$on('allPhenomenaSelected', function (evt) {
                        map.selectedPhenomenonId = null;
                        requestStations();
                    });
                    $rootScope.$on('phenomenonSelected', function (evt, phenomenon) {
                        map.selectedPhenomenonId = phenomenon.id;
                        requestStations(phenomenon.id);
                    });
                    $rootScope.$on('redrawStations', function (evt, phenomenon) {
                        requestStations(map.selectedPhenomenonId);
                    });
                    $rootScope.$on('newProviderSelected', function (evt) {
                        requestStations();
                    });

                    requestStations();
                };

                var requestStations = function (phenomenon) {
                    var params;
                    if (settingsService.aggregateServicesInMap && angular.isUndefined(statusService.status.apiProvider.url)) {
                        requestAggregatedStations(phenomenon);
                    } else {
                        var provider = statusService.status.apiProvider;
                        if (statusService.status.concentrationMarker && phenomenon) {
                            params = {
                                service: provider.serviceID,
                                phenomenon: phenomenon,
                                expanded: true,
                                force_latest_values: true,
                                status_intervals: true
                            };
                            interfaceService.getTimeseries(null, provider.url, params).then(function (data) {
                                createMarkers(data, provider.url, provider.serviceID);
                            });
                        } else {
                            params = {
                                service: provider.serviceID,
                                phenomenon: phenomenon
                            };
                            interfaceService.getStations(null, provider.url, params).then(function (data) {
                                createMarkers(data, provider.url, provider.serviceID);
                            });
                        }
                    }
                };

                requestAggregatedStations = function (phenomenon) {
                    aggregateCounter = 0;
                    aggregateBounds = null;
                    angular.copy({}, map.markers);
                    angular.copy({}, map.paths);
                    angular.copy({}, map.bounds);
                    angular.forEach(settingsService.restApiUrls, function (id, url) {
                        interfaceService.getServices(url).then(function (providers) {
                            angular.forEach(providers, function (provider) {
                                aggregateCounter++;
                                interfaceService.getStations(null, url, {
                                    service: provider.id,
                                    phenomenon: phenomenon
                                }).then(function (data) {
                                    createAggregatedStations(data, url, provider.id + id);
                                });
                            });
                        });
                    });
                };

                var createAggregatedStations = function (data, serviceUrl, serviceId) {
                    aggregateCounter--;
                    if (data.length > 0) {
                        var firstElemCoord = getCoordinates(data[0]);
                        if (!angular.isObject(aggregateBounds)) {
                            aggregateBounds = {
                                topmost: firstElemCoord[1],
                                bottommost: firstElemCoord[1],
                                leftmost: firstElemCoord[0],
                                rightmost: firstElemCoord[0]
                            };
                        }
                        angular.forEach(data, function (elem) {
                            var geom = getCoordinates(elem);
                            if (!isNaN(geom[0]) || !isNaN(geom[1])) {
                                if (geom[0] > aggregateBounds.rightmost) {
                                    aggregateBounds.rightmost = geom[0];
                                }
                                if (geom[0] < aggregateBounds.leftmost) {
                                    aggregateBounds.leftmost = geom[0];
                                }
                                if (geom[1] > aggregateBounds.topmost) {
                                    aggregateBounds.topmost = geom[1];
                                }
                                if (geom[1] < aggregateBounds.bottommost) {
                                    aggregateBounds.bottommost = geom[1];
                                }
                                if (statusService.status.concentrationMarker && isTimeseries(elem)) {
                                    addColoredCircle(geom, elem);
                                } else {
                                    addNormalMarker(geom, elem, serviceUrl, serviceId);
                                }
                            }
                        });
                        angular.copy(leafletBoundsHelpers.createBoundsFromArray([
                            [parseFloat(aggregateBounds.bottommost), parseFloat(aggregateBounds.leftmost)],
                            [parseFloat(aggregateBounds.topmost), parseFloat(aggregateBounds.rightmost)]]), map.bounds);
                    }
                };

                var createMarkers = function (data, serviceUrl, serviceId) {
                    angular.copy({}, map.markers);
                    angular.copy({}, map.paths);
                    angular.copy({}, map.bounds);
                    if (data.length > 0) {
                        var firstElemCoord = getCoordinates(data[0]);
                        var topmost = firstElemCoord[1];
                        var bottommost = firstElemCoord[1];
                        var leftmost = firstElemCoord[0];
                        var rightmost = firstElemCoord[0];
                        angular.forEach(data, function (elem) {
                            var geom = getCoordinates(elem);
                            if (!isNaN(geom[0]) || !isNaN(geom[1])) {
                                if (geom[0] > rightmost) {
                                    rightmost = geom[0];
                                }
                                if (geom[0] < leftmost) {
                                    leftmost = geom[0];
                                }
                                if (geom[1] > topmost) {
                                    topmost = geom[1];
                                }
                                if (geom[1] < bottommost) {
                                    bottommost = geom[1];
                                }
                                if (statusService.status.concentrationMarker && isTimeseries(elem)) {
                                    addColoredCircle(geom, elem, serviceUrl, serviceId);
                                } else {
                                    addNormalMarker(geom, elem, serviceUrl, serviceId);
                                }
                            }
                        });
                        angular.copy(leafletBoundsHelpers.createBoundsFromArray([
                            [parseFloat(bottommost), parseFloat(leftmost)],
                            [parseFloat(topmost), parseFloat(rightmost)]]), map.bounds);
                    }
                };

                var isTimeseries = function (elem) {
                    return angular.isDefined(elem.station);
                };

                var getCoordinates = function (elem) {
                    if (elem.geometry && elem.geometry.coordinates) {
                        return elem.geometry.coordinates;
                    } else {
                        return elem.station.geometry.coordinates;
                    }
                };

                var addNormalMarker = function (geom, elem, serviceUrl, serviceId) {
                    var marker = {
                        lat: geom[1],
                        lng: geom[0],
                        icon: stationMarkerIcon,
                        stationsId: elem.properties.id,
                        url: serviceUrl
                    };
                    if (statusService.status.clusterStations) {
                        marker.layer = 'cluster';
                    }
                    map.markers[tidyUpStationId(elem.properties.id + serviceId)] = marker;
                };

                var addColoredCircle = function (geom, elem, serviceUrl, serviceId) {
                    var interval = getMatchingInterval(elem);
                    var fillcolor = interval && interval.color ? interval.color : settingsService.defaultMarkerColor;
                    map.paths[tidyUpStationId(elem.station.properties.id + serviceId)] = {
                        type: "circleMarker",
                        latlngs: {
                            lat: geom[1],
                            lng: geom[0]
                        },
                        color: '#000',
                        fillColor: fillcolor,
                        fill: true,
                        radius: 10,
                        weight: 2,
                        opacity: 1,
                        fillOpacity: 0.8,
                        stationsId: elem.station.properties.id,
                        url: serviceUrl
                    };
                };

                var tidyUpStationId = function (id) {
                    return id.replace('-', '');
                };

                var getMatchingInterval = function (elem) {
                    var matchedInterval = null;
                    if (elem.lastValue && elem.statusIntervals) {
                        var lastValue = elem.lastValue.value;
                        angular.forEach(elem.statusIntervals, function (interval) {
                            if (interval.upper === null) {
                                interval.upper = Number.MAX_VALUE;
                            }
                            if (interval.lower === null) {
                                interval.lower = Number.MIN_VALUE;
                            }
                            if (!isNaN(interval.upper) && !isNaN(interval.lower) && parseFloat(interval.lower) < lastValue && lastValue < parseFloat(interval.upper)) {
                                matchedInterval = interval;
                                return false;
                            }
                        });
                    }
                    return matchedInterval;
                };

                init();
                return {
                    map: map
                };
            }])
        .service('stationService', ['interfaceService',
            function (interfaceService) {
                var station = {
                    entry: {}
                };
                determineTimeseries = function (stationId, url) {
                    station.entry = {};
                    interfaceService.getStations(stationId, url).then(function (result) {
                        station.entry = result;
                        angular.forEach(result.properties.timeseries, function (timeseries, id) {
                            interfaceService.getTimeseries(id, url).then(function (ts) {
                                angular.extend(timeseries, ts);
                                timeseries.selected = true;
                            });
                        });
                    });
                };

                return {
                    determineTimeseries: determineTimeseries,
                    station: station
                };
            }]);<commit_msg>add a flag to show scale bar in map<commit_after>angular.module('n52.core.map', [])
        .factory('mapService', ['$rootScope', 'leafletBoundsHelpers', 'interfaceService', 'statusService', 'settingsService',
            function ($rootScope, leafletBoundsHelpers, interfaceService, statusService, settingsService) {
                var stationMarkerIcon = settingsService.stationIconOptions ? settingsService.stationIconOptions : {};
                var baselayer = settingsService.baselayer ? settingsService.baselayer : {
                    osm: {
                        name: 'Open Street Map',
                        type: 'xyz',
                        url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        layerOptions: {
                            showOnSelector: true
                        }
                    }
                };
                var overlays = angular.extend(settingsService.overlays,
                        {
                            cluster: {
                                name: 'Stations',
                                type: 'markercluster',
                                visible: true,
                                layerOptions: {
                                    showOnSelector: false
                                }
                            }
                        });
                var map = {};
                if (settingsService.showScale) {
                    map.controls = {
                        scale : true
                    };
                }
                var aggregateCounter;
                var aggregateBounds;

                var init = function () {
                    map.markers = {};
                    map.paths = {};
                    map.popup = {};
                    map.bounds = {};
                    map.center = {};
                    map.layers = {
                        baselayers: baselayer,
                        overlays: overlays
                    };

                    $rootScope.$on('allPhenomenaSelected', function (evt) {
                        map.selectedPhenomenonId = null;
                        requestStations();
                    });
                    $rootScope.$on('phenomenonSelected', function (evt, phenomenon) {
                        map.selectedPhenomenonId = phenomenon.id;
                        requestStations(phenomenon.id);
                    });
                    $rootScope.$on('redrawStations', function (evt, phenomenon) {
                        requestStations(map.selectedPhenomenonId);
                    });
                    $rootScope.$on('newProviderSelected', function (evt) {
                        requestStations();
                    });

                    requestStations();
                };

                var requestStations = function (phenomenon) {
                    var params;
                    if (settingsService.aggregateServicesInMap && angular.isUndefined(statusService.status.apiProvider.url)) {
                        requestAggregatedStations(phenomenon);
                    } else {
                        var provider = statusService.status.apiProvider;
                        if (statusService.status.concentrationMarker && phenomenon) {
                            params = {
                                service: provider.serviceID,
                                phenomenon: phenomenon,
                                expanded: true,
                                force_latest_values: true,
                                status_intervals: true
                            };
                            interfaceService.getTimeseries(null, provider.url, params).then(function (data) {
                                createMarkers(data, provider.url, provider.serviceID);
                            });
                        } else {
                            params = {
                                service: provider.serviceID,
                                phenomenon: phenomenon
                            };
                            interfaceService.getStations(null, provider.url, params).then(function (data) {
                                createMarkers(data, provider.url, provider.serviceID);
                            });
                        }
                    }
                };

                requestAggregatedStations = function (phenomenon) {
                    aggregateCounter = 0;
                    aggregateBounds = null;
                    angular.copy({}, map.markers);
                    angular.copy({}, map.paths);
                    angular.copy({}, map.bounds);
                    angular.forEach(settingsService.restApiUrls, function (id, url) {
                        interfaceService.getServices(url).then(function (providers) {
                            angular.forEach(providers, function (provider) {
                                aggregateCounter++;
                                interfaceService.getStations(null, url, {
                                    service: provider.id,
                                    phenomenon: phenomenon
                                }).then(function (data) {
                                    createAggregatedStations(data, url, provider.id + id);
                                });
                            });
                        });
                    });
                };

                var createAggregatedStations = function (data, serviceUrl, serviceId) {
                    aggregateCounter--;
                    if (data.length > 0) {
                        var firstElemCoord = getCoordinates(data[0]);
                        if (!angular.isObject(aggregateBounds)) {
                            aggregateBounds = {
                                topmost: firstElemCoord[1],
                                bottommost: firstElemCoord[1],
                                leftmost: firstElemCoord[0],
                                rightmost: firstElemCoord[0]
                            };
                        }
                        angular.forEach(data, function (elem) {
                            var geom = getCoordinates(elem);
                            if (!isNaN(geom[0]) || !isNaN(geom[1])) {
                                if (geom[0] > aggregateBounds.rightmost) {
                                    aggregateBounds.rightmost = geom[0];
                                }
                                if (geom[0] < aggregateBounds.leftmost) {
                                    aggregateBounds.leftmost = geom[0];
                                }
                                if (geom[1] > aggregateBounds.topmost) {
                                    aggregateBounds.topmost = geom[1];
                                }
                                if (geom[1] < aggregateBounds.bottommost) {
                                    aggregateBounds.bottommost = geom[1];
                                }
                                if (statusService.status.concentrationMarker && isTimeseries(elem)) {
                                    addColoredCircle(geom, elem);
                                } else {
                                    addNormalMarker(geom, elem, serviceUrl, serviceId);
                                }
                            }
                        });
                        angular.copy(leafletBoundsHelpers.createBoundsFromArray([
                            [parseFloat(aggregateBounds.bottommost), parseFloat(aggregateBounds.leftmost)],
                            [parseFloat(aggregateBounds.topmost), parseFloat(aggregateBounds.rightmost)]]), map.bounds);
                    }
                };

                var createMarkers = function (data, serviceUrl, serviceId) {
                    angular.copy({}, map.markers);
                    angular.copy({}, map.paths);
                    angular.copy({}, map.bounds);
                    if (data.length > 0) {
                        var firstElemCoord = getCoordinates(data[0]);
                        var topmost = firstElemCoord[1];
                        var bottommost = firstElemCoord[1];
                        var leftmost = firstElemCoord[0];
                        var rightmost = firstElemCoord[0];
                        angular.forEach(data, function (elem) {
                            var geom = getCoordinates(elem);
                            if (!isNaN(geom[0]) || !isNaN(geom[1])) {
                                if (geom[0] > rightmost) {
                                    rightmost = geom[0];
                                }
                                if (geom[0] < leftmost) {
                                    leftmost = geom[0];
                                }
                                if (geom[1] > topmost) {
                                    topmost = geom[1];
                                }
                                if (geom[1] < bottommost) {
                                    bottommost = geom[1];
                                }
                                if (statusService.status.concentrationMarker && isTimeseries(elem)) {
                                    addColoredCircle(geom, elem, serviceUrl, serviceId);
                                } else {
                                    addNormalMarker(geom, elem, serviceUrl, serviceId);
                                }
                            }
                        });
                        angular.copy(leafletBoundsHelpers.createBoundsFromArray([
                            [parseFloat(bottommost), parseFloat(leftmost)],
                            [parseFloat(topmost), parseFloat(rightmost)]]), map.bounds);
                    }
                };

                var isTimeseries = function (elem) {
                    return angular.isDefined(elem.station);
                };

                var getCoordinates = function (elem) {
                    if (elem.geometry && elem.geometry.coordinates) {
                        return elem.geometry.coordinates;
                    } else {
                        return elem.station.geometry.coordinates;
                    }
                };

                var addNormalMarker = function (geom, elem, serviceUrl, serviceId) {
                    var marker = {
                        lat: geom[1],
                        lng: geom[0],
                        icon: stationMarkerIcon,
                        stationsId: elem.properties.id,
                        url: serviceUrl
                    };
                    if (statusService.status.clusterStations) {
                        marker.layer = 'cluster';
                    }
                    map.markers[tidyUpStationId(elem.properties.id + serviceId)] = marker;
                };

                var addColoredCircle = function (geom, elem, serviceUrl, serviceId) {
                    var interval = getMatchingInterval(elem);
                    var fillcolor = interval && interval.color ? interval.color : settingsService.defaultMarkerColor;
                    map.paths[tidyUpStationId(elem.station.properties.id + serviceId)] = {
                        type: "circleMarker",
                        latlngs: {
                            lat: geom[1],
                            lng: geom[0]
                        },
                        color: '#000',
                        fillColor: fillcolor,
                        fill: true,
                        radius: 10,
                        weight: 2,
                        opacity: 1,
                        fillOpacity: 0.8,
                        stationsId: elem.station.properties.id,
                        url: serviceUrl
                    };
                };

                var tidyUpStationId = function (id) {
                    return id.replace('-', '');
                };

                var getMatchingInterval = function (elem) {
                    var matchedInterval = null;
                    if (elem.lastValue && elem.statusIntervals) {
                        var lastValue = elem.lastValue.value;
                        angular.forEach(elem.statusIntervals, function (interval) {
                            if (interval.upper === null) {
                                interval.upper = Number.MAX_VALUE;
                            }
                            if (interval.lower === null) {
                                interval.lower = Number.MIN_VALUE;
                            }
                            if (!isNaN(interval.upper) && !isNaN(interval.lower) && parseFloat(interval.lower) < lastValue && lastValue < parseFloat(interval.upper)) {
                                matchedInterval = interval;
                                return false;
                            }
                        });
                    }
                    return matchedInterval;
                };

                init();
                return {
                    map: map
                };
            }])
        .service('stationService', ['interfaceService',
            function (interfaceService) {
                var station = {
                    entry: {}
                };
                determineTimeseries = function (stationId, url) {
                    station.entry = {};
                    interfaceService.getStations(stationId, url).then(function (result) {
                        station.entry = result;
                        angular.forEach(result.properties.timeseries, function (timeseries, id) {
                            interfaceService.getTimeseries(id, url).then(function (ts) {
                                angular.extend(timeseries, ts);
                                timeseries.selected = true;
                            });
                        });
                    });
                };

                return {
                    determineTimeseries: determineTimeseries,
                    station: station
                };
            }]);<|endoftext|>


<commit_before>var path = require('path')

var secret
try { secret = require('./secret') } catch (err) {}

exports.desktopVersion = '0.17.2'

exports.isProd = process.env.NODE_ENV === 'production'
exports.port = exports.isProd ? 80 : 9000

exports.gitterBot = {
  ircChannel: '#webtorrent',
  ircNick: 'irc-gitter-bot',
  ircServer: 'irc.freenode.net',
  gitterRoom: 'feross/webtorrent',
  gitterApiKey: secret && secret.gitterApiKey
}

exports.logPath = process.env.NODE_ENV === 'production'
  ? '/home/feross/www/log/webtorrent.io'
  : path.join(__dirname, 'logs')
<commit_msg>always listen on port 9000<commit_after>var path = require('path')

var secret
try { secret = require('./secret') } catch (err) {}

exports.desktopVersion = '0.17.2'

exports.isProd = process.env.NODE_ENV === 'production'
exports.port = 9000

exports.gitterBot = {
  ircChannel: '#webtorrent',
  ircNick: 'irc-gitter-bot',
  ircServer: 'irc.freenode.net',
  gitterRoom: 'feross/webtorrent',
  gitterApiKey: secret && secret.gitterApiKey
}

exports.logPath = process.env.NODE_ENV === 'production'
  ? '/home/feross/www/log/webtorrent.io'
  : path.join(__dirname, 'logs')
<|endoftext|>


<commit_before>// ;(function () {

  var game = new Phaser.Game(GAME_WIDTH, GAME_HEIGHT, Phaser.AUTO, 'breakout', { preload: preload, create: create, update: update });
  var GAME_WIDTH = 800;
  var GAME_HEIGHT = 600;

  var background;

  var bricks;
  var BRICK_ROWS = 4;
  var BRICK_COLS = 15;
  var BRICK_START_X = 120;
  var BRICK_START_Y = 100;
  var BRICK_SPACING_X = 36;
  var BRICK_SPACING_Y = 52;

  var paddle;
  var PADDLE_Y = 500;
  var PADDLE_WIDTH = 48;

  var ball;
  var ballOnPaddle = true;
  var BALL_WIDTH = 16;
  var BALL_HEIGHT = 16;
  var BALL_RELEASE_VELOCITY_X = -75;
  var BALL_RELEASE_VELOCITY_Y = -300;
  var BALL_VELOCITY_MULTIPLIER_X = 10;

  var score = 0;
  var lives = 3;
  var scoreText;
  var livesText;
  var infoText;
  var TEXT_Y = 550;

  var socket;
  var remotePlayers = {};

  function preload() {
    console.log('preload invoked');
    game.load.atlas('breakout', '/assets/breakout.png', '/assets/breakout.json');
    game.load.image('starfield', '/assets/starfield.jpg');
  }

  function create() {
    console.log('create invoked');

    // Prevent game from pausing when browser tab loses focus
    game.stage.disableVisibilityChange = true;

    game.physics.startSystem(Phaser.Physics.ARCADE);

    background = game.add.tileSprite(0, 0, GAME_WIDTH, GAME_HEIGHT, 'starfield');

    // Check bounds collisions on all walls except bottom
    game.physics.arcade.checkCollision.down = false;

    createBricks();
    createLocalPaddle();
    createLocalBall();
    createText();

    game.input.onDown.add(releaseBall, this);

    socket = io.connect(window.location.hostname);
    attachSocketHandlers();
  }

  function createBricks() {
    console.log('createBricks invoked');
    bricks = game.add.group();
    bricks.enableBody = true;
    bricks.physicsBodyType = Phaser.Physics.ARCADE;

    var brick;
    var brickCount = 0;

    for (var row = 0; row < BRICK_ROWS; row++) {
      for (var col = 0; col < BRICK_COLS; col++) {
        brick = bricks.create(
          BRICK_START_X + (col * BRICK_SPACING_X),
          BRICK_START_Y + (row * BRICK_SPACING_Y),
          'breakout',
          'brick_' + (row + 1) + '_1.png'
        );
        brick.body.bounce.set(1);
        brick.body.immovable = true;
        brick.brickIndex = brickCount++;
      }
    }
  }

  function createLocalPaddle() {
    console.log('createLocalPaddle invoked');
    paddle = game.add.sprite(game.world.centerX, PADDLE_Y, 'breakout', 'paddle_big.png');
    paddle.anchor.setTo(0.5, 0.5);

    game.physics.enable(paddle, Phaser.Physics.ARCADE);

    paddle.body.collideWorldBounds = true;
    paddle.body.bounce.set(1);
    paddle.body.immovable = true;
  }

  function createLocalBall() {
    console.log('createLocalBall invoked');
    ball = game.add.sprite(game.world.centerX, PADDLE_Y - BALL_HEIGHT, 'breakout', 'ball_1.png');
    ball.anchor.set(0.5);
    ball.checkWorldBounds = true;

    game.physics.enable(ball, Phaser.Physics.ARCADE);

    ball.body.collideWorldBounds = true;
    ball.body.bounce.set(1);

    ball.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);

    ball.events.onOutOfBounds.add(ballLost, this);
  }

  function releaseRemoteBall(data) {
    console.log('releaseRemoteBall invoked');

    //Creating a remoteball
    var remoteBall = game.add.sprite(data.posX, PADDLE_Y - BALL_HEIGHT, 'breakout', 'ball_1.png');
    remoteBall.anchor.set(0.5);
    remoteBall.checkWorldBounds = true;
    game.physics.enable(remoteBall, Phaser.Physics.ARCADE);
    remoteBall.body.collideWorldBounds = true;
    remoteBall.body.bounce.set(1);
    remoteBall.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);
    remoteBall.animations.play('spin');

    remoteBall.body.x = data.posX;
    remoteBall.body.velocity.x = data.exitVelocityX;
    remoteBall.body.velocity.y = data.exitVelocityY;

    remotePlayers[data.remotePlayerID]["remotePlayerBall"] = remoteBall;
    // ball.events.onOutOfBounds.add(ballLost, this);
  }

  function createText() {
    console.log('createText invoked');
    scoreText = game.add.text(32, TEXT_Y, 'score: 0',
      { font: '20px Arial', fill: '#ffffff', align: 'left' });
    livesText = game.add.text(GAME_WIDTH - 120, TEXT_Y, 'lives: 3',
      { font: '20px Arial', fill: '#ffffff', align: 'left' });
    infoText = game.add.text(game.world.centerX, GAME_HEIGHT * (2 / 3), 'Click to start',
      { font: '40px Arial', fill: '#ffffff', align: 'center' });
    infoText.anchor.setTo(0.5, 0.5);
  }

  function attachSocketHandlers() {
    console.log('attachSocketHandlers invoked');
    socket.on('connect', onSocketConnect);
    socket.on('disconnect', onSocketDisconnect);
    socket.on('new player', onNewPlayer);
    socket.on('remove player', onRemovePlayer);
    socket.on('initial bricks', onInitialBricks);
    socket.on('brick kill to other clients', onBrickKillToOtherClients);
    socket.on('paddle release ball', onPaddleReleaseBall);
    socket.on('ball hit paddle', onBallHitPaddle);
    socket.on('existing ball', onExistingBall);
    socket.on('kill remote ball', onKillRemoteBall);
  }

  function onKillRemoteBall(data) {
    console.log('onKillRemoteBall invoked');

    remotePlayers[data.remotePlayerID]["remotePlayerBall"].kill();
  }

  function onExistingBall(data) {
    console.log('onExistingBall invoked');
    //Creating a remoteball

    var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];

    if (typeof b === "undefined") {
      var remoteBall = game.add.sprite(data.posX, data.posY, 'breakout', 'ball_1.png');
      remoteBall.anchor.set(0.5);
      remoteBall.checkWorldBounds = true;
      game.physics.enable(remoteBall, Phaser.Physics.ARCADE);
      remoteBall.body.collideWorldBounds = true;
      remoteBall.body.bounce.set(1);
      remoteBall.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);
      remoteBall.animations.play('spin');

      remoteBall.body.velocity.x = data.velocityX;
      remoteBall.body.velocity.y = data.velocityY;

      remotePlayers[data.remotePlayerID]["remotePlayerBall"] = remoteBall;
    }
  }

  function onBallHitPaddle(data) {
    // Identify remote player's ball and change its velocity accordingly
    var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];
    b.body.velocity.x = data.exitVelocityX;
    b.body.velocity.y = data.exitVelocityY;
  }

  function onPaddleReleaseBall(data) {
    console.log("received message that other client has released ball");
    releaseRemoteBall(data);
  }

  function onSocketConnect() {
    console.log('onSocketConnect invoked');
    console.log('emitting "new player" message with existingBricks = ' + bricksString(bricks));
    socket.emit('new player', { existingBricks: bricksString(bricks) });
  }

  function onSocketDisconnect() {
    console.log('onSocketDisconnect invoked');
  }

  function onNewPlayer(data) {

    // Notify new player of client's ball position and velocity, but only do so if player hasn't released ball

    if (!ballOnPaddle) {
      socket.emit('existing ball', {
        velocityX: ball.body.velocity.x,
        velocityY: ball.body.velocity.y,
        posX: ball.body.position.x,
        posY: ball.body.position.y
      });
    }

    // Commented out due to errors with Converting circular structure to JSON
    console.log('onNewPlayer invoked. data = ' + JSON.stringify(data));
    remotePlayers[data.id] = { score: data.score };
    // console.log(data.id + ' added to remotePlayers: ' + JSON.stringify(remotePlayers));
  }

  // function onRemovePlayer(data) {
  //   if (delete remotePlayers[data.id]) {
  //     console.log(data.id + ' removed from remotePlayers: ' + JSON.stringify(remotePlayers));
  //   } else {
  //     console.log(data.id + ' not found in remotePlayers');
  //   }
  // }

  function onRemovePlayer(data) {  remotePlayers[data.id].remotePlayerBall.parent.removeChild(remotePlayers[data.id].remotePlayerBall);
    var newRemotePlayers = {};

    for (var i in remotePlayers) {
      if (i != data.id) {
        newRemotePlayers[i] = remotePlayers[i];
      }
    }

    remotePlayers = newRemotePlayers;
  }

  function onInitialBricks(data) {
    console.log('onInitialBricks invoked. data = ' + JSON.stringify(data));
    var i;
    for (var row = 0; row < BRICK_ROWS; row++) {
      for (var col = 0; col < BRICK_COLS; col++) {
        i = row * BRICK_COLS + col;
        if (data.initialBricks.charAt(i) === "0") {
          bricks.children[i].kill();
        }
      }
    }
  }

  function onBrickKillToOtherClients(data) {
    // console.log('onBrickKillToOtherClients invoked');

    // Change the velocity of the remote ball
    if (typeof remotePlayers[data.remotePlayerID] != "undefined") {
      var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];
      b.body.velocity.x = data.exitVelocityX;
      b.body.velocity.y = data.exitVelocityY;
    }

    bricks.children[data.brickIndex].kill();
  }

  function update() {
    paddle.body.x = game.input.x - 0.5 * PADDLE_WIDTH;

    if (paddle.body.x < 0) {
      paddle.body.x = 0;
    } else if (paddle.body.x > game.width - PADDLE_WIDTH) {
      paddle.body.x = game.width - PADDLE_WIDTH;
    }

    if (ballOnPaddle) {
      ball.body.x = paddle.body.x + 0.5 * PADDLE_WIDTH - 0.5 * BALL_WIDTH;
    } else {
      game.physics.arcade.collide(ball, paddle, ballHitPaddle, null, this);
      game.physics.arcade.collide(ball, bricks, ballHitBrick, null, this);
    }

    if (bricks.countLiving() === 0) {
      startNewRound();
    }
  }

  function releaseBall() {
    if (ballOnPaddle) {
      ballOnPaddle = false;
      ball.body.velocity.x = BALL_RELEASE_VELOCITY_X * ( Math.random() * 4 - 1 );
      ball.body.velocity.y = BALL_RELEASE_VELOCITY_Y;
      ball.animations.play('spin');
      infoText.visible = false;

      // Tell other clients of the release of ball
      socket.emit('paddle release ball', {
        exitVelocityX: ball.body.velocity.x,
        exitVelocityY: ball.body.velocity.y,
        posX: ball.body.x,
      });
    }
  }

  function ballLost() {
    socket.emit('kill remote ball');

    lives--;
    livesText.text = 'lives: ' + lives;
    if (lives === 0) {
      gameOver();
    } else {
      putBallOnPaddle();
    }
  }

  function putBallOnPaddle() {
    ballOnPaddle = true;
    ball.reset(paddle.body.x + 0.5 * PADDLE_WIDTH - 0.5 * BALL_WIDTH, PADDLE_Y - BALL_HEIGHT);
    ball.animations.stop();
  }

  function startNewRound() {
    socket.emit('kill remote ball');

    console.log('startNewRound invoked');
    bricks.callAll('revive');
    if (lives !==0 ) {
      putBallOnPaddle();
      score += 1000;
      scoreText.text = 'score: ' + score;
      infoText.text = 'Next Round';
    }
  }

  function gameOver() {
    ball.body.velocity.setTo(0, 0);

    infoText.text = 'Game Over!';
    infoText.visible = true;
  }

  function ballHitBrick(_ball, _brick) {
    socket.emit('brick kill from client', {
      brickIndex: _brick.brickIndex,
      exitVelocityX: ball.body.velocity.x,
      exitVelocityY: ball.body.velocity.y
    });

    _brick.kill();

    score += 10;
    scoreText.text = 'score: ' + score;
  }

  function ballHitPaddle(_ball, _paddle) {
    var diff = 0;

    if (_ball.body.x < _paddle.body.x) {
      //  Ball is on the left-hand side of the paddle
      diff = _paddle.body.x - _ball.body.x;
      _ball.body.velocity.x = (BALL_VELOCITY_MULTIPLIER_X * diff * -1);
    } else if (_ball.body.x > _paddle.body.x) {
      //  Ball is on the right-hand side of the paddle
      diff = _ball.body.x -_paddle.body.x;
      _ball.body.velocity.x = (BALL_VELOCITY_MULTIPLIER_X * diff);
    } else {
      //  Ball is perfectly in the middle
      //  Add a little random X to stop it bouncing straight up!
      _ball.body.velocity.x = BALL_VELOCITY_MULTIPLIER_X * 0.2 + Math.random() * BALL_VELOCITY_MULTIPLIER_X * 0.8;
    }

    socket.emit('ball hit paddle', {
      exitVelocityX: _ball.body.velocity.x,
      exitVelocityY: _ball.body.velocity.y
    });
  }

  function bricksString(bricksGroup) {
    var i;
    var length = bricksGroup.children.length;
    var result = "";
    for (i = 0; i < length; i++) {
      if (bricksGroup.children[i].alive) {
        result += "1";
      } else {
        result += "0";
      }
    }
    return result;
  }

// }());
<commit_msg>uncommented closure on game.js<commit_after>;(function () {

  var game = new Phaser.Game(GAME_WIDTH, GAME_HEIGHT, Phaser.AUTO, 'breakout', { preload: preload, create: create, update: update });
  var GAME_WIDTH = 800;
  var GAME_HEIGHT = 600;

  var background;

  var bricks;
  var BRICK_ROWS = 4;
  var BRICK_COLS = 15;
  var BRICK_START_X = 120;
  var BRICK_START_Y = 100;
  var BRICK_SPACING_X = 36;
  var BRICK_SPACING_Y = 52;

  var paddle;
  var PADDLE_Y = 500;
  var PADDLE_WIDTH = 48;

  var ball;
  var ballOnPaddle = true;
  var BALL_WIDTH = 16;
  var BALL_HEIGHT = 16;
  var BALL_RELEASE_VELOCITY_X = -75;
  var BALL_RELEASE_VELOCITY_Y = -300;
  var BALL_VELOCITY_MULTIPLIER_X = 10;

  var score = 0;
  var lives = 3;
  var scoreText;
  var livesText;
  var infoText;
  var TEXT_Y = 550;

  var socket;
  var remotePlayers = {};

  function preload() {
    console.log('preload invoked');
    game.load.atlas('breakout', '/assets/breakout.png', '/assets/breakout.json');
    game.load.image('starfield', '/assets/starfield.jpg');
  }

  function create() {
    console.log('create invoked');

    // Prevent game from pausing when browser tab loses focus
    game.stage.disableVisibilityChange = true;

    game.physics.startSystem(Phaser.Physics.ARCADE);

    background = game.add.tileSprite(0, 0, GAME_WIDTH, GAME_HEIGHT, 'starfield');

    // Check bounds collisions on all walls except bottom
    game.physics.arcade.checkCollision.down = false;

    createBricks();
    createLocalPaddle();
    createLocalBall();
    createText();

    game.input.onDown.add(releaseBall, this);

    socket = io.connect(window.location.hostname);
    attachSocketHandlers();
  }

  function createBricks() {
    console.log('createBricks invoked');
    bricks = game.add.group();
    bricks.enableBody = true;
    bricks.physicsBodyType = Phaser.Physics.ARCADE;

    var brick;
    var brickCount = 0;

    for (var row = 0; row < BRICK_ROWS; row++) {
      for (var col = 0; col < BRICK_COLS; col++) {
        brick = bricks.create(
          BRICK_START_X + (col * BRICK_SPACING_X),
          BRICK_START_Y + (row * BRICK_SPACING_Y),
          'breakout',
          'brick_' + (row + 1) + '_1.png'
        );
        brick.body.bounce.set(1);
        brick.body.immovable = true;
        brick.brickIndex = brickCount++;
      }
    }
  }

  function createLocalPaddle() {
    console.log('createLocalPaddle invoked');
    paddle = game.add.sprite(game.world.centerX, PADDLE_Y, 'breakout', 'paddle_big.png');
    paddle.anchor.setTo(0.5, 0.5);

    game.physics.enable(paddle, Phaser.Physics.ARCADE);

    paddle.body.collideWorldBounds = true;
    paddle.body.bounce.set(1);
    paddle.body.immovable = true;
  }

  function createLocalBall() {
    console.log('createLocalBall invoked');
    ball = game.add.sprite(game.world.centerX, PADDLE_Y - BALL_HEIGHT, 'breakout', 'ball_1.png');
    ball.anchor.set(0.5);
    ball.checkWorldBounds = true;

    game.physics.enable(ball, Phaser.Physics.ARCADE);

    ball.body.collideWorldBounds = true;
    ball.body.bounce.set(1);

    ball.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);

    ball.events.onOutOfBounds.add(ballLost, this);
  }

  function releaseRemoteBall(data) {
    console.log('releaseRemoteBall invoked');

    //Creating a remoteball
    var remoteBall = game.add.sprite(data.posX, PADDLE_Y - BALL_HEIGHT, 'breakout', 'ball_1.png');
    remoteBall.anchor.set(0.5);
    remoteBall.checkWorldBounds = true;
    game.physics.enable(remoteBall, Phaser.Physics.ARCADE);
    remoteBall.body.collideWorldBounds = true;
    remoteBall.body.bounce.set(1);
    remoteBall.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);
    remoteBall.animations.play('spin');

    remoteBall.body.x = data.posX;
    remoteBall.body.velocity.x = data.exitVelocityX;
    remoteBall.body.velocity.y = data.exitVelocityY;

    remotePlayers[data.remotePlayerID]["remotePlayerBall"] = remoteBall;
    // ball.events.onOutOfBounds.add(ballLost, this);
  }

  function createText() {
    console.log('createText invoked');
    scoreText = game.add.text(32, TEXT_Y, 'score: 0',
      { font: '20px Arial', fill: '#ffffff', align: 'left' });
    livesText = game.add.text(GAME_WIDTH - 120, TEXT_Y, 'lives: 3',
      { font: '20px Arial', fill: '#ffffff', align: 'left' });
    infoText = game.add.text(game.world.centerX, GAME_HEIGHT * (2 / 3), 'Click to start',
      { font: '40px Arial', fill: '#ffffff', align: 'center' });
    infoText.anchor.setTo(0.5, 0.5);
  }

  function attachSocketHandlers() {
    console.log('attachSocketHandlers invoked');
    socket.on('connect', onSocketConnect);
    socket.on('disconnect', onSocketDisconnect);
    socket.on('new player', onNewPlayer);
    socket.on('remove player', onRemovePlayer);
    socket.on('initial bricks', onInitialBricks);
    socket.on('brick kill to other clients', onBrickKillToOtherClients);
    socket.on('paddle release ball', onPaddleReleaseBall);
    socket.on('ball hit paddle', onBallHitPaddle);
    socket.on('existing ball', onExistingBall);
    socket.on('kill remote ball', onKillRemoteBall);
  }

  function onKillRemoteBall(data) {
    console.log('onKillRemoteBall invoked');

    remotePlayers[data.remotePlayerID]["remotePlayerBall"].kill();
  }

  function onExistingBall(data) {
    console.log('onExistingBall invoked');
    //Creating a remoteball

    var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];

    if (typeof b === "undefined") {
      var remoteBall = game.add.sprite(data.posX, data.posY, 'breakout', 'ball_1.png');
      remoteBall.anchor.set(0.5);
      remoteBall.checkWorldBounds = true;
      game.physics.enable(remoteBall, Phaser.Physics.ARCADE);
      remoteBall.body.collideWorldBounds = true;
      remoteBall.body.bounce.set(1);
      remoteBall.animations.add('spin', [ 'ball_1.png', 'ball_2.png', 'ball_3.png', 'ball_4.png', 'ball_5.png' ], 50, true, false);
      remoteBall.animations.play('spin');

      remoteBall.body.velocity.x = data.velocityX;
      remoteBall.body.velocity.y = data.velocityY;

      remotePlayers[data.remotePlayerID]["remotePlayerBall"] = remoteBall;
    }
  }

  function onBallHitPaddle(data) {
    // Identify remote player's ball and change its velocity accordingly
    var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];
    b.body.velocity.x = data.exitVelocityX;
    b.body.velocity.y = data.exitVelocityY;
  }

  function onPaddleReleaseBall(data) {
    console.log("received message that other client has released ball");
    releaseRemoteBall(data);
  }

  function onSocketConnect() {
    console.log('onSocketConnect invoked');
    console.log('emitting "new player" message with existingBricks = ' + bricksString(bricks));
    socket.emit('new player', { existingBricks: bricksString(bricks) });
  }

  function onSocketDisconnect() {
    console.log('onSocketDisconnect invoked');
  }

  function onNewPlayer(data) {

    // Notify new player of client's ball position and velocity, but only do so if player hasn't released ball

    if (!ballOnPaddle) {
      socket.emit('existing ball', {
        velocityX: ball.body.velocity.x,
        velocityY: ball.body.velocity.y,
        posX: ball.body.position.x,
        posY: ball.body.position.y
      });
    }

    // Commented out due to errors with Converting circular structure to JSON
    console.log('onNewPlayer invoked. data = ' + JSON.stringify(data));
    remotePlayers[data.id] = { score: data.score };
    // console.log(data.id + ' added to remotePlayers: ' + JSON.stringify(remotePlayers));
  }

  // function onRemovePlayer(data) {
  //   if (delete remotePlayers[data.id]) {
  //     console.log(data.id + ' removed from remotePlayers: ' + JSON.stringify(remotePlayers));
  //   } else {
  //     console.log(data.id + ' not found in remotePlayers');
  //   }
  // }

  function onRemovePlayer(data) {  remotePlayers[data.id].remotePlayerBall.parent.removeChild(remotePlayers[data.id].remotePlayerBall);
    var newRemotePlayers = {};

    for (var i in remotePlayers) {
      if (i != data.id) {
        newRemotePlayers[i] = remotePlayers[i];
      }
    }

    remotePlayers = newRemotePlayers;
  }

  function onInitialBricks(data) {
    console.log('onInitialBricks invoked. data = ' + JSON.stringify(data));
    var i;
    for (var row = 0; row < BRICK_ROWS; row++) {
      for (var col = 0; col < BRICK_COLS; col++) {
        i = row * BRICK_COLS + col;
        if (data.initialBricks.charAt(i) === "0") {
          bricks.children[i].kill();
        }
      }
    }
  }

  function onBrickKillToOtherClients(data) {
    // console.log('onBrickKillToOtherClients invoked');

    // Change the velocity of the remote ball
    if (typeof remotePlayers[data.remotePlayerID] != "undefined") {
      var b = remotePlayers[data.remotePlayerID]["remotePlayerBall"];
      b.body.velocity.x = data.exitVelocityX;
      b.body.velocity.y = data.exitVelocityY;
    }

    bricks.children[data.brickIndex].kill();
  }

  function update() {
    paddle.body.x = game.input.x - 0.5 * PADDLE_WIDTH;

    if (paddle.body.x < 0) {
      paddle.body.x = 0;
    } else if (paddle.body.x > game.width - PADDLE_WIDTH) {
      paddle.body.x = game.width - PADDLE_WIDTH;
    }

    if (ballOnPaddle) {
      ball.body.x = paddle.body.x + 0.5 * PADDLE_WIDTH - 0.5 * BALL_WIDTH;
    } else {
      game.physics.arcade.collide(ball, paddle, ballHitPaddle, null, this);
      game.physics.arcade.collide(ball, bricks, ballHitBrick, null, this);
    }

    if (bricks.countLiving() === 0) {
      startNewRound();
    }
  }

  function releaseBall() {
    if (ballOnPaddle) {
      ballOnPaddle = false;
      ball.body.velocity.x = BALL_RELEASE_VELOCITY_X * ( Math.random() * 4 - 1 );
      ball.body.velocity.y = BALL_RELEASE_VELOCITY_Y;
      ball.animations.play('spin');
      infoText.visible = false;

      // Tell other clients of the release of ball
      socket.emit('paddle release ball', {
        exitVelocityX: ball.body.velocity.x,
        exitVelocityY: ball.body.velocity.y,
        posX: ball.body.x,
      });
    }
  }

  function ballLost() {
    socket.emit('kill remote ball');

    lives--;
    livesText.text = 'lives: ' + lives;
    if (lives === 0) {
      gameOver();
    } else {
      putBallOnPaddle();
    }
  }

  function putBallOnPaddle() {
    ballOnPaddle = true;
    ball.reset(paddle.body.x + 0.5 * PADDLE_WIDTH - 0.5 * BALL_WIDTH, PADDLE_Y - BALL_HEIGHT);
    ball.animations.stop();
  }

  function startNewRound() {
    socket.emit('kill remote ball');

    console.log('startNewRound invoked');
    bricks.callAll('revive');
    if (lives !==0 ) {
      putBallOnPaddle();
      score += 1000;
      scoreText.text = 'score: ' + score;
      infoText.text = 'Next Round';
    }
  }

  function gameOver() {
    ball.body.velocity.setTo(0, 0);

    infoText.text = 'Game Over!';
    infoText.visible = true;
  }

  function ballHitBrick(_ball, _brick) {
    socket.emit('brick kill from client', {
      brickIndex: _brick.brickIndex,
      exitVelocityX: ball.body.velocity.x,
      exitVelocityY: ball.body.velocity.y
    });

    _brick.kill();

    score += 10;
    scoreText.text = 'score: ' + score;
  }

  function ballHitPaddle(_ball, _paddle) {
    var diff = 0;

    if (_ball.body.x < _paddle.body.x) {
      //  Ball is on the left-hand side of the paddle
      diff = _paddle.body.x - _ball.body.x;
      _ball.body.velocity.x = (BALL_VELOCITY_MULTIPLIER_X * diff * -1);
    } else if (_ball.body.x > _paddle.body.x) {
      //  Ball is on the right-hand side of the paddle
      diff = _ball.body.x -_paddle.body.x;
      _ball.body.velocity.x = (BALL_VELOCITY_MULTIPLIER_X * diff);
    } else {
      //  Ball is perfectly in the middle
      //  Add a little random X to stop it bouncing straight up!
      _ball.body.velocity.x = BALL_VELOCITY_MULTIPLIER_X * 0.2 + Math.random() * BALL_VELOCITY_MULTIPLIER_X * 0.8;
    }

    socket.emit('ball hit paddle', {
      exitVelocityX: _ball.body.velocity.x,
      exitVelocityY: _ball.body.velocity.y
    });
  }

  function bricksString(bricksGroup) {
    var i;
    var length = bricksGroup.children.length;
    var result = "";
    for (i = 0; i < length; i++) {
      if (bricksGroup.children[i].alive) {
        result += "1";
      } else {
        result += "0";
      }
    }
    return result;
  }

}());
<|endoftext|>