<commit_before>angular.module('lg.controllers')
// URL to API that enables cross-origin requests to anywhere
 .value('CORSURL', '//cors-anywhere.herokuapp.com/')
 .value('APIURL', 'https://volontario-server.herokuapp.com/')
 .value('UPDATE_INTERVAL', '10000000')

.controller('MapCtrl', function($scope, $q, $ionicLoading, $compile, $http, $timeout, $interval, $cordovaDatePicker, CORSURL, APIURL, Categories,MapSettings,UPDATE_INTERVAL) {

      var markers = [];
      var markerObj = [];
      var infowindow = null;
      var updateMarkersPromise;
      var clearMarkers = function(){
        _.map(markerObj,function(item){
          item.setMap(null);
        })
      }
      var loading;

    $scope.showDatePickerStart = function () {
      var options = {
        date: new Date(),
        mode: 'datetime',
        minDate:  moment().subtract(100, 'years').toDate(),
        allowOldDates: true,
        allowFutureDates: false,
        doneButtonLabel: 'Done',
        doneButtonColor: '#000000',
        cancelButtonLabel: 'Abort',
        cancelButtonColor: '#000000'
      };

      $cordovaDatePicker.show(options).then(function(date){
      });
    };

    $scope.showDatePickerEnd = function () {
      var options = {
        date: new Date(),
        mode: 'datetime',
        minDate:  moment().subtract(100, 'years').toDate(),
        allowOldDates: true,
        allowFutureDates: false,
        doneButtonLabel: 'Done',
        doneButtonColor: '#000000',
        cancelButtonLabel: 'Abort',
        cancelButtonColor: '#000000'
      };

  $cordovaDatePicker.show(options).then(function(date){
  });
};

      function initialize() {

        var mapOptions = MapSettings;
        var map = new google.maps.Map(document.getElementById("map"),
            mapOptions);
        $scope.map = map;

        map.addListener('zoom_changed', function() {
          _.map(markerObj,function(item,i){
            var t =  Math.round(markers[i].traffic.average*100) || 0;
            animateCircle(i,t,0)
          })
        });

      }
      initialize();

      $scope.clearAll = function(){
        clearMarkers();
        Categories.clear();
        updateActive();
      }

      $scope.centerOnMe = function() {
        if(!$scope.map) {
          return;
        }

        $scope.loading = $ionicLoading.show({
          content: 'Loading...',
          showBackdrop: false
        });

        navigator.geolocation.getCurrentPosition(function(pos) {
          $scope.map.setCenter(new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude));
          $scope.loading.hide();
        }, function(error) {
          alert('Unable to get location: ' + error.message);
        });
      };

      $scope.clickTest = function() {
        alert('Example of infowindow with ng-click')
      };

      var CalendarStart = '
\
                            \
                            \
                              \
                              <button class="button button-block button-stable icon-left ion-calendar" ng-click="showDatePickerStart()" data-instantActivate>\
                                  Alku{{inputNavParams.current_value}}\
                              </button>\
                          ';

      var CalendarEnd =  '
\
                          \
                              \
                              \
                              <button class="button button-block button-stable icon-left ion-calendar" ng-click="showDatePickerEnd()" data-instantActivate>\
                                  loppu{{inputNavParams.current_value}}\
                              </button>\
                          ';

      var searchLocations = function() {
        loading = $ionicLoading.show({
          content: 'Loading...',
          showBackdrop: false
        });

        var selectedTags = Categories.getActive();
        clearMarkers();
        var locations = $http.get(CORSURL+APIURL+"/locations"),
        events = $http.get(CORSURL+APIURL+"/events?"+selectedTags);

          $q.all([locations, events]).then(function successCallback(response) {
            // this callback will be called asynchronously
            // when the response is available

            // hide loader
            $timeout(function(){
              $ionicLoading.hide();
            });
            var geocoder = new google.maps.Geocoder();
            var res = response[0].data.concat(response[1].data);
            var traffic;
            var tag;
            markers = [];
            markerObj = [];
            infowindow = new google.maps.InfoWindow({
                  content: "content"
            });
            //Clear previous update promise
            $interval.cancel(updateMarkersPromise);

            //Start new update process
            updateMarkersPromise = $interval(updateMarkers, UPDATE_INTERVAL);

            for(var i=0;i < res.length; i++){
                  markers.push(res[i]);
                  content = '
'+JSON.stringify(res[i].name) || JSON.stringify(res[i].title);
                  content += '
lue lisää..';
                  content += '
'+ CalendarStart +'' + CalendarEnd;
                  content += '<button class="btn waves-effect waves-light" ng-click="addToCalendar();" name="action">Lisää kalenteriin</button>';
                  var compiled = $compile(content)($scope);

                  tag = Categories.getIcon(res[i].category);
                  var title = res[i].title;
                  // this is still random
                  traffic = 1;
                  // define activity class
                  var light = 'darkest';
                  if(traffic<25){
                    light = 'light';
                  }
                  else if(traffic<50){
                    light = 'medium';
                  }
                  else if(traffic<75){
                    light = 'dark';
                  }

                  // define label class
                  var className = "labels "+light;

                  // Get center
                  var coords = new google.maps.LatLng(
                    res[i].coordinates.latitude,
                    res[i].coordinates.longitude
                    );
                    // Set marker also
                    marker = new MarkerWithLabel({
                      position: coords,
                      map: $scope.map,
                      title: title,
                      icon: ' ',
                      labelContent: '',
                      labelAnchor: new google.maps.Point(18, 18),
                      labelClass: className,
                      html: content
                    });
                    markerObj.push(marker);
                    animateCircle(i,traffic,500);

                    google.maps.event.addListener(marker, 'click', function() {
                      infowindow.setContent( compiled[0] );
                      infowindow.open($scope.map,this);
                    });

              }
            }, function errorCallback(response) {
              $ionicLoading.hide();
          });
      };

      var updateMarkers = function(){
        var selectedTags = Categories.getActive();
        $http({
          method: 'GET',
          headers : {"content-type" : "application/json"},
          params: {category: selectedTags.join(',')},
          url: CORSURL+APIURL}).then(function successCallback(response) {
            var res = response.data;
            for(var i=0;i < res.length; i++){

                var traffic = Math.round(res[i].traffic.average*100 ) || 0;
                var light = 'darkest';
                  if(traffic<25){
                    light = 'light';
                  }else if(traffic<50){
                    light = 'medium';
                  } else if(traffic<75){
                    light = 'dark';
                  }

                var content = res[i].title +'
' +res[i].description;
                var tag = Categories.getIcon(res[i].category);

                var classes = "labels "+light;
                if(res[i].discounts[0]){
                    content = res[i].title +'
'+ (res[i].discounts[0].description||'')+'';
                    classes += ' has-sale';
                }
                var labelContent = '';

                // update content
                markerObj[i].labelContent = labelContent;
                markerObj[i].labelClass  = classes;
                animateCircle(i,traffic,500);

            }
          })
      }

      $scope.categories = Categories.all();

      var animateCircle = function(id, traffic,time){
         $timeout(function(){
          if(document.getElementById('circle_'+id)){
            document.getElementById('circle_'+id).style.strokeDashoffset = (100-traffic);
          }
         },time);
      }

      $scope.toggleCategory = function(id){
        Categories.toggle(id);
        searchLocations();
        updateActive();
      }

      var updateActive = function(){
        $scope.selectedCategories = Categories.getActive();
        console.log($scope.selectedCategories );
      }

      updateActive();
      searchLocations();

    });

<commit_msg>fixed info window content<commit_after>angular.module('lg.controllers')
// URL to API that enables cross-origin requests to anywhere
 .value('CORSURL', '//cors-anywhere.herokuapp.com/')
 .value('APIURL', 'https://volontario-server.herokuapp.com/')
 .value('UPDATE_INTERVAL', '10000000')

.controller('MapCtrl', function($scope, $q, $ionicLoading, $compile, $http, $timeout, $interval, $cordovaDatePicker, CORSURL, APIURL, Categories,MapSettings,UPDATE_INTERVAL) {

      var markers = [];
      var markerObj = [];
      var infowindow = null;
      var updateMarkersPromise;
      var clearMarkers = function(){
        _.map(markerObj,function(item){
          item.setMap(null);
        })
      }
      var loading;

    $scope.showDatePickerStart = function () {
      var options = {
        date: new Date(),
        mode: 'datetime',
        minDate:  moment().subtract(100, 'years').toDate(),
        allowOldDates: true,
        allowFutureDates: false,
        doneButtonLabel: 'Done',
        doneButtonColor: '#000000',
        cancelButtonLabel: 'Abort',
        cancelButtonColor: '#000000'
      };

      $cordovaDatePicker.show(options).then(function(date){
      });
    };

    $scope.showDatePickerEnd = function () {
      var options = {
        date: new Date(),
        mode: 'datetime',
        minDate:  moment().subtract(100, 'years').toDate(),
        allowOldDates: true,
        allowFutureDates: false,
        doneButtonLabel: 'Done',
        doneButtonColor: '#000000',
        cancelButtonLabel: 'Abort',
        cancelButtonColor: '#000000'
      };

  $cordovaDatePicker.show(options).then(function(date){
  });
};

      function initialize() {

        var mapOptions = MapSettings;
        var map = new google.maps.Map(document.getElementById("map"),
            mapOptions);
        $scope.map = map;

        map.addListener('zoom_changed', function() {
          _.map(markerObj,function(item,i){
            var t =  Math.round(markers[i].traffic.average*100) || 0;
            animateCircle(i,t,0)
          })
        });

      }
      initialize();

      $scope.clearAll = function(){
        clearMarkers();
        Categories.clear();
        updateActive();
      }

      $scope.centerOnMe = function() {
        if(!$scope.map) {
          return;
        }

        $scope.loading = $ionicLoading.show({
          content: 'Loading...',
          showBackdrop: false
        });

        navigator.geolocation.getCurrentPosition(function(pos) {
          $scope.map.setCenter(new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude));
          $scope.loading.hide();
        }, function(error) {
          alert('Unable to get location: ' + error.message);
        });
      };

      $scope.clickTest = function() {
        alert('Example of infowindow with ng-click')
      };

      var CalendarStart = '
\
                            \
                            \
                              \
                              <button class="button button-block button-stable icon-left ion-calendar" ng-click="showDatePickerStart()" data-instantActivate>\
                                  Alku{{inputNavParams.current_value}}\
                              </button>\
                          ';

      var CalendarEnd =  '
\
                          \
                              \
                              \
                              <button class="button button-block button-stable icon-left ion-calendar" ng-click="showDatePickerEnd()" data-instantActivate>\
                                  loppu{{inputNavParams.current_value}}\
                              </button>\
                          ';

      var searchLocations = function() {
        loading = $ionicLoading.show({
          content: 'Loading...',
          showBackdrop: false
        });

        var selectedTags = Categories.getActive();
        clearMarkers();
        var locations = $http.get(CORSURL+APIURL+"/locations"),
        events = $http.get(CORSURL+APIURL+"/events?"+selectedTags);

          $q.all([locations, events]).then(function successCallback(response) {
            // this callback will be called asynchronously
            // when the response is available

            // hide loader
            $timeout(function(){
              $ionicLoading.hide();
            });
            var geocoder = new google.maps.Geocoder();
            var res = response[0].data.concat(response[1].data);
            var traffic;
            var tag;
            markers = [];
            markerObj = [];
            infowindow = new google.maps.InfoWindow({
                  content: "content"
            });
            //Clear previous update promise
            $interval.cancel(updateMarkersPromise);

            //Start new update process
            updateMarkersPromise = $interval(updateMarkers, UPDATE_INTERVAL);

            for(var i=0;i < res.length; i++){
                  markers.push(res[i]);
                  content = '
'+JSON.stringify(res[i].name) || JSON.stringify(res[i].title);
                  content += '
lue lisää..';
                  content += '
'+ CalendarStart +'' + CalendarEnd;
                  content += '<button class="btn waves-effect waves-light" ng-click="addToCalendar();" name="action">Lisää kalenteriin</button>';
                  var compiled = $compile(content)($scope);

                  tag = Categories.getIcon(res[i].category);
                  var title = res[i].title;
                  // this is still random
                  traffic = 1;
                  // define activity class
                  var light = 'darkest';
                  if(traffic<25){
                    light = 'light';
                  }
                  else if(traffic<50){
                    light = 'medium';
                  }
                  else if(traffic<75){
                    light = 'dark';
                  }

                  // define label class
                  var className = "labels "+light;

                  // Get center
                  var coords = new google.maps.LatLng(
                    res[i].coordinates.latitude,
                    res[i].coordinates.longitude
                    );
                    // Set marker also
                    marker = new MarkerWithLabel({
                      position: coords,
                      map: $scope.map,
                      title: title,
                      icon: ' ',
                      labelContent: '',
                      labelAnchor: new google.maps.Point(18, 18),
                      labelClass: className,
                      content: compiled[0],
                      html: content
                    });
                    markerObj.push(marker);
                    animateCircle(i,traffic,500);

                    google.maps.event.addListener(marker, 'click', function() {
                      infowindow.setContent(this.content);
                      infowindow.open($scope.map,this);
                    });

              }
            }, function errorCallback(response) {
              $ionicLoading.hide();
          });
      };

      var updateMarkers = function(){
        var selectedTags = Categories.getActive();
        $http({
          method: 'GET',
          headers : {"content-type" : "application/json"},
          params: {category: selectedTags.join(',')},
          url: CORSURL+APIURL}).then(function successCallback(response) {
            var res = response.data;
            for(var i=0;i < res.length; i++){

                var traffic = Math.round(res[i].traffic.average*100 ) || 0;
                var light = 'darkest';
                  if(traffic<25){
                    light = 'light';
                  }else if(traffic<50){
                    light = 'medium';
                  } else if(traffic<75){
                    light = 'dark';
                  }

                var content = res[i].title +'
' +res[i].description;
                var tag = Categories.getIcon(res[i].category);

                var classes = "labels "+light;
                if(res[i].discounts[0]){
                    content = res[i].title +'
'+ (res[i].discounts[0].description||'')+'';
                    classes += ' has-sale';
                }
                var labelContent = '';

                // update content
                markerObj[i].labelContent = labelContent;
                markerObj[i].labelClass  = classes;
                animateCircle(i,traffic,500);

            }
          })
      }

      $scope.categories = Categories.all();

      var animateCircle = function(id, traffic,time){
         $timeout(function(){
          if(document.getElementById('circle_'+id)){
            document.getElementById('circle_'+id).style.strokeDashoffset = (100-traffic);
          }
         },time);
      }

      $scope.toggleCategory = function(id){
        Categories.toggle(id);
        searchLocations();
        updateActive();
      }

      var updateActive = function(){
        $scope.selectedCategories = Categories.getActive();
        console.log($scope.selectedCategories );
      }

      updateActive();
      searchLocations();

    });

<|endoftext|>


<commit_before>// JavaScript Document

/*
 * FileSender www.filesender.org
 *
 * Copyright (c) 2009-2012, AARNet, Belnet, HEAnet, SURFnet, UNINETT
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *	Redistributions of source code must retain the above copyright
 * 	notice, this list of conditions and the following disclaimer.
 * *	Redistributions in binary form must reproduce the above copyright
 * 	notice, this list of conditions and the following disclaimer in the
 * 	documentation and/or other materials provided with the distribution.
 * *	Neither the name of AARNet, Belnet, HEAnet, SURFnet and UNINETT nor the
 * 	names of its contributors may be used to endorse or promote products
 * 	derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

$(function() {
    var tables = $('table.transfers');
    if(!tables.length) return;

    tables.each(function() {
        var table = $(this);
        if(table.attr('data-processed')) return;
        table.attr('data-processed', '1');

        // Expand each transfer's details
        table.find('.transfer .expand span, .transfer span.expand').on('click', function() {
            var el = $(this);
            var tr = el.closest('tr');
            var details = table.find('.transfer_details[data-id="' + tr.attr('data-id') + '"]');

            tr.hide('fast');
            details.show('fast');
        });

        // Collapse each transfer's details
        table.find('.transfer_details .collapse span').on('click', function() {
            var el = $(this);
            var details = el.closest('tr');
            var tr = table.find('.transfer[data-id="' + details.attr('data-id') + '"]');

            details.hide('fast');
            tr.show('fast');
        });

        // Expand / retract all
        table.find('thead .expand span').on('click', function() {
            var el = $(this);
            var table = el.closest('table');

            var expanded = !el.hasClass('expanded');

            table.find('.transfer_details')[expanded ? 'show' : 'hide']('fast');
            table.find('.transfer')[expanded ? 'hide' : 'show']('fast');

            el.toggleClass('expanded', expanded).toggleClass('fa-plus-circle', !expanded).toggleClass('fa-minus-circle', expanded);
        });

        if(!table.is('[data-status="closed"]')) {
            // Setup action buttons
            table.find('tbody .actions').each(function() {
                var container = $(this);

                // Delete button
                $('').appendTo(container).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var id = $(this).closest('tr').attr('data-id');
                    if(!id || isNaN(id)) return;

                    if(table.is('[data-mode="admin"]')) {
                        var d = filesender.ui.chooseAction(['close_transfer', 'delete_transfer_nicely', 'delete_transfer_roughly'], function(choosen) {
                            var messages = {
                                close: 'transfer_closed',
                                delete_nicely: 'transfer_deleted',
                                delete: 'transfer_deleted'
                            };
                            var done = function() {
                                filesender.ui.alert('success', lang.tr(messages[choosen]), function() {
                                    table.find('[data-id="' + id + '"]').remove();
                                });
                            };

                            switch(choosen) {
                                case 'close' : filesender.client.closeTransfer(id, done); break;
                                case 'delete_nicely' : filesender.client.closeTransfer(id, function() {
                                    filesender.client.deleteTransfer(id, done);
                                }); break;
                                case 'delete' : filesender.client.deleteTransfer(id, done); break;
                            }
                        });
                    } else {
                        filesender.ui.confirm(lang.tr('confirm_close_transfer'), function() {
                            filesender.client.closeTransfer(id, function() {
                                filesender.ui.alert('success', lang.tr('transfer_closed'), function() {
                                    table.find('[data-id="' + id + '"]').remove();
                                });
                            });
                        });
                    }
                });

                if(table.is('[data-mode="user"]')) {
                    // Add recipient(s)
                    $('').appendTo(container).attr({
                        title: lang.tr('add_recipient')
                    }).on('click', function() {
                        var id = $(this).closest('tr').attr('data-id');
                        if(!id || isNaN(id)) return;

                        var recipients = [];
                        table.find('.transfer_details[data-id="' + id + '"] .recipients .recipient').each(function() {
                            recipients.push($(this).attr('data-email'));
                        });

                        var prompt = filesender.ui.prompt(lang.tr('enter_to_email'), function() {
                            var input = $(this).find('input');
                            $('p.error', this).remove();

                            var raw_emails = input.val().split(/[,;]/);

                            var emails = [];
                            var errors = [];

                            for(var i=0; i<raw_emails.length; i++) {
                                var email = raw_emails[i].replace(/^\s+/, '').replace(/\s+$/, '');
                                if(!email) continue;

                                if(!email.match(filesender.ui.validators.email)) {
                                    errors.push(lang.tr('invalid_recipient').r({email: email}));
                                    continue;
                                }

                                for(var j=0; j<recipients.length; j++) {
                                    if(recipients[j] == email) {
                                        errors.push(lang.tr('duplicate_recipient').r({email: email}));
                                        continue;
                                    }
                                }

                                for(var j=0; j<emails.length; j++) {
                                    if(emails[j] == email) {
                                        errors.push(lang.tr('duplicate_recipient').r({email: email}));
                                        continue;
                                    }
                                }

                                emails.push(email);
                            }

                            if(recipients.length + emails.length >= filesender.config.max_email_recipients)
                                errors.push(lang.tr('max_email_recipients_exceeded').r({max: filesender.config.max_email_recipients}));

                            if(errors.length) {
                                for(var i=0; i<errors.length; i++)
                                    $('
').text(errors[i].out()).appendTo(this);
                                return false;
                            }

                            alert(emails.join(', '));

                            return true;
                        })

                        prompt.append('
' + lang.tr('email_separator_msg') + '
');
                        prompt.append($('<input type="text" />'));
                    });

                    // Send reminder button
                    $('').appendTo(container).attr({
                        title: lang.tr('send_reminder')
                    }).on('click', function() {
                        var id = $(this).closest('tr').attr('data-id');
                        if(!id || isNaN(id)) return;

                        filesender.ui.confirm(lang.tr('confirm_remind_transfer'), function() {
                            // TODO
                        });
                    });
                }
            });

            // Setup buttons in details
            table.find('.transfer_details .recipient').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var rcpt = $(this).closest('.recipient');
                    var id = rcpt.attr('data-id');
                    var transfer_details = rcpt.closest('.transfer_details');
                    if(!id || isNaN(id)) return;

                    filesender.ui.confirm(lang.tr('confirm_delete_recipient'), function() {
                        filesender.client.deleteRecipient(id, function() {
                            filesender.ui.alert('success', lang.tr('recipient_deleted'), function() {
                                rcpt.remove();
                                if(!transfer_details.find('.recipients .recipient').length) {
                                    transfer_details.prev('.transfer').remove();
                                    transfer_details.remove();
                                }
                            });
                        });
                    });
                });
            });

            table.find('.transfer_details .recipient .errors').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('details')
                }).on('click', function() {
                    var rcpt = $(this).closest('.recipient');
                    var id = rcpt.attr('data-id');
                    if(!id || isNaN(id)) return;

                    filesender.client.getRecipient(id, function(recipient) {
                        var popup = filesender.ui.wideInfoPopup(lang.tr('recipient_errors'));

                        for(var i=0; i<recipient.errors.length; i++) {
                            var error = recipient.errors[i];

                            var node = $('
').appendTo(popup);

                            var type = $('
').appendTo(node);
                            $('').appendTo(type).text(lang.tr('error_type') + ' : ');
                            $('').appendTo(type).text(lang.tr('recipient_error_' + error.type));

                            var date = $('
').appendTo(node);
                            $('').appendTo(date).text(lang.tr('error_date') + ' : ');
                            $('').appendTo(date).text(error.date.formatted);

                            var details = $('
').appendTo(node);
                            $('').appendTo(details).text(lang.tr('error_details') + ' : ');
                            $('
').appendTo(details).text(error.details);
                        }
                    });
                });
            });

            table.find('.transfer_details .file').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var file = $(this).closest('.file');
                    var id = file.attr('data-id');
                    var transfer_details = file.closest('.transfer_details');
                    if(!id || isNaN(id)) return;

                    filesender.ui.confirm(lang.tr('confirm_delete_file'), function() {
                        filesender.client.deleteFile(id, function() {
                            filesender.ui.alert('success', lang.tr('file_deleted'), function() {
                                file.remove();
                                if(!transfer_details.find('.files .file').length) {
                                    transfer_details.prev('.transfer').remove();
                                    transfer_details.remove();
                                }
                            });
                        });
                    });
                });
            });
        }

        // Add auditlogs triggers
        var auditlogs = function(transfer_id, filter) {
            filesender.client.getTransferAuditlog(transfer_id, function(log) {
                var popup = filesender.ui.wideInfoPopup(lang.tr('auditlog'));

                if(!log || !log.length) {
                    $('
').text(lang.tr('no_auditlog')).appendTo(popup);
                    return;
                }

                var tbl = $('
').appendTo(popup);
                var th = $('
').appendTo($('
').appendTo(tbl));
                $('').text(lang.tr('date')).appendTo(th);
                $('').text(lang.tr('action')).appendTo(th);

                if(filter) {
                    filter = filter.split('/');
                    if(filter.length != 3) filter = null;
                }

                if(filter) {
                    var flt = $('
').text(lang.tr('filtered_transfer_log')).prependTo(popup);
                    $('').text(lang.tr('view_full_log')).appendTo(flt).on('click', function(e) {
                        e.stopPropagation();
                        e.preventDefault();
                        $(this).closest('.wide_info').find('table tr').show('fast');
                        $(this).closest('.filtered').hide('fast');
                        return false;
                    });
                }

                var tb = $('
').appendTo(tbl);
                for(var i=0; i<log.length; i++) {
                    var tr = $('
').appendTo(tb);

                    var filtered = false;
                    if(filter) {
                        var v = log[i][filter[0]];
                        if(v && v.type) {
                            if(v.type.toLowerCase() == filter[1]) {
                                if(v.id != filter[2]) filtered = true;
                            } else filtered = true;
                        }
                    }
                    if(filtered) tr.hide();

                    $('').text(log[i].date.formatted).appendTo(tr);

                    var lid = 'report_event_' + log[i].event;

                    var rpl = {author: log[i].author};
                    rpl[log[i].target.type.toLowerCase()] = log[i].target;

                    $('').html(lang.tr(lid).r(rpl).out()).appendTo(tr);

                }

                var actions = $('
').appendTo(popup);

                var send_by_email = $('').text(lang.tr('send_to_my_email')).appendTo(actions);
                $('').prependTo(send_by_email);
                send_by_email.on('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();

                    filesender.client.getTransferAuditlogByEmail(transfer_id, function() {
                        filesender.ui.alert('success', lang.tr('email_sent'));
                    });

                    return false;
                }).button();
            });
        };

        if(filesender.config.auditlog_lifetime !== null) {
            table.find('.transfer_details td').each(function() {
                var ld = $('
').appendTo(this);
                $('
').text(lang.tr('auditlog')).appendTo(ld);
                $('').text(lang.tr('open_auditlog')).appendTo(ld).on('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();
                    auditlogs($(this).closest('tr').attr('data-id'));
                    return false;
                }).prepend('').button();
            });

            table.find('.transfer_details .recipients .recipient').each(function() {
                $('').attr({
                    title: lang.tr('open_recipient_auditlog').out()
                }).appendTo(this).on('click', function() {
                    auditlogs($(this).closest('tr').attr('data-id'), 'author/recipient/' + $(this).closest('.recipient').attr('data-id'));
                });
            });

            table.find('.transfer_details .files .file').each(function() {
                $('').attr({
                    title: lang.tr('open_file_auditlog').out()
                }).appendTo(this).on('click', function() {
                    auditlogs($(this).closest('tr').attr('data-id'), 'target/file/' + $(this).closest('.file').attr('data-id'));
                });
            });
        }
    });
});
<commit_msg>Add transfer recipient addition capabilities to ui<commit_after>// JavaScript Document

/*
 * FileSender www.filesender.org
 *
 * Copyright (c) 2009-2012, AARNet, Belnet, HEAnet, SURFnet, UNINETT
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *	Redistributions of source code must retain the above copyright
 * 	notice, this list of conditions and the following disclaimer.
 * *	Redistributions in binary form must reproduce the above copyright
 * 	notice, this list of conditions and the following disclaimer in the
 * 	documentation and/or other materials provided with the distribution.
 * *	Neither the name of AARNet, Belnet, HEAnet, SURFnet and UNINETT nor the
 * 	names of its contributors may be used to endorse or promote products
 * 	derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

$(function() {
    var tables = $('table.transfers');
    if(!tables.length) return;

    tables.each(function() {
        var table = $(this);
        if(table.attr('data-processed')) return;
        table.attr('data-processed', '1');

        // Expand each transfer's details
        table.find('.transfer .expand span, .transfer span.expand').on('click', function() {
            var el = $(this);
            var tr = el.closest('tr');
            var details = table.find('.transfer_details[data-id="' + tr.attr('data-id') + '"]');

            tr.hide('fast');
            details.show('fast');
        });

        // Collapse each transfer's details
        table.find('.transfer_details .collapse span').on('click', function() {
            var el = $(this);
            var details = el.closest('tr');
            var tr = table.find('.transfer[data-id="' + details.attr('data-id') + '"]');

            details.hide('fast');
            tr.show('fast');
        });

        // Expand / retract all
        table.find('thead .expand span').on('click', function() {
            var el = $(this);
            var table = el.closest('table');

            var expanded = !el.hasClass('expanded');

            table.find('.transfer_details')[expanded ? 'show' : 'hide']('fast');
            table.find('.transfer')[expanded ? 'hide' : 'show']('fast');

            el.toggleClass('expanded', expanded).toggleClass('fa-plus-circle', !expanded).toggleClass('fa-minus-circle', expanded);
        });

        if(!table.is('[data-status="closed"]')) {
            // Setup action buttons
            table.find('tbody .actions').each(function() {
                var container = $(this);

                // Delete button
                $('').appendTo(container).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var id = $(this).closest('tr').attr('data-id');
                    if(!id || isNaN(id)) return;

                    if(table.is('[data-mode="admin"]')) {
                        var d = filesender.ui.chooseAction(['close_transfer', 'delete_transfer_nicely', 'delete_transfer_roughly'], function(choosen) {
                            var messages = {
                                close: 'transfer_closed',
                                delete_nicely: 'transfer_deleted',
                                delete: 'transfer_deleted'
                            };
                            var done = function() {
                                filesender.ui.alert('success', lang.tr(messages[choosen]), function() {
                                    table.find('[data-id="' + id + '"]').remove();
                                });
                            };

                            switch(choosen) {
                                case 'close' : filesender.client.closeTransfer(id, done); break;
                                case 'delete_nicely' : filesender.client.closeTransfer(id, function() {
                                    filesender.client.deleteTransfer(id, done);
                                }); break;
                                case 'delete' : filesender.client.deleteTransfer(id, done); break;
                            }
                        });
                    } else {
                        filesender.ui.confirm(lang.tr('confirm_close_transfer'), function() {
                            filesender.client.closeTransfer(id, function() {
                                filesender.ui.alert('success', lang.tr('transfer_closed'), function() {
                                    table.find('[data-id="' + id + '"]').remove();
                                });
                            });
                        });
                    }
                });

                if(table.is('[data-mode="user"]')) {
                    // Add recipient(s)
                    $('').appendTo(container).attr({
                        title: lang.tr('add_recipient')
                    }).on('click', function() {
                        var id = $(this).closest('tr').attr('data-id');
                        if(!id || isNaN(id)) return;

                        var recipients = [];
                        table.find('.transfer_details[data-id="' + id + '"] .recipients .recipient').each(function() {
                            recipients.push($(this).attr('data-email'));
                        });

                        var prompt = filesender.ui.prompt(lang.tr('enter_to_email'), function() {
                            var input = $(this).find('input');
                            $('p.error', this).remove();

                            var raw_emails = input.val().split(/[,;]/);

                            var emails = [];
                            var errors = [];

                            for(var i=0; i<raw_emails.length; i++) {
                                var email = raw_emails[i].replace(/^\s+/, '').replace(/\s+$/, '');
                                if(!email) continue;

                                if(!email.match(filesender.ui.validators.email)) {
                                    errors.push(lang.tr('invalid_recipient').r({email: email}));
                                    continue;
                                }

                                for(var j=0; j<recipients.length; j++) {
                                    if(recipients[j] == email) {
                                        errors.push(lang.tr('duplicate_recipient').r({email: email}));
                                        continue;
                                    }
                                }

                                for(var j=0; j<emails.length; j++) {
                                    if(emails[j] == email) {
                                        errors.push(lang.tr('duplicate_recipient').r({email: email}));
                                        continue;
                                    }
                                }

                                emails.push(email);
                            }

                            if(recipients.length + emails.length >= filesender.config.max_email_recipients)
                                errors.push(lang.tr('max_email_recipients_exceeded').r({max: filesender.config.max_email_recipients}));

                            if(errors.length) {
                                for(var i=0; i<errors.length; i++)
                                    $('
').text(errors[i].out()).appendTo(this);
                                return false;
                            }

                            var done = 0;
                            for(var i=0; i<emails.length; i++) {
                                filesender.client.addRecipient(id, emails[i], function() {
                                    done++;

                                    if(done < emails.length) return;

                                    filesender.ui.alert('success', lang.tr('recipient_added'), function() {
                                        filesender.ui.reload();
                                    });
                                });
                            }

                            return true;
                        })

                        prompt.append('
' + lang.tr('email_separator_msg') + '
');
                        var input = $('<input type="text" class="wide" />').appendTo(prompt);

                        // Autocomplete is buggy
                        /*input.autocomplete({
                            source: function (request, response) {
                                filesender.client.getFrequentRecipients(request.term,
                                    function (data) {
                                        response($.map(data, function (item) {
                                            if ($('.transfers .transfer_details[data-id="' + id + '"] .recipient[data-email="' + item + '"]').length == 0){
                                                return {
                                                    label: item,
                                                    value: item
                                                };
                                            }else{
                                                return undefined;
                                            }
                                        }))
                                    }
                                );
                            },
                            select: function (event, ui) {
                                input.val(input.val() + ', ' + ui.item.value);

                                return false;
                            },
                            minLength: filesender.config.minimum_characters_for_autocomplete
                        });*/
                    });

                    // Send reminder button
                    $('').appendTo(container).attr({
                        title: lang.tr('send_reminder')
                    }).on('click', function() {
                        var id = $(this).closest('tr').attr('data-id');
                        if(!id || isNaN(id)) return;

                        filesender.ui.confirm(lang.tr('confirm_remind_transfer'), function() {
                            // TODO
                        });
                    });
                }
            });

            // Setup buttons in details
            table.find('.transfer_details .recipient').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var rcpt = $(this).closest('.recipient');
                    var id = rcpt.attr('data-id');
                    var transfer_details = rcpt.closest('.transfer_details');
                    if(!id || isNaN(id)) return;

                    filesender.ui.confirm(lang.tr('confirm_delete_recipient'), function() {
                        filesender.client.deleteRecipient(id, function() {
                            filesender.ui.alert('success', lang.tr('recipient_deleted'), function() {
                                rcpt.remove();
                                if(!transfer_details.find('.recipients .recipient').length) {
                                    transfer_details.prev('.transfer').remove();
                                    transfer_details.remove();
                                }
                            });
                        });
                    });
                });
            });

            table.find('.transfer_details .recipient .errors').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('details')
                }).on('click', function() {
                    var rcpt = $(this).closest('.recipient');
                    var id = rcpt.attr('data-id');
                    if(!id || isNaN(id)) return;

                    filesender.client.getRecipient(id, function(recipient) {
                        var popup = filesender.ui.wideInfoPopup(lang.tr('recipient_errors'));

                        for(var i=0; i<recipient.errors.length; i++) {
                            var error = recipient.errors[i];

                            var node = $('
').appendTo(popup);

                            var type = $('
').appendTo(node);
                            $('').appendTo(type).text(lang.tr('error_type') + ' : ');
                            $('').appendTo(type).text(lang.tr('recipient_error_' + error.type));

                            var date = $('
').appendTo(node);
                            $('').appendTo(date).text(lang.tr('error_date') + ' : ');
                            $('').appendTo(date).text(error.date.formatted);

                            var details = $('
').appendTo(node);
                            $('').appendTo(details).text(lang.tr('error_details') + ' : ');
                            $('
').appendTo(details).text(error.details);
                        }
                    });
                });
            });

            table.find('.transfer_details .file').each(function() {
                $('').appendTo($(this)).attr({
                    title: lang.tr('delete')
                }).on('click', function() {
                    var file = $(this).closest('.file');
                    var id = file.attr('data-id');
                    var transfer_details = file.closest('.transfer_details');
                    if(!id || isNaN(id)) return;

                    filesender.ui.confirm(lang.tr('confirm_delete_file'), function() {
                        filesender.client.deleteFile(id, function() {
                            filesender.ui.alert('success', lang.tr('file_deleted'), function() {
                                file.remove();
                                if(!transfer_details.find('.files .file').length) {
                                    transfer_details.prev('.transfer').remove();
                                    transfer_details.remove();
                                }
                            });
                        });
                    });
                });
            });
        }

        // Add auditlogs triggers
        var auditlogs = function(transfer_id, filter) {
            filesender.client.getTransferAuditlog(transfer_id, function(log) {
                var popup = filesender.ui.wideInfoPopup(lang.tr('auditlog'));

                if(!log || !log.length) {
                    $('
').text(lang.tr('no_auditlog')).appendTo(popup);
                    return;
                }

                var tbl = $('
').appendTo(popup);
                var th = $('
').appendTo($('
').appendTo(tbl));
                $('').text(lang.tr('date')).appendTo(th);
                $('').text(lang.tr('action')).appendTo(th);

                if(filter) {
                    filter = filter.split('/');
                    if(filter.length != 3) filter = null;
                }

                if(filter) {
                    var flt = $('
').text(lang.tr('filtered_transfer_log')).prependTo(popup);
                    $('').text(lang.tr('view_full_log')).appendTo(flt).on('click', function(e) {
                        e.stopPropagation();
                        e.preventDefault();
                        $(this).closest('.wide_info').find('table tr').show('fast');
                        $(this).closest('.filtered').hide('fast');
                        return false;
                    });
                }

                var tb = $('
').appendTo(tbl);
                for(var i=0; i<log.length; i++) {
                    var tr = $('
').appendTo(tb);

                    var filtered = false;
                    if(filter) {
                        var v = log[i][filter[0]];
                        if(v && v.type) {
                            if(v.type.toLowerCase() == filter[1]) {
                                if(v.id != filter[2]) filtered = true;
                            } else filtered = true;
                        }
                    }
                    if(filtered) tr.hide();

                    $('').text(log[i].date.formatted).appendTo(tr);

                    var lid = 'report_event_' + log[i].event;

                    var rpl = {author: log[i].author};
                    rpl[log[i].target.type.toLowerCase()] = log[i].target;

                    $('').html(lang.tr(lid).r(rpl).out()).appendTo(tr);

                }

                var actions = $('
').appendTo(popup);

                var send_by_email = $('').text(lang.tr('send_to_my_email')).appendTo(actions);
                $('').prependTo(send_by_email);
                send_by_email.on('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();

                    filesender.client.getTransferAuditlogByEmail(transfer_id, function() {
                        filesender.ui.alert('success', lang.tr('email_sent'));
                    });

                    return false;
                }).button();
            });
        };

        if(filesender.config.auditlog_lifetime !== null) {
            table.find('.transfer_details td').each(function() {
                var ld = $('
').appendTo(this);
                $('
').text(lang.tr('auditlog')).appendTo(ld);
                $('').text(lang.tr('open_auditlog')).appendTo(ld).on('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();
                    auditlogs($(this).closest('tr').attr('data-id'));
                    return false;
                }).prepend('').button();
            });

            table.find('.transfer_details .recipients .recipient').each(function() {
                $('').attr({
                    title: lang.tr('open_recipient_auditlog').out()
                }).appendTo(this).on('click', function() {
                    auditlogs($(this).closest('tr').attr('data-id'), 'author/recipient/' + $(this).closest('.recipient').attr('data-id'));
                });
            });

            table.find('.transfer_details .files .file').each(function() {
                $('').attr({
                    title: lang.tr('open_file_auditlog').out()
                }).appendTo(this).on('click', function() {
                    auditlogs($(this).closest('tr').attr('data-id'), 'target/file/' + $(this).closest('.file').attr('data-id'));
                });
            });
        }
    });
});
<|endoftext|>


<commit_before>goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler');
goog.provide('ol.MapBrowserPointerEvent');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol');
goog.require('ol.Coordinate');
goog.require('ol.MapEvent');
goog.require('ol.Pixel');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');

/**
 * @constructor
 * @extends {ol.MapEvent}
 * @implements {oli.MapBrowserEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @param {?olx.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserEvent = function(type, map, browserEvent, opt_frameState) {

  goog.base(this, type, map, opt_frameState);

  /**
   * @const
   * @type {goog.events.BrowserEvent}
   */
  this.browserEvent = browserEvent;

  /**
   * @const
   * @type {Event}
   * @todo api
   */
  this.originalEvent = browserEvent.getBrowserEvent();

  /**
   * @type {ol.Coordinate}
   * @todo api
   */
  this.coordinate = map.getEventCoordinate(this.originalEvent);

  /**
   * @type {ol.Pixel}
   * @todo api
   */
  this.pixel = map.getEventPixel(this.originalEvent);

};
goog.inherits(ol.MapBrowserEvent, ol.MapEvent);

/**
 * Prevents the default browser action.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
 * @override
 * @todo api
 */
ol.MapBrowserEvent.prototype.preventDefault = function() {
  goog.base(this, 'preventDefault');
  this.browserEvent.preventDefault();
};

/**
 * Prevents further propagation of the current event.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
 * @override
 * @todo api
 */
ol.MapBrowserEvent.prototype.stopPropagation = function() {
  goog.base(this, 'stopPropagation');
  this.browserEvent.stopPropagation();
};

/**
 * @constructor
 * @extends {ol.MapBrowserEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @param {?olx.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_frameState) {

  goog.base(this, type, map, pointerEvent.browserEvent, opt_frameState);

  /**
   * @const
   * @type {ol.pointer.PointerEvent}
   */
  this.pointerEvent = pointerEvent;

};
goog.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent);

/**
 * @param {ol.Map} map The map with the viewport to listen to events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.MapBrowserEventHandler = function(map) {

  goog.base(this);

  /**
   * This is the element that we will listen to the real events on.
   * @type {ol.Map}
   * @private
   */
  this.map_ = map;

  /**
   * @type {number}
   * @private
   */
  this.clickTimeoutId_ = 0;

  /**
   * @type {boolean}
   * @private
   */
  this.dragged_ = false;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.dragListenerKeys_ = null;

  /**
   * @type {goog.events.Key}
   * @private
   */
  this.pointerdownListenerKey_ = null;

  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    /**
     * @type {goog.events.Key}
     * @private
     */
    this.ieDblclickListenerKey_ = null;
  }

  /**
   * @type {ol.pointer.PointerEvent}
   * @private
   */
  this.down_ = null;

  var element = this.map_.getViewport();

  /**
   * @type {number}
   * @private
   */
  this.activePointers_ = 0;

  /**
   * @type {Object.<number, boolean>}
   * @private
   */
  this.trackedTouches_ = {};

  /**
   * Event handler which generates pointer events for
   * the viewport element.
   *
   * @type {ol.pointer.PointerEventHandler}
   * @private
   */
  this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element);

  /**
   * Event handler which generates pointer events for
   * the document (used when dragging).
   *
   * @type {ol.pointer.PointerEventHandler}
   * @private
   */
  this.documentPointerEventHandler_ = null;

  this.pointerdownListenerKey_ = goog.events.listen(this.pointerEventHandler_,
      ol.pointer.EventType.POINTERDOWN,
      this.handlePointerDown_, false, this);

  this.relayedListenerKey_ = goog.events.listen(this.pointerEventHandler_,
      ol.pointer.EventType.POINTERMOVE,
      this.relayEvent_, false, this);

  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    /*
     * On legacy IE, double clicks do not produce two mousedown and
     * mouseup events. That is why a separate DBLCLICK event listener
     * is used.
     */
    this.ieDblclickListenerKey_ = goog.events.listen(element,
        goog.events.EventType.DBLCLICK,
        this.emulateClickLegacyIE_, false, this);
  }

};
goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);

/**
 * Get the last "down" type event. This will be set on pointerdown.
 * @return {ol.pointer.PointerEvent} The most recent "down" type event (or null
 * if none have occurred).
 */
ol.MapBrowserEventHandler.prototype.getDown = function() {
  return this.down_;
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.emulateClickLegacyIE_ =
    function(browserEvent) {
  var pointerEvent = this.pointerEventHandler_.wrapMouseEvent(
      ol.MapBrowserEvent.EventType.POINTERUP,
      browserEvent
      );
  this.emulateClick_(pointerEvent);
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
  var newEvent;
  newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.CLICK, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);
  if (this.clickTimeoutId_ !== 0) {
    // double-click
    goog.global.clearTimeout(this.clickTimeoutId_);
    this.clickTimeoutId_ = 0;
    newEvent = new ol.MapBrowserPointerEvent(
        ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);
  } else {
    // click
    this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
      this.clickTimeoutId_ = 0;
      var newEvent = new ol.MapBrowserPointerEvent(
          ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent);
      this.dispatchEvent(newEvent);
    }, this), 250);
  }
};

/**
 * Keeps track on how many pointers are currently active.
 *
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.updateActivePointers_ =
    function(pointerEvent) {
  var event = pointerEvent;

  if (event.type == ol.MapBrowserEvent.EventType.POINTERUP ||
      event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) {
    delete this.trackedTouches_[event.pointerId];
  } else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
    this.trackedTouches_[event.pointerId] = true;
  }
  this.activePointers_ = goog.object.getCount(this.trackedTouches_);
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
  this.updateActivePointers_(pointerEvent);
  var newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);

  goog.asserts.assert(this.activePointers_ >= 0);
  if (this.activePointers_ === 0) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
    goog.dispose(this.documentPointerEventHandler_);
    this.documentPointerEventHandler_ = null;
  }

  // We emulate click event on left mouse button click, touch contact, and pen
  // contact. isMouseActionButton returns true in these cases (evt.button is set
  // to 0).
  // See http://www.w3.org/TR/pointerevents/#button-states
  if (!this.dragged_ && this.isMouseActionButton_(pointerEvent)) {
    goog.asserts.assert(!goog.isNull(this.down_));
    this.emulateClick_(this.down_);
  }
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @return {boolean} If the left mouse button was pressed.
 * @private
 */
ol.MapBrowserEventHandler.prototype.isMouseActionButton_ =
    function(pointerEvent) {
  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    return pointerEvent.button == 1;
  } else {
    return pointerEvent.button === 0;
  }
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerDown_ =
    function(pointerEvent) {
  this.updateActivePointers_(pointerEvent);
  var newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);

  this.down_ = pointerEvent;
  this.dragged_ = false;

  if (goog.isNull(this.dragListenerKeys_)) {
    /* Set up a pointer event handler on the `document`,
     * which is required when the pointer is moved outside
     * the viewport when dragging.
     */
    this.documentPointerEventHandler_ =
        new ol.pointer.PointerEventHandler(document);

    this.dragListenerKeys_ = [
      goog.events.listen(this.documentPointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERMOVE,
          this.handlePointerMove_, false, this),
      goog.events.listen(this.documentPointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERUP,
          this.handlePointerUp_, false, this),
      /* Note that the listener for `pointercancel is set up on
       * `pointerEventHandler_` and not `documentPointerEventHandler_` like
       * the `pointerup` and `pointermove` listeners.
       *
       * The reason for this is the following: `TouchSource.vacuumTouches_()`
       * issues `pointercancel` events, when there was no `touchend` for a
       * `touchstart`. Now, let's say a first `touchstart` is registered on
       * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
       * But `documentPointerEventHandler_` doesn't know about the first
       * `touchstart`. If there is no `touchend` for the `touchstart`, we can
       * only receive a `touchcancel` from `pointerEventHandler_`, because it is
       * only registered there.
       */
      goog.events.listen(this.pointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERCANCEL,
          this.handlePointerUp_, false, this)
    ];
  }

  // FIXME check if/when this is necessary
  // prevent context menu
  pointerEvent.preventDefault();
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerMove_ =
    function(pointerEvent) {
  // Fix IE10 on windows Surface : When you tap the tablet, it triggers
  // multiple pointermove events between pointerdown and pointerup with
  // the exact same coordinates of the pointerdown event. To avoid a
  // 'false' touchmove event to be dispatched , we test if the pointer
  // effectively moved.
  if (pointerEvent.clientX != this.down_.clientX ||
      pointerEvent.clientY != this.down_.clientY) {
    this.dragged_ = true;
    var newEvent = new ol.MapBrowserPointerEvent(
        ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);
  }

  // Some native android browser triggers mousemove events during small period
  // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
  // https://code.google.com/p/android/issues/detail?id=19827
  // ex: Galaxy Tab P3110 + Android 4.1.1
  pointerEvent.preventDefault();
};

/**
 * Wrap and relay a pointer event.  Note that this requires that the type
 * string for the MapBrowserPointerEvent matches the PointerEvent type.
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
  this.dispatchEvent(new ol.MapBrowserPointerEvent(
      pointerEvent.type, this.map_, pointerEvent));
};

/**
 * @inheritDoc
 */
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
  if (!goog.isNull(this.relayedListenerKey_)) {
    goog.events.unlistenByKey(this.relayedListenerKey_);
    this.relayedListenerKey_ = null;
  }
  if (!goog.isNull(this.pointerdownListenerKey_)) {
    goog.events.unlistenByKey(this.pointerdownListenerKey_);
    this.pointerdownListenerKey_ = null;
  }
  if (!goog.isNull(this.dragListenerKeys_)) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
  }
  if (!goog.isNull(this.documentPointerEventHandler_)) {
    goog.dispose(this.documentPointerEventHandler_);
    this.documentPointerEventHandler_ = null;
  }
  if (!goog.isNull(this.pointerEventHandler_)) {
    goog.dispose(this.pointerEventHandler_);
    this.pointerEventHandler_ = null;
  }
  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE &&
      !goog.isNull(this.ieDblclickListenerKey_)) {
    goog.events.unlistenByKey(this.ieDblclickListenerKey_);
    this.ieDblclickListenerKey_ = null;
  }
  goog.base(this, 'disposeInternal');
};

/**
 * Constants for event names.
 * @enum {string}
 */
ol.MapBrowserEvent.EventType = {
  // derived event types
  /**
   * A true single click with no dragging and no double click. Note that this
   * event is delayed by 250 ms to ensure that it is not a double click.
   * @event ol.MapBrowserEvent#singleclick
   * @todo api
   */
  SINGLECLICK: 'singleclick',
  /**
   * A click with no dragging. A double click will fire two of this.
   * @event ol.MapBrowserEvent#click
   * @todo api
   */
  CLICK: goog.events.EventType.CLICK,
  /**
   * A true double click, with no dragging.
   * @event ol.MapBrowserEvent#dblclick
   * @todo api
   */
  DBLCLICK: goog.events.EventType.DBLCLICK,
  /**
   * Triggered when a pointer is dragged.
   * @event ol.MapBrowserEvent#pointerdrag
   * @todo api
   */
  POINTERDRAG: 'pointerdrag',

  // original pointer event types
  /**
   * Triggered when a pointer is moved.
   * @event ol.MapBrowserEvent#pointermove
   * @todo api
   */
  POINTERMOVE: 'pointermove',
  POINTERDOWN: 'pointerdown',
  POINTERUP: 'pointerup',
  POINTEROVER: 'pointerover',
  POINTEROUT: 'pointerout',
  POINTERENTER: 'pointerenter',
  POINTERLEAVE: 'pointerleave',
  POINTERCANCEL: 'pointercancel'
};
<commit_msg>Document that panning triggers pointermove on touch<commit_after>goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler');
goog.provide('ol.MapBrowserPointerEvent');

goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('ol');
goog.require('ol.Coordinate');
goog.require('ol.MapEvent');
goog.require('ol.Pixel');
goog.require('ol.pointer.PointerEvent');
goog.require('ol.pointer.PointerEventHandler');

/**
 * @constructor
 * @extends {ol.MapEvent}
 * @implements {oli.MapBrowserEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {goog.events.BrowserEvent} browserEvent Browser event.
 * @param {?olx.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserEvent = function(type, map, browserEvent, opt_frameState) {

  goog.base(this, type, map, opt_frameState);

  /**
   * @const
   * @type {goog.events.BrowserEvent}
   */
  this.browserEvent = browserEvent;

  /**
   * @const
   * @type {Event}
   * @todo api
   */
  this.originalEvent = browserEvent.getBrowserEvent();

  /**
   * @type {ol.Coordinate}
   * @todo api
   */
  this.coordinate = map.getEventCoordinate(this.originalEvent);

  /**
   * @type {ol.Pixel}
   * @todo api
   */
  this.pixel = map.getEventPixel(this.originalEvent);

};
goog.inherits(ol.MapBrowserEvent, ol.MapEvent);

/**
 * Prevents the default browser action.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault
 * @override
 * @todo api
 */
ol.MapBrowserEvent.prototype.preventDefault = function() {
  goog.base(this, 'preventDefault');
  this.browserEvent.preventDefault();
};

/**
 * Prevents further propagation of the current event.
 * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation
 * @override
 * @todo api
 */
ol.MapBrowserEvent.prototype.stopPropagation = function() {
  goog.base(this, 'stopPropagation');
  this.browserEvent.stopPropagation();
};

/**
 * @constructor
 * @extends {ol.MapBrowserEvent}
 * @param {string} type Event type.
 * @param {ol.Map} map Map.
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @param {?olx.FrameState=} opt_frameState Frame state.
 */
ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_frameState) {

  goog.base(this, type, map, pointerEvent.browserEvent, opt_frameState);

  /**
   * @const
   * @type {ol.pointer.PointerEvent}
   */
  this.pointerEvent = pointerEvent;

};
goog.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent);

/**
 * @param {ol.Map} map The map with the viewport to listen to events on.
 * @constructor
 * @extends {goog.events.EventTarget}
 */
ol.MapBrowserEventHandler = function(map) {

  goog.base(this);

  /**
   * This is the element that we will listen to the real events on.
   * @type {ol.Map}
   * @private
   */
  this.map_ = map;

  /**
   * @type {number}
   * @private
   */
  this.clickTimeoutId_ = 0;

  /**
   * @type {boolean}
   * @private
   */
  this.dragged_ = false;

  /**
   * @type {Array.<number>}
   * @private
   */
  this.dragListenerKeys_ = null;

  /**
   * @type {goog.events.Key}
   * @private
   */
  this.pointerdownListenerKey_ = null;

  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    /**
     * @type {goog.events.Key}
     * @private
     */
    this.ieDblclickListenerKey_ = null;
  }

  /**
   * @type {ol.pointer.PointerEvent}
   * @private
   */
  this.down_ = null;

  var element = this.map_.getViewport();

  /**
   * @type {number}
   * @private
   */
  this.activePointers_ = 0;

  /**
   * @type {Object.<number, boolean>}
   * @private
   */
  this.trackedTouches_ = {};

  /**
   * Event handler which generates pointer events for
   * the viewport element.
   *
   * @type {ol.pointer.PointerEventHandler}
   * @private
   */
  this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element);

  /**
   * Event handler which generates pointer events for
   * the document (used when dragging).
   *
   * @type {ol.pointer.PointerEventHandler}
   * @private
   */
  this.documentPointerEventHandler_ = null;

  this.pointerdownListenerKey_ = goog.events.listen(this.pointerEventHandler_,
      ol.pointer.EventType.POINTERDOWN,
      this.handlePointerDown_, false, this);

  this.relayedListenerKey_ = goog.events.listen(this.pointerEventHandler_,
      ol.pointer.EventType.POINTERMOVE,
      this.relayEvent_, false, this);

  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    /*
     * On legacy IE, double clicks do not produce two mousedown and
     * mouseup events. That is why a separate DBLCLICK event listener
     * is used.
     */
    this.ieDblclickListenerKey_ = goog.events.listen(element,
        goog.events.EventType.DBLCLICK,
        this.emulateClickLegacyIE_, false, this);
  }

};
goog.inherits(ol.MapBrowserEventHandler, goog.events.EventTarget);

/**
 * Get the last "down" type event. This will be set on pointerdown.
 * @return {ol.pointer.PointerEvent} The most recent "down" type event (or null
 * if none have occurred).
 */
ol.MapBrowserEventHandler.prototype.getDown = function() {
  return this.down_;
};

/**
 * @param {goog.events.BrowserEvent} browserEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.emulateClickLegacyIE_ =
    function(browserEvent) {
  var pointerEvent = this.pointerEventHandler_.wrapMouseEvent(
      ol.MapBrowserEvent.EventType.POINTERUP,
      browserEvent
      );
  this.emulateClick_(pointerEvent);
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) {
  var newEvent;
  newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.CLICK, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);
  if (this.clickTimeoutId_ !== 0) {
    // double-click
    goog.global.clearTimeout(this.clickTimeoutId_);
    this.clickTimeoutId_ = 0;
    newEvent = new ol.MapBrowserPointerEvent(
        ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);
  } else {
    // click
    this.clickTimeoutId_ = goog.global.setTimeout(goog.bind(function() {
      this.clickTimeoutId_ = 0;
      var newEvent = new ol.MapBrowserPointerEvent(
          ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent);
      this.dispatchEvent(newEvent);
    }, this), 250);
  }
};

/**
 * Keeps track on how many pointers are currently active.
 *
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.updateActivePointers_ =
    function(pointerEvent) {
  var event = pointerEvent;

  if (event.type == ol.MapBrowserEvent.EventType.POINTERUP ||
      event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) {
    delete this.trackedTouches_[event.pointerId];
  } else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) {
    this.trackedTouches_[event.pointerId] = true;
  }
  this.activePointers_ = goog.object.getCount(this.trackedTouches_);
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) {
  this.updateActivePointers_(pointerEvent);
  var newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);

  goog.asserts.assert(this.activePointers_ >= 0);
  if (this.activePointers_ === 0) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
    goog.dispose(this.documentPointerEventHandler_);
    this.documentPointerEventHandler_ = null;
  }

  // We emulate click event on left mouse button click, touch contact, and pen
  // contact. isMouseActionButton returns true in these cases (evt.button is set
  // to 0).
  // See http://www.w3.org/TR/pointerevents/#button-states
  if (!this.dragged_ && this.isMouseActionButton_(pointerEvent)) {
    goog.asserts.assert(!goog.isNull(this.down_));
    this.emulateClick_(this.down_);
  }
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @return {boolean} If the left mouse button was pressed.
 * @private
 */
ol.MapBrowserEventHandler.prototype.isMouseActionButton_ =
    function(pointerEvent) {
  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE) {
    return pointerEvent.button == 1;
  } else {
    return pointerEvent.button === 0;
  }
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerDown_ =
    function(pointerEvent) {
  this.updateActivePointers_(pointerEvent);
  var newEvent = new ol.MapBrowserPointerEvent(
      ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent);
  this.dispatchEvent(newEvent);

  this.down_ = pointerEvent;
  this.dragged_ = false;

  if (goog.isNull(this.dragListenerKeys_)) {
    /* Set up a pointer event handler on the `document`,
     * which is required when the pointer is moved outside
     * the viewport when dragging.
     */
    this.documentPointerEventHandler_ =
        new ol.pointer.PointerEventHandler(document);

    this.dragListenerKeys_ = [
      goog.events.listen(this.documentPointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERMOVE,
          this.handlePointerMove_, false, this),
      goog.events.listen(this.documentPointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERUP,
          this.handlePointerUp_, false, this),
      /* Note that the listener for `pointercancel is set up on
       * `pointerEventHandler_` and not `documentPointerEventHandler_` like
       * the `pointerup` and `pointermove` listeners.
       *
       * The reason for this is the following: `TouchSource.vacuumTouches_()`
       * issues `pointercancel` events, when there was no `touchend` for a
       * `touchstart`. Now, let's say a first `touchstart` is registered on
       * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up.
       * But `documentPointerEventHandler_` doesn't know about the first
       * `touchstart`. If there is no `touchend` for the `touchstart`, we can
       * only receive a `touchcancel` from `pointerEventHandler_`, because it is
       * only registered there.
       */
      goog.events.listen(this.pointerEventHandler_,
          ol.MapBrowserEvent.EventType.POINTERCANCEL,
          this.handlePointerUp_, false, this)
    ];
  }

  // FIXME check if/when this is necessary
  // prevent context menu
  pointerEvent.preventDefault();
};

/**
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.handlePointerMove_ =
    function(pointerEvent) {
  // Fix IE10 on windows Surface : When you tap the tablet, it triggers
  // multiple pointermove events between pointerdown and pointerup with
  // the exact same coordinates of the pointerdown event. To avoid a
  // 'false' touchmove event to be dispatched , we test if the pointer
  // effectively moved.
  if (pointerEvent.clientX != this.down_.clientX ||
      pointerEvent.clientY != this.down_.clientY) {
    this.dragged_ = true;
    var newEvent = new ol.MapBrowserPointerEvent(
        ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent);
    this.dispatchEvent(newEvent);
  }

  // Some native android browser triggers mousemove events during small period
  // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or
  // https://code.google.com/p/android/issues/detail?id=19827
  // ex: Galaxy Tab P3110 + Android 4.1.1
  pointerEvent.preventDefault();
};

/**
 * Wrap and relay a pointer event.  Note that this requires that the type
 * string for the MapBrowserPointerEvent matches the PointerEvent type.
 * @param {ol.pointer.PointerEvent} pointerEvent Pointer event.
 * @private
 */
ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) {
  this.dispatchEvent(new ol.MapBrowserPointerEvent(
      pointerEvent.type, this.map_, pointerEvent));
};

/**
 * @inheritDoc
 */
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
  if (!goog.isNull(this.relayedListenerKey_)) {
    goog.events.unlistenByKey(this.relayedListenerKey_);
    this.relayedListenerKey_ = null;
  }
  if (!goog.isNull(this.pointerdownListenerKey_)) {
    goog.events.unlistenByKey(this.pointerdownListenerKey_);
    this.pointerdownListenerKey_ = null;
  }
  if (!goog.isNull(this.dragListenerKeys_)) {
    goog.array.forEach(this.dragListenerKeys_, goog.events.unlistenByKey);
    this.dragListenerKeys_ = null;
  }
  if (!goog.isNull(this.documentPointerEventHandler_)) {
    goog.dispose(this.documentPointerEventHandler_);
    this.documentPointerEventHandler_ = null;
  }
  if (!goog.isNull(this.pointerEventHandler_)) {
    goog.dispose(this.pointerEventHandler_);
    this.pointerEventHandler_ = null;
  }
  if (ol.LEGACY_IE_SUPPORT && ol.IS_LEGACY_IE &&
      !goog.isNull(this.ieDblclickListenerKey_)) {
    goog.events.unlistenByKey(this.ieDblclickListenerKey_);
    this.ieDblclickListenerKey_ = null;
  }
  goog.base(this, 'disposeInternal');
};

/**
 * Constants for event names.
 * @enum {string}
 */
ol.MapBrowserEvent.EventType = {
  // derived event types
  /**
   * A true single click with no dragging and no double click. Note that this
   * event is delayed by 250 ms to ensure that it is not a double click.
   * @event ol.MapBrowserEvent#singleclick
   * @todo api
   */
  SINGLECLICK: 'singleclick',
  /**
   * A click with no dragging. A double click will fire two of this.
   * @event ol.MapBrowserEvent#click
   * @todo api
   */
  CLICK: goog.events.EventType.CLICK,
  /**
   * A true double click, with no dragging.
   * @event ol.MapBrowserEvent#dblclick
   * @todo api
   */
  DBLCLICK: goog.events.EventType.DBLCLICK,
  /**
   * Triggered when a pointer is dragged.
   * @event ol.MapBrowserEvent#pointerdrag
   * @todo api
   */
  POINTERDRAG: 'pointerdrag',

  // original pointer event types
  /**
   * Triggered when a pointer is moved. Note that on touch devices this is
   * triggered when the map is panned, so is not the same as mousemove.
   * @event ol.MapBrowserEvent#pointermove
   * @todo api
   */
  POINTERMOVE: 'pointermove',
  POINTERDOWN: 'pointerdown',
  POINTERUP: 'pointerup',
  POINTEROVER: 'pointerover',
  POINTEROUT: 'pointerout',
  POINTERENTER: 'pointerenter',
  POINTERLEAVE: 'pointerleave',
  POINTERCANCEL: 'pointercancel'
};
<|endoftext|>


<commit_before>/**
 * @memberOf module:rg-async
 * @author Rúben Gomes <gomesruben21@gmail.com> (http://rubengomes10.github.io/)
 * @classdesc Defines a Map class. Provides two methods (map that will run in parallel and mapSeries that will run in series).
 */
class Map {

    /**
     * Invokes in parallel an async mapper function on each item in the given source Array.
     * This will return a promise to be resolved containing the new array with the mapped/transformed items.
     * @method module:rg-async.Map.map
     * @static
     *
     * @param  {Array} srcArray     Specifies the source array to map over.
     * @param  {Function} mapper    Specifies the async function that will be called with (value, index, array) as arguments and returns the new mapped value.
     * @return {Promise}            Returns a promise to be resolved containing the new array with the mapped/transformed items.
     */
    static map(srcArray, mapper){
        return Promise.all(srcArray.map(mapper));
    }

    static mapSeries(srcArray, mapper){}
}

module.exports = Map;<commit_msg>Rollback commit.<commit_after>const _ = require('lodash');

/**
 * @memberOf module:rg-async
 * @author Rúben Gomes <gomesruben21@gmail.com> (http://rubengomes10.github.io/)
 * @classdesc Defines a Map class. Provides two methods (map that will run in parallel and mapSeries that will run in series).
 */
class Map {

    /**
     * Invokes in parallel an async mapper function on each item in the given source Array.
     * This will return a promise to be resolved containing the new array with the mapped/transformed items.
     * @method module:rg-async.Map.map
     * @static
     *
     * @param  {Array} srcArray     Specifies the source array to map over.
     * @param  {Function} mapper    Specifies the async function that will be called with (value, index, array) as arguments and returns the new mapped value.
     * @return {Promise}            Returns a promise to be resolved containing the new array with the mapped/transformed items.
     */
    static map(srcArray, mapper){
        return Promise.all(srcArray.map(mapper));
    }

    static mapSeries(srcArray, mapper){}
}

module.exports = Map;<|endoftext|>


<commit_before>(function () {
    "use strict";

    angular
        .module('VinculacionApp')
        .controller('LoginController', LoginController);

    LoginController.$inject = ['$rootScope', '$location', 'authentication', 'toaster'];

    function LoginController ($rootScope, $location, authentication, toaster) {
        var vm = this;

        vm.username = "";
        vm.password = "";
        vm.login = login;
        vm.loading = false;

        authentication.ClearCredentials();

        function login() {
            vm.loading = true;

            authentication.Login( vm.username, vm.password, LoginSuccess, LoginFail);
        }

        function LoginSuccess(response) {
            authentication.SetCredentials(response.data);
            $location.path('/home');

            window.localStorage['Session'] =
            $rootScope.Session =
            JSON.parse(response.config.data).User;

            vm.loading = false;
        }

        function LoginFail(response) {
            console.log(response);
        /**
         *  @todo Cambiar este switch por un toaster que mande el error del server.
         */
            switch(response.statusText) {
                case "Unauthorized":
                    toaster.pop(
                        {
                                type: 'warning',
                                title: 'Falla autorizacion',
                                body: 'La cuenta ingresada no tiene privilegios de acceso'
                        }
                    );
                    break;

                default:
                    toaster.pop(
                        {
                            type: 'error',
                            title: 'Error',
                            body: 'Se ha producido un error! Lamentamos los inconvenientes.'
                        }
                );
            }
            vm.loading = false;
        }
    }
})();<commit_msg>Update Role on login<commit_after>(function () {
    "use strict";

    angular
        .module('VinculacionApp')
        .controller('LoginController', LoginController);

    LoginController.$inject = ['$rootScope', '$location', 'authentication',
                                'role', 'toaster'];

    function LoginController ($rootScope, $location, authentication, role, toaster) {
        var vm = this;

        vm.username = "";
        vm.password = "";
        vm.login = login;
        vm.loading = false;

        authentication.ClearCredentials();

        function login() {
            vm.loading = true;

            authentication.Login( vm.username, vm.password, LoginSuccess, LoginFail);
        }

        function LoginSuccess(response) {
            authentication.SetCredentials(response.data);
            $location.path('/home');

            window.localStorage['Session'] =
            $rootScope.Session =
            JSON.parse(response.config.data).User;

            role.get($rootScope.Session, getRoleSuccess);

            vm.loading = false;
        }

        function getRoleSuccess (response) {
            console.log(response.data);
            // window.localStorage['Role'] =
            // $rootScope.Role =
            // JSON.parse(response.data);
        }

        function LoginFail(response) {
            console.log(response);
        /**
         *  @todo Cambiar este switch por un toaster que mande el error del server.
         */
            switch(response.statusText) {
                case "Unauthorized":
                    toaster.pop(
                        {
                                type: 'warning',
                                title: 'Falla autorizacion',
                                body: 'La cuenta ingresada no tiene privilegios de acceso'
                        }
                    );
                    break;

                default:
                    toaster.pop(
                        {
                            type: 'error',
                            title: 'Error',
                            body: 'Se ha producido un error! Lamentamos los inconvenientes.'
                        }
                );
            }
            vm.loading = false;
        }
    }
})();<|endoftext|>


<commit_before>module.exports = {
    core: {
        type:{
            Fabric: 'fabrics',
            Textile: 'textiles',
            Accessories: 'accessories',
            Buyer: 'buyers',
            Supplier: 'suppliers',
            UoM: 'unit-of-measurement',
            Sparepart:'sparepart',
            GeneralMerchandise:'general-merchandise'
        },
        collection:{
            Buyer: 'buyers',
            Supplier: 'suppliers',
            UoM: 'unit-of-measurement',
            Product: 'product'
        }
    },
    po:{
        type:{
            POGarmentJobOrderFabric: 'po-garment-job-order-fabric',
            POGarmentJobOrderAccessories: 'po-garment-job-order-accessories',
            POGarmentMasterFabric: 'po-garment-master-fabric',
            POGarmentMasterAccessories: 'po-garment-master-accessories',
            POGarmentSparepart: 'po-garment-sparepart',
            POGarmentGeneral: 'po-garment-general',
            POTextileSparepart: 'po-textile-sparepart',
            POTextileGeneralATK: 'po-textile-general-atk',
            POTextileGeneralOtherATK: 'po-textile-general-other-atk',
            POTextileJobOrderExternal: 'po-textile-job-order-external'
        },
        collection:{
            PurchaseOrder:'purchase-order',
            PurchaseOrderGroup:'purchase-order-groups'
        }
    }
}
<commit_msg>Update map.js<commit_after>module.exports = {
    core: {
        type:{
            Fabric: 'fabrics',
            Textile: 'textiles',
            Accessories: 'accessories',
            Buyer: 'buyers',
            Supplier: 'suppliers',
            UoM: 'unit-of-measurements',
            Sparepart:'spareparts',
            GeneralMerchandise:'general-merchandises'
        },
        collection:{
            Buyer: 'buyers',
            Supplier: 'suppliers',
            UoM: 'unit-of-measurements',
            Product: 'products'
        }
    },
    po:{
        type:{
            POGarmentJobOrderFabric: 'po-garment-job-order-fabric',
            POGarmentJobOrderAccessories: 'po-garment-job-order-accessories',
            POGarmentMasterFabric: 'po-garment-master-fabric',
            POGarmentMasterAccessories: 'po-garment-master-accessories',
            POGarmentSparepart: 'po-garment-sparepart',
            POGarmentGeneral: 'po-garment-general',
            POTextileSparepart: 'po-textile-sparepart',
            POTextileGeneralATK: 'po-textile-general-atk',
            POTextileGeneralOtherATK: 'po-textile-general-other-atk',
            POTextileJobOrderExternal: 'po-textile-job-order-external'
        },
        collection:{
            PurchaseOrder:'purchase-orders',
            PurchaseOrderGroup:'purchase-order-groups'
        }
    }
}
<|endoftext|>