<commit_before>const SparqlClient = require('sparql-client')
const $ = require('jquery')
const magnificPopup = require('magnific-popup')

// <-------------- Variables ---------------->
var endpoint = 'http://dbpedia.org/sparql'
var client = new SparqlClient(endpoint)
var sights = []
var markers = []
var infoWindows = []
var centerLat, centerLng

// <------------- onload ----------------------->
$(document).ready(function(){

  // sets the lat and lng of the current user
  getLocation()

  $('#sightlist').on('click', 'li', function() {
    showDetails(this.dataset.id)
  })
})

// eventhandler for input field
  $( 'form' ).submit(function( event ) {
    var city = $('#inputfield').val()
    city = city.charAt(0).toUpperCase() + city.slice(1)
    $('#cityname').html(city)
    $('#inputfield').val('')
    $('#cityerror').html('')
    $('#sightlabel').html('')
    $('#sightabstract').html('')
    $('#sightthumb').attr("src", '')
    getSightsByCity(city)
    event.preventDefault()
  })

// view results
  function showResults(object) {
    $('#sightlist').html("")

    if(sights.length == 0) {
      $('#cityerror').html("We are terribly sorry, we were not able to find any sights matching your city. ")
    }
    else {
      for (var i = 0; i < sights.length; i++) {
        var val = sights[i].Label.value
        $('#sightlist').append('
' + val + '
')
      }
    }
    // add popup functionality after li are appended
    $('.detail-popup').magnificPopup({
      type:'inline',
      midClick: true // Allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source in href.
    })
  }

// show details per sight
  function showDetails(id) {
    $('#sightlabel').html(sights[id].Label.value)
    $('#sightabstract').html(sights[id].Abstract.value)
    $('#sightthumb').attr("src", sights[id].Thumbnail.value)
  }

  // <------------ User Location --------------->
  function getLocation() {
    if (navigator.geolocation) {
        return navigator.geolocation.getCurrentPosition(setCoords);
    } else {
      console.log("Geolocation is not supported by this browser.")
    }
  }

  function setCoords(position) {
    centerLat = position.coords.latitude
    centerLng = position.coords.longitude

    // centers the map at the current position of the user
    map.setCenter({lat: centerLat, lng: centerLng})
    map.addListener('click', function() {
      closeInfowindows()
    })
  }

  function addMarkers() {
    clearMarkers()
    for (var i = 0; i < sights.length; i++) {
      addDelayedMarkers(sights[i], i * 50)
    }
  }

  function clearMarkers() {
    for (var i = 0; i < markers.length; i++) {
      markers[i].setMap(null)
    }
  }

  function closeInfowindows() {
    for (var i = 0; i < infoWindows.length; i++) {
      infoWindows[i].close()
    }
  }

  function addDelayedMarkers(sight, delay) {
    var string = ''
    if(sight.Thumbnail.value != '') string += ''
    string += '
' + sight.Label.value + '
'

    window.setTimeout(function() {
      var marker = new google.maps.Marker({
        position: {lat: parseFloat(sight.Lat.value), lng: parseFloat(sight.Long.value)},
        map: map,
        title: sight.Label.value,
        animation: google.maps.Animation.DROP
      })
      marker.info = new google.maps.InfoWindow({
        content: string
      })
      infoWindows.push(marker.info)
      google.maps.event.addListener(marker, 'click', function() {
        closeInfowindows()
        marker.info.open(map, marker)
      })
      markers.push(marker)
    }, delay)
  }

  // runs a query against the DB with given params
  function queryDB(query) {
    client.query(query, function (error, results) {
      if (error) {
        console.log(error)
      } else {
        console.log(results.results.bindings)
        sights = results.results.bindings
        showResults()
        $('html, body').stop().animate({
          scrollTop: $(document).height()
        }, 1500)
        addMarkers()
        var mean = getLocationMean()
        console.log("lat: " + mean.lat + " long: " + mean.long);
        map.setCenter({lat: mean.lat, lng: mean.long})
      }
    })
  }

  function getLocationMean() {
    var mean = {
      lat: 0,
      long: 0
    }

    for (var i = 0; i < sights.length; i++) {
      mean.lat += parseFloat(sights[i].Lat.value)
      mean.long += parseFloat(sights[i].Long.value)
    }

    mean.lat = mean.lat / sights.length
    mean.long = mean.long / sights.length
    return mean
  }

  function stringify(city) {
    var str = city.split(' ')

    for (var i = 0; i < str.length; i++) {
      str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1)
    }

    return str.join('_')
  }

  function getSightsInProximity() {
    var query = ""

    queryDB(query)
  }

  function getSightsByCity(city) {
    city = stringify(city)

    var query = "PREFIX dcterms:  <http://purl.org/dc/terms/>\
                  PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\
                  SELECT DISTINCT (str(?label) as ?Label) (str(?lat) as ?Lat) (str(?long) as ?Long) (str(?thumbnail) as ?Thumbnail) (?abstract as ?Abstract)\
                  WHERE { \
                    ?sight dct:subject <http://dbpedia.org/resource/Category:Visitor_attractions_in_" + city + "> .\
                  FILTER langMatches(lang(?label), 'en').\
                  FILTER langMatches(lang(?abstract), 'en').\
                    ?sight rdfs:label ?label .\
                    ?sight geo:lat ?lat .\
                    ?sight geo:long ?long .\
                    ?sight dbo:thumbnail ?thumbnail .\
                    ?sight dbo:abstract ?abstract \
                  }\
                  ORDER BY ASC(?Label)"
    queryDB(query)
  }
<commit_msg>feat: loading architectal structures into map on movement<commit_after>const SparqlClient = require('sparql-client')
const $ = require('jquery')
const magnificPopup = require('magnific-popup')

// <-------------- Variables ---------------->
var endpoint = 'http://dbpedia.org/sparql'
var client = new SparqlClient(endpoint)
var sights = []
var markers = []
var infoWindows = []
var centerLat, centerLng
var buildingsOverall = {}

// <------------- onload ----------------------->
$(document).ready(function(){

  // sets the lat and lng of the current user

  $('#sightlist').on('click', 'li', function() {
    showDetails(this.dataset.id)
  })
})

window.map.addListener("idle",function(){
  var bounds = window.map.getBounds();
  var top = bounds.H.j
  var left = bounds.j.j
  var bottom = bounds.H.H
  var right = bounds.j.H

//Get radius from coordinate data of Map Borders
  var radius=(Math.max(Math.abs(top-bottom),Math.abs(right-left)))

  getSightsInProximity(window.map.getCenter().lng(), window.map.getCenter().lat(), radius)
})

// eventhandler for input field
  $( 'form' ).submit(function( event ) {
    var city = $('#inputfield').val()
    city = city.charAt(0).toUpperCase() + city.slice(1)
    $('#cityname').html(city)
    $('#inputfield').val('')
    $('#cityerror').html('')
    $('#sightlabel').html('')
    $('#sightabstract').html('')
    $('#sightthumb').attr("src", '')
    getSightsByCity(city)
    event.preventDefault()
  })

// view results
  function showResults(object) {
    $('#sightlist').html("")

    if(sights.length == 0) {
      $('#cityerror').html("We are terribly sorry, we were not able to find any sights matching your city. ")
    }
    else {
      for (var i = 0; i < sights.length; i++) {
        var val = sights[i].Label.value
        $('#sightlist').append('
' + val + '
')
      }
    }
    // add popup functionality after li are appended
    $('.detail-popup').magnificPopup({
      type:'inline',
      midClick: true // Allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source in href.
    })
  }

// show details per sight
  function showDetails(id) {
    $('#sightlabel').html(sights[id].Label.value)
    $('#sightabstract').html(sights[id].Abstract.value)
    $('#sightthumb').attr("src", sights[id].Thumbnail.value)
  }

  // <------------ User Location --------------->
  function getLocation() {
    if (navigator.geolocation) {
        return navigator.geolocation.getCurrentPosition(setCoords);
    } else {
      console.log("Geolocation is not supported by this browser.")
    }
  }

  function setCoords(position) {
    centerLat = position.coords.latitude
    centerLng = position.coords.longitude

    // centers the map at the current position of the user
    map.setCenter({lat: centerLat, lng: centerLng})
    map.addListener('click', function() {
      closeInfowindows()
    })
  }

  function addMarkers() {
    clearMarkers()
    for (var i = 0; i < sights.length; i++) {
      addDelayedMarkers(sights[i], i * 50)
    }
  }

  function clearMarkers() {
    for (var i = 0; i < markers.length; i++) {
      markers[i].setMap(null)
    }
  }

  function closeInfowindows() {
    for (var i = 0; i < infoWindows.length; i++) {
      infoWindows[i].close()
    }
  }

  function addDelayedMarkers(sight, delay) {
    var string = ''
    if(sight.Thumbnail.value != '') string += ''
    string += '
' + sight.Label.value + '
'

    window.setTimeout(function() {
      var marker = new google.maps.Marker({
        position: {lat: parseFloat(sight.Lat.value), lng: parseFloat(sight.Long.value)},
        map: map,
        title: sight.Label.value,
        animation: google.maps.Animation.DROP
      })
      marker.info = new google.maps.InfoWindow({
        content: string
      })
      infoWindows.push(marker.info)
      google.maps.event.addListener(marker, 'click', function() {
        closeInfowindows()
        marker.info.open(map, marker)
      })
      markers.push(marker)
    }, delay)
  }

  // runs a query against the DB with given params
  function queryDB(query) {
    client.query(query, function (error, results) {
      if (error) {
        console.log(error)
      } else {
        console.log(results.results.bindings)
        sights = results.results.bindings
        showResults()
        $('html, body').stop().animate({
          scrollTop: $(document).height()
        }, 1500)
        addMarkers()
        var mean = getLocationMean()
        console.log("lat: " + mean.lat + " long: " + mean.long);
        map.setCenter({lat: mean.lat, lng: mean.long})
      }
    })
  }

  function addProximityMarkers(buildings, delay){
    //clearMarkers()
    for (var i = 0; i < buildings.length; i++) {

      if (!(buildingsOverall[buildings[i].name.value]))
      {
        console.log("add");
        buildingsOverall[buildings[i].name.value] = buildings[i];
        addDelayedProximityMarkers(buildings[i], i * 50)
      }
    }
  }

  function addDelayedProximityMarkers(building, delay) {
    var string = ''
    if(building.thumbnail.value != '') string += ''
    string += '
' + building.name.value + '
'

    window.setTimeout(function() {
      var marker = new google.maps.Marker({
        position: {lat: parseFloat(building.lat.value), lng: parseFloat(building.long.value)},
        map: map,
        title: building.name.value,
        animation: google.maps.Animation.DROP
      })
      marker.info = new google.maps.InfoWindow({
        content: string
      })
      infoWindows.push(marker.info)
      google.maps.event.addListener(marker, 'click', function() {
        closeInfowindows()
        marker.info.open(map, marker)
      })
      markers.push(marker)
    }, delay)
  }

  function queryInProximity(query) {
    client.query(query, function (error, results) {
      if (error) {
        console.log(error)
      } else {

        var buildings = results.results.bindings
        console.log(buildings)
        addProximityMarkers(buildings)
        }
    })
  }

  function getLocationMean() {
    var mean = {
      lat: 0,
      long: 0
    }

    for (var i = 0; i < sights.length; i++) {
      mean.lat += parseFloat(sights[i].Lat.value)
      mean.long += parseFloat(sights[i].Long.value)
    }

    mean.lat = mean.lat / sights.length
    mean.long = mean.long / sights.length
    return mean
  }

  function stringify(city) {
    var str = city.split(' ')

    for (var i = 0; i < str.length; i++) {
      str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1)
    }

    return str.join('_')
  }

  function getSightsInProximity(long, lat, radius=0.2) {
     var query = `PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
PREFIX dbo: <http://dbpedia.org/ontology/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT * WHERE
{
?s a dbo:ArchitecturalStructure .
?s rdfs:label ?name .
FILTER langMatches(lang(?name), 'en').
?s dbo:thumbnail ?thumbnail .
?s geo:lat ?lat .
?s geo:long ?long . FILTER ( ?long > ${long} - ${radius} && ?long < ${long} +  ${radius} && ?lat > ${lat} -  ${radius} && ?lat < ${lat} +  ${radius}) }
LIMIT 10000`;
    queryInProximity(query)
   //   console.log(query)
  }

  function getSightsByCity(city) {
    city = stringify(city)

    var query = "PREFIX dcterms:  <http://purl.org/dc/terms/>\
                  PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\
                  SELECT DISTINCT (str(?label) as ?Label) (str(?lat) as ?Lat) (str(?long) as ?Long) (str(?thumbnail) as ?Thumbnail) (?abstract as ?Abstract)\
                  WHERE { \
                    ?sight dct:subject <http://dbpedia.org/resource/Category:Visitor_attractions_in_" + city + "> .\
                  FILTER langMatches(lang(?label), 'en').\
                  FILTER langMatches(lang(?abstract), 'en').\
                    ?sight rdfs:label ?label .\
                    ?sight geo:lat ?lat .\
                    ?sight geo:long ?long .\
                    ?sight dbo:thumbnail ?thumbnail .\
                    ?sight dbo:abstract ?abstract \
                  }\
                  ORDER BY ASC(?Label)"
    queryDB(query)

  }
<|endoftext|>


<commit_before>'use strict';

/* Main App Module */
var mainApp = angular.module('dataMovingApp', [
  'pipes',
  'salesforce',
  'ui.router',
  'ui.bootstrap'
],function($locationProvider) {
    //$locationProvider.html5Mode({'enabled': true, 'requireBase': false});
})

.run( ['$rootScope', '$state', '$stateParams', '$http',
    function ($rootScope,   $state,   $stateParams, $http) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;

    //Get the user id if security is enabled
    $http.get('/userid').success(function(data) {
      $rootScope.userid = data;
    })
    .error( function( data, status, headers, config){
      console.log("Running tool with security disabled");
    })
  }
])

.config(function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('home', {
            url:'',
            views: {
              'pipeList': {
                  templateUrl: '/templates/pipeList.html',
                  controller: 'pipesController'
              },
              'pipeDetails':{
                templateUrl: "/templates/pipeDetails.about.html"
              },
              'pipeSidebar':{
                templateUrl: "/templates/pipeSidebar.html",
                  controller: 'pipesController'
              }
          }
        })
        .state('home.pipeDetails', {
            url:'/:id',
            resolve:{
              //Return a promise that will be resolved before the controller is instantiated
            pipeList:  function(pipesService){
               return pipesService.listPipes();
            }
            },
            views: {
              'pipeDetails@' : {
                templateUrl: '/templates/pipeDetails.html',
                  controller: 'pipeDetailsController'
              }
            }
        })
        .state('home.pipeDetails.tab',{
        	parent: 'home.pipeDetails',
        	url:'/tab/:tab',
        	templateUrl: function (stateParams){
        		if ( !stateParams.tab ){
        			return '/templates/pipeDetails.about.html';
        		}
        		return '/templates/pipeDetails.' + stateParams.tab + '.html';
        	},
        	controllerProvider: function($stateParams) {
        		if ( $stateParams.tab && $stateParams.tab === 'monitoring' ){
        			return 'pipeDetails.tab.' + $stateParams.tab + '.controller';
        		}
        		//Default
        		return 'pipeDetails.tab.controller';
        	},
        	onEnter: function($stateParams, pipesService){
        		if ( $stateParams.tab === 'monitoring' ){
        			pipesService.startMonitorCurrentRun();
        		}
        	},
        	onExit: function($stateParams, pipesService){
        		if ( $stateParams.tab === 'monitoring' ){
        			pipesService.stopMonitorCurrentRun( );
        		}
        		if ( pipesService.scope ){
        			delete pipesService.scope;
        		}
        	}
      })
})

.controller('dataMovingModel', ['$scope', '$http', '$location',
  function($scope, $http, $location) {
  $scope.goSalesforce = function(){
    $http.get('/sf').success(function(data) {
      console.log( data );
    })
    .error( function( data, status, headers, config){
      console.log( "error: " + data + " Status: " + status);
    })
  };
 }]
)

.controller('pipesController', ['$scope', '$rootScope', '$http', '$location', 'pipesService',
  function($scope, $rootScope, $http, $location, pipesService) {

  function listPipes(){
    pipesService.listPipes().then(
      function(pipes){
        $scope.pipes = pipes;
      },function( reason ){
        console.log("error reading list of pipes: " + reason );
      }
    );
  }

  listPipes();

  $scope.createNewPipe = function(){
    pipesService.createPipe()
    .then( function( pipe ){
      listPipes();
      $scope.$apply();
    }, function( reason ){
      alert("Unable to create new pipe: " + reason );
    });
  }

  $scope.removePipe = function(){
    if ( !$rootScope.$stateParams.id ){
      alert("No Pipe selected");
    }else{
      pipesService.removePipe( $rootScope.$stateParams.id ).then(
        function(){
          console.log("Pipe " + $rootScope.$stateParams.id + " successfully removed");
        },
        function( err ){
          alert("Unable to remove the pipe: " + err );
        }
      );
    }
  }
 }]
)

.directive('displayUtcTime', function () {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope:false,
    link: function (scope, element, attrs, ngModel) {
    	ngModel.$formatters.push(function(value) {
    		if ( !value || value === "" ){
    			return "";
    		}
    		return moment(value).format("hh:mm a");
    	});

    	ngModel.$parsers.push(function( value ){
    		if ( !value || value === "" ){
    			return;
    		}
    		var date = moment(value, "hh:mm a", true);
    		if ( !date.isValid() ){
    			ngModel.$setValidity('parse', true);
    			if ( scope && scope.$parent){
    				scope.$parent.error = "Invalid Date";
    			}
    			return;
    		}else{
    			ngModel.$setValidity('parse', false);
    			if ( scope && scope.$parent && scope.$parent.error ){
    				delete scope.$parent.error;
    			}
    		}
    		return date.format();
    	});
    }
  }
})

.controller('pipeDetailsController', ['$scope', '$http', '$location', '$state', '$stateParams','pipesService', 'salesforceService',
  function($scope, $http, $location, $state, $stateParams, pipesService, salesforceService) {
  $scope.selectedPipe = pipesService.findPipe( $stateParams.id);
  if ( $scope.selectedPipe.scheduleTime ){
    $scope.selectedPipe.scheduleTime = moment.utc( $scope.selectedPipe.scheduleTime ).toDate();
  }

  var port = $location.port();
  $scope.oauthCallback=$location.protocol() + "://" + $location.host() + ( (port === 80 || port === 443) ? "" : (":" + port)) +"/authCallback";

  $scope.activateRun = function(run){ // simple toggle to show/hide run details on monitoring
    if($scope.activeRun && $scope.activeRun == run){
      delete $scope.activeRun;
    } else {
      $scope.activeRun = run;
    }
  };

  $scope.savePipe = function( Obj ){
    Obj = Obj || {};

    var connect = (Obj.connect === "true" || Obj.connect === true) ? true : false;
    var nextPageTab = Obj.nextPageTab;

    pipesService.savePipe( $scope.selectedPipe ).then(
      function(){
        console.log("Pipe " + $scope.selectedPipe._id + " successfully saved");
        if ( !connect ){
          setTimeout( function(){
            $('#savePipe').modal('hide');
            if(nextPageTab){
              $scope.goToNextPage(nextPageTab);
            }
          },500);
        }else{
          $scope.connect();
        }

      },
      function( err ){
        var message = "Unable to save pipe: " + err;
        console.log(message);
        if ( !connect ){
          $('#savePipeBody').html( message );
        }
      }
    );
  }

  $scope.openTableList = function(){
      $scope.tableMenuOpenClass = (!$scope.tableMenuOpenClass || $scope.tableMenuOpenClass === "") ? "open" : "";
  }

  $scope.connect = function(){
	  window.location.href = "/sf/" + $scope.selectedPipe._id + "?url=" + encodeURIComponent($location.absUrl());
	  return;
  }

  $scope.getTablesList = function(){
    var tables = [ pipesService.allTables ];
    _.forEach( $scope.selectedPipe.tables, function( table ){
      tables.push( table );
    });
    return tables;
  }

  $scope.selectTable = function(table){
    $scope.selectedPipe.selectedTableName = table.labelPlural;
    $scope.selectedPipe.selectedTableId = table.name;
  }

  $scope.goToNextPage = function( tab ){
    $state.go("home.pipeDetails.tab", {tab:tab, id: $scope.selectedPipe._id });
  }

 }]
)

.directive('pageButtonBar', function(){
  return {
    restrict: 'E',
    scope: true,
    templateUrl: function( element, attr) {
      return "/templates/pageButtonBar.html";
    },
    link: function(scope, elem, attrs){
      scope.connect = attrs.connect;
      scope.nextPageTab = attrs.nextpagetab;
    }
  };
})

.directive('repeatRunDetailsDirective', function() {
  return function(scope, element, attrs) {
    $(element).popover({
      html : true,
      trigger: 'manual',
      placement: function (context, source) {
        var get_position = $(source).position();
        if (get_position.left > 515) {
          return "left";
        }
        if (get_position.left < 515) {
          return "right";
        }
        if (get_position.top < 110){
          return "bottom";
        }
        return "top";
      },
      content: function() {
        return $( element ).find("." + attrs.repeatRunDetailsDirective).html();
      }
    }).on("click", function(e) {
      e.preventDefault();
    }).on("mouseenter", function() {
      var _this = this;
      $(this).popover("show");
      $(this).siblings(".popover").on("mouseleave", function() {
        $(_this).popover('hide');
      });
    }).on("mouseleave", function() {
      var _this = this;
      setTimeout(function() {
        if (!$(".popover:hover").length) {
          $(_this).popover("hide")
        }
      }, 100);
    });
  };
})

.controller('pipeDetails.tab.controller', ['$scope', '$http', '$location', '$stateParams','pipesService',
    function($scope, $http, $location, $stateParams, pipesService) {
    $scope.tabName = $stateParams.tab;
  }]
)

.controller('pipeDetails.tab.monitoring.controller', ['$scope', '$http', '$location', '$state', '$stateParams','pipesService', 'salesforceService',
    function($scope, $http, $location, $state, $stateParams, pipesService, salesforceService) {
	pipesService.scope = $scope;
    $scope.tabName = $stateParams.tab;

    $scope.isPipeRunning = function(){
    	return $scope.selectedPipe && $scope.currentRun;
    }

    $scope.runNow = function(){
      salesforceService.runPipe( $scope.selectedPipe ).then(
          function(){
            console.log("Pipe " + $scope.selectedPipe._id + " successfully started");
          },
          function( err ){
            alert("Error while running pipe: " + err );
          }
      );
    }

    //Get the list of runs
    $scope.fetchRuns = function(){
      pipesService.getLastRuns($scope.selectedPipe).then(
          function( runs ){
            $scope.runs = runs;
            $scope.disableRunNow = false; // re-enables the Run Now button on monitoring
            if(!$scope.$$phase){
              $scope.$apply();
            }
          },function(err){
            alert("Unable to query the activity history: " + err);
          }
      );
    }

    $scope.fetchRuns();
  }]
)
<commit_msg>Minor fix: make sure we don't show invalid time when the user delete the time<commit_after>'use strict';

/* Main App Module */
var mainApp = angular.module('dataMovingApp', [
  'pipes',
  'salesforce',
  'ui.router',
  'ui.bootstrap'
],function($locationProvider) {
    //$locationProvider.html5Mode({'enabled': true, 'requireBase': false});
})

.run( ['$rootScope', '$state', '$stateParams', '$http',
    function ($rootScope,   $state,   $stateParams, $http) {
    $rootScope.$state = $state;
    $rootScope.$stateParams = $stateParams;

    //Get the user id if security is enabled
    $http.get('/userid').success(function(data) {
      $rootScope.userid = data;
    })
    .error( function( data, status, headers, config){
      console.log("Running tool with security disabled");
    })
  }
])

.config(function ($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('home', {
            url:'',
            views: {
              'pipeList': {
                  templateUrl: '/templates/pipeList.html',
                  controller: 'pipesController'
              },
              'pipeDetails':{
                templateUrl: "/templates/pipeDetails.about.html"
              },
              'pipeSidebar':{
                templateUrl: "/templates/pipeSidebar.html",
                  controller: 'pipesController'
              }
          }
        })
        .state('home.pipeDetails', {
            url:'/:id',
            resolve:{
              //Return a promise that will be resolved before the controller is instantiated
            pipeList:  function(pipesService){
               return pipesService.listPipes();
            }
            },
            views: {
              'pipeDetails@' : {
                templateUrl: '/templates/pipeDetails.html',
                  controller: 'pipeDetailsController'
              }
            }
        })
        .state('home.pipeDetails.tab',{
        	parent: 'home.pipeDetails',
        	url:'/tab/:tab',
        	templateUrl: function (stateParams){
        		if ( !stateParams.tab ){
        			return '/templates/pipeDetails.about.html';
        		}
        		return '/templates/pipeDetails.' + stateParams.tab + '.html';
        	},
        	controllerProvider: function($stateParams) {
        		if ( $stateParams.tab && $stateParams.tab === 'monitoring' ){
        			return 'pipeDetails.tab.' + $stateParams.tab + '.controller';
        		}
        		//Default
        		return 'pipeDetails.tab.controller';
        	},
        	onEnter: function($stateParams, pipesService){
        		if ( $stateParams.tab === 'monitoring' ){
        			pipesService.startMonitorCurrentRun();
        		}
        	},
        	onExit: function($stateParams, pipesService){
        		if ( $stateParams.tab === 'monitoring' ){
        			pipesService.stopMonitorCurrentRun( );
        		}
        		if ( pipesService.scope ){
        			delete pipesService.scope;
        		}
        	}
      })
})

.controller('dataMovingModel', ['$scope', '$http', '$location',
  function($scope, $http, $location) {
  $scope.goSalesforce = function(){
    $http.get('/sf').success(function(data) {
      console.log( data );
    })
    .error( function( data, status, headers, config){
      console.log( "error: " + data + " Status: " + status);
    })
  };
 }]
)

.controller('pipesController', ['$scope', '$rootScope', '$http', '$location', 'pipesService',
  function($scope, $rootScope, $http, $location, pipesService) {

  function listPipes(){
    pipesService.listPipes().then(
      function(pipes){
        $scope.pipes = pipes;
      },function( reason ){
        console.log("error reading list of pipes: " + reason );
      }
    );
  }

  listPipes();

  $scope.createNewPipe = function(){
    pipesService.createPipe()
    .then( function( pipe ){
      listPipes();
      $scope.$apply();
    }, function( reason ){
      alert("Unable to create new pipe: " + reason );
    });
  }

  $scope.removePipe = function(){
    if ( !$rootScope.$stateParams.id ){
      alert("No Pipe selected");
    }else{
      pipesService.removePipe( $rootScope.$stateParams.id ).then(
        function(){
          console.log("Pipe " + $rootScope.$stateParams.id + " successfully removed");
        },
        function( err ){
          alert("Unable to remove the pipe: " + err );
        }
      );
    }
  }
 }]
)

.directive('displayUtcTime', function () {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope:false,
    link: function (scope, element, attrs, ngModel) {
    	ngModel.$formatters.push(function(value) {
    		if ( !value || value === "" ){
    			return "";
    		}
    		return moment(value).format("hh:mm a");
    	});

    	ngModel.$parsers.push(function( value ){
    		if ( !value || value === "" ){
    			ngModel.$setValidity('parse', false);
    			if ( scope && scope.$parent && scope.$parent.error ){
    				delete scope.$parent.error;
    			}
    			return "";
    		}
    		var date = moment(value, "hh:mm a", true);
    		if ( !date.isValid() ){
    			ngModel.$setValidity('parse', true);
    			if ( scope && scope.$parent){
    				scope.$parent.error = "Invalid Date";
    			}
    			return;
    		}else{
    			ngModel.$setValidity('parse', false);
    			if ( scope && scope.$parent && scope.$parent.error ){
    				delete scope.$parent.error;
    			}
    		}
    		return date.format();
    	});
    }
  }
})

.controller('pipeDetailsController', ['$scope', '$http', '$location', '$state', '$stateParams','pipesService', 'salesforceService',
  function($scope, $http, $location, $state, $stateParams, pipesService, salesforceService) {
  $scope.selectedPipe = pipesService.findPipe( $stateParams.id);
  if ( $scope.selectedPipe.scheduleTime ){
    $scope.selectedPipe.scheduleTime = moment.utc( $scope.selectedPipe.scheduleTime ).toDate();
  }

  var port = $location.port();
  $scope.oauthCallback=$location.protocol() + "://" + $location.host() + ( (port === 80 || port === 443) ? "" : (":" + port)) +"/authCallback";

  $scope.activateRun = function(run){ // simple toggle to show/hide run details on monitoring
    if($scope.activeRun && $scope.activeRun == run){
      delete $scope.activeRun;
    } else {
      $scope.activeRun = run;
    }
  };

  $scope.savePipe = function( Obj ){
    Obj = Obj || {};

    var connect = (Obj.connect === "true" || Obj.connect === true) ? true : false;
    var nextPageTab = Obj.nextPageTab;

    pipesService.savePipe( $scope.selectedPipe ).then(
      function(){
        console.log("Pipe " + $scope.selectedPipe._id + " successfully saved");
        if ( !connect ){
          setTimeout( function(){
            $('#savePipe').modal('hide');
            if(nextPageTab){
              $scope.goToNextPage(nextPageTab);
            }
          },500);
        }else{
          $scope.connect();
        }

      },
      function( err ){
        var message = "Unable to save pipe: " + err;
        console.log(message);
        if ( !connect ){
          $('#savePipeBody').html( message );
        }
      }
    );
  }

  $scope.openTableList = function(){
      $scope.tableMenuOpenClass = (!$scope.tableMenuOpenClass || $scope.tableMenuOpenClass === "") ? "open" : "";
  }

  $scope.connect = function(){
	  window.location.href = "/sf/" + $scope.selectedPipe._id + "?url=" + encodeURIComponent($location.absUrl());
	  return;
  }

  $scope.getTablesList = function(){
    var tables = [ pipesService.allTables ];
    _.forEach( $scope.selectedPipe.tables, function( table ){
      tables.push( table );
    });
    return tables;
  }

  $scope.selectTable = function(table){
    $scope.selectedPipe.selectedTableName = table.labelPlural;
    $scope.selectedPipe.selectedTableId = table.name;
  }

  $scope.goToNextPage = function( tab ){
    $state.go("home.pipeDetails.tab", {tab:tab, id: $scope.selectedPipe._id });
  }

 }]
)

.directive('pageButtonBar', function(){
  return {
    restrict: 'E',
    scope: true,
    templateUrl: function( element, attr) {
      return "/templates/pageButtonBar.html";
    },
    link: function(scope, elem, attrs){
      scope.connect = attrs.connect;
      scope.nextPageTab = attrs.nextpagetab;
    }
  };
})

.directive('repeatRunDetailsDirective', function() {
  return function(scope, element, attrs) {
    $(element).popover({
      html : true,
      trigger: 'manual',
      placement: function (context, source) {
        var get_position = $(source).position();
        if (get_position.left > 515) {
          return "left";
        }
        if (get_position.left < 515) {
          return "right";
        }
        if (get_position.top < 110){
          return "bottom";
        }
        return "top";
      },
      content: function() {
        return $( element ).find("." + attrs.repeatRunDetailsDirective).html();
      }
    }).on("click", function(e) {
      e.preventDefault();
    }).on("mouseenter", function() {
      var _this = this;
      $(this).popover("show");
      $(this).siblings(".popover").on("mouseleave", function() {
        $(_this).popover('hide');
      });
    }).on("mouseleave", function() {
      var _this = this;
      setTimeout(function() {
        if (!$(".popover:hover").length) {
          $(_this).popover("hide")
        }
      }, 100);
    });
  };
})

.controller('pipeDetails.tab.controller', ['$scope', '$http', '$location', '$stateParams','pipesService',
    function($scope, $http, $location, $stateParams, pipesService) {
    $scope.tabName = $stateParams.tab;
  }]
)

.controller('pipeDetails.tab.monitoring.controller', ['$scope', '$http', '$location', '$state', '$stateParams','pipesService', 'salesforceService',
    function($scope, $http, $location, $state, $stateParams, pipesService, salesforceService) {
	pipesService.scope = $scope;
    $scope.tabName = $stateParams.tab;

    $scope.isPipeRunning = function(){
    	return $scope.selectedPipe && $scope.currentRun;
    }

    $scope.runNow = function(){
      salesforceService.runPipe( $scope.selectedPipe ).then(
          function(){
            console.log("Pipe " + $scope.selectedPipe._id + " successfully started");
          },
          function( err ){
            alert("Error while running pipe: " + err );
          }
      );
    }

    //Get the list of runs
    $scope.fetchRuns = function(){
      pipesService.getLastRuns($scope.selectedPipe).then(
          function( runs ){
            $scope.runs = runs;
            $scope.disableRunNow = false; // re-enables the Run Now button on monitoring
            if(!$scope.$$phase){
              $scope.$apply();
            }
          },function(err){
            alert("Unable to query the activity history: " + err);
          }
      );
    }

    $scope.fetchRuns();
  }]
)
<|endoftext|>


<commit_before>import React from 'react';
import '../sass/main.scss';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux';
import * as actionCreators from './action-creators';
import { Link } from 'react-router';

const pages = [
  {to: 'build', text: 'Build Resume'},
  {to: 'output', text: 'HTML Output'},
  {to: 'json', text: 'JSON Output'}
];

let App = React.createClass({
  exportToJSON() {
    const {actions, resume} = this.props;
    actions.saveJSON(resume);
  },

  render() {
    const {resume, actions, children, history} = this.props;

    return (

            <button type="button" className="btn btn-default">export JSON</button>
            <button type="button" className="btn btn-default">export HTML</button>

Resumaker

          {
            pages.map(({to, text}) =>

<Link to={to}>{text}</Link>

            )
          }

        {
          React.Children.map(children, child =>
            React.cloneElement(child, {resume, actions})
          )
        }

    );
  }
})

const mapStateToProps = (state) => state;
const mapDispatchToProps = (dispatch) => ({actions: bindActionCreators(actionCreators, dispatch)});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
<commit_msg>Add 'export to html' handler<commit_after>import React from 'react';
import '../sass/main.scss';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux';
import * as actionCreators from './action-creators';
import { Link } from 'react-router';

const pages = [
  {to: 'build', text: 'Build Resume'},
  {to: 'output', text: 'HTML Output'},
  {to: 'json', text: 'JSON Output'}
];

let App = React.createClass({
  exportToJSON() {
    const {actions, resume} = this.props;
    actions.saveJSON(resume);
  },

  exportToHTML() {
    const {actions, resume} = this.props;
    actions.saveHTML(resume);
  },

  render() {
    const {resume, actions, children, history} = this.props;

    return (

            <button type="button" className="btn btn-default">export JSON</button>
            <button type="button" className="btn btn-default">export HTML</button>

Resumaker

          {
            pages.map(({to, text}) =>

<Link to={to}>{text}</Link>

            )
          }

        {
          React.Children.map(children, child =>
            React.cloneElement(child, {resume, actions})
          )
        }

    );
  }
})

const mapStateToProps = (state) => state;
const mapDispatchToProps = (dispatch) => ({actions: bindActionCreators(actionCreators, dispatch)});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
<|endoftext|>


<commit_before>var app = angular.module('github-email-thief', [
  'ui.router',
  'ui.bootstrap',
  'ngResource',
  'ngSanitize',
  'LocalStorageModule',
  'angular-growl',
  'ngCsv',
  'angulartics',
  'angulartics.google.analytics'
]);

app.constant('BaseGHUrl', 'https://api.github.com');
app.constant('PopularLanguages', [
  'JavaScript',
  'Python',
  'Java',
  'Ruby',
  'Scala',
  'Swift',
  'Objective-C',
  'C',
  'PHP',
  'C#',
  'Go',
  'C++',
  'Clojure',
  'CoffeeScript',
  'CSS',
  'Haskell',
  'HTML',
  'Lua',
  'Matlab',
  'Perl',
  'R',
  'Shell',
  'TeX',
  'VimL',
]);

app.config(function($locationProvider) {
  $locationProvider.html5Mode(true);
});

// http://stackoverflow.com/a/23087400/468653
app.config(function($httpProvider) {
  $httpProvider.interceptors.push(function($q) {
    var realEncodeURIComponent = window.encodeURIComponent;
    return {
      'request': function(config) {
         window.encodeURIComponent = function(input) {
           return realEncodeURIComponent(input).split("%2B").join("+");
         };
         return config || $q.when(config);
      },
      'response': function(config) {
         window.encodeURIComponent = realEncodeURIComponent;
         return config || $q.when(config);
      }
    };
  });
});

app.config(function(growlProvider) {
  growlProvider.globalTimeToLive({
    success: 1000,
    error: 2000,
    warning: 3000,
    info: 4000
  });
  growlProvider.globalDisableIcons(true);
});

app.config(function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise("/");
    $stateProvider
      .state('base', {
        abstract: true,
        templateUrl: '/views/base.html',
        controller: 'BaseCtrl'
      })
      .state('home', {
        url: "/",
        parent: 'base',
        templateUrl: "views/home.html",
        controller: "HomeCtrl"
      })
      .state('search', {
        url: "/search",
        parent: 'base',
        templateUrl: "views/search.html",
        controller: "SearchCtrl"
      })
      .state('search.repos', {
        url: "/repos?language&page&query",
        templateUrl: "views/search_repos.html",
        controller: "SearchReposCtrl"
      })
      .state('search.codes', {
        url: "/codes?language&page&query",
        templateUrl: "views/search_codes.html",
        controller: "SearchCodesCtrl"
      })
      .state('search.users', {
        url: "/users?language&page&query",
        templateUrl: "views/search_users.html",
        controller: "SearchUsersCtrl"
      })
      .state('terms', {
        url: "/terms",
        parent: 'base',
        templateUrl: "views/terms.html"
      })
      .state('bookmarks', {
        url: "/bookmarks",
        parent: 'base',
        templateUrl: "views/bookmarks.html",
        controller: "BookmarksCtrl"
      });
  }
);

/** services **/
app.service('RepoSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/repositories',
    {},
    {
      get: {
        method: 'GET',
        transformResponse: function(data, headers){
            response = {}
            response.data = angular.fromJson(data);
            response.headers = headers();
            return response;
        }
      }
    });
});
app.service('CodeSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/code');
});
app.service('UserSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/users');
});
app.service('UserRepo', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/users/:owner/repos');
});
app.service('Repo', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/repos/:owner/:repo/:commits');
});
app.service('Commit', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/repos/:owner/:repo/commits/:sha');
});

app.service('Bookmark', function(localStorageService) {
  var userBookmarksKey = 'bookmarkedUsers';

  var addBookmark = function(data) {
    var bookmarks = localStorageService.get(userBookmarksKey);
    if (!bookmarks) {
      bookmarks = '{}';
    }
    bookmarks = angular.fromJson(bookmarks);
    bookmarks[data.email] = data;

    var newBookmarks = angular.toJson(bookmarks);
    localStorageService.set(userBookmarksKey, newBookmarks);
    return data;
  };
  var getBookmarks = function() {
    var bookmarks = localStorageService.get(userBookmarksKey);
    if (!bookmarks) {
      bookmarks = '{}';
    }
    return angular.fromJson(bookmarks);
  };
  return {
    addBookmark: addBookmark,
    getBookmarks: getBookmarks
  }
});
app.service('range', function() {
  // http://stackoverflow.com/a/8273091/468653
  return function(start, stop, step) {
    if (typeof stop == 'undefined') {
      stop = start;
      start = 0;
    }
    if (typeof step == 'undefined') {
      step = 1;
    }
    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
      return [];
    }
    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
      result.push(i);
    }
    return result;
  };
});

/** controllers **/
app.controller('BaseCtrl', function() { });
app.controller('HomeCtrl', function($scope, $state, PopularLanguages) {

  $scope.languages = PopularLanguages;
  $scope.goToSearch = function(lang) {
    $state.go('search.repos', {
      page: 1,
      language: lang
    });
  };
});

app.controller('SearchCtrl', function($scope, $modal, PopularLanguages, Repo, UserRepo, Bookmark, growl) {

  var uniqueEmails = function(results) {
    var s = new Set();
    for (var i = 0; i < results.length; i++) {
      var login = results[i].committer ? results[i].committer.login : null;
      var name = results[i].commit.committer ? results[i].commit.committer.name : null;
      var email = results[i].commit.committer ? results[i].commit.committer.email : null;
      s.add(JSON.stringify({
        login: login,
        name: name,
        email: email
      }));
    }
    var x = [];
    for (item of s) {
      x.push(JSON.parse(item));
    };
    return x;
  };

  $scope.searchEmail = function(owner, repo) {
    Repo.query({owner: owner, repo: repo, commits: 'commits'}, function(data) {
      $scope.authors = uniqueEmails(data);
      $modal.open({
        scope: $scope,
        show: true,
        size: 'lg',
        windowClass: 'app-modal-window',
        templateUrl: 'views/result-modal.html'
      });
    });
  };

  $scope.searchEmailByUser = function(owner) {
    UserRepo.query({owner: owner}, function(data) {
      if (data.length) {
        $scope.searchEmail(owner, data[0].name);
      } else {
        growl.error('No repos found for ' + owner);
      };
    });
  };

  $scope.bookmarkUser = function(data) {
    Bookmark.addBookmark(data);
    growl.success('Bookmarked');
  };
});

app.controller('SearchReposCtrl', function($scope, $state, $stateParams, PopularLanguages, RepoSearch, Repo, $modal, range) {

  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.repos", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.language) {
      qry.push("language="+$scope.formData.language);
    }
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    qry = qry.join('+');
    RepoSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response.data;
      $scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('SearchCodesCtrl', function($scope, $state, $stateParams, PopularLanguages, CodeSearch, range) {
  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.codes", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    qry.push("in:file");
    if ($scope.formData.language) {
      qry.push("language:"+$scope.formData.language);
    }
    qry = qry.join('+');
    CodeSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response.data;
      $scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('SearchUsersCtrl', function($scope, $state, $stateParams, PopularLanguages, UserSearch, range) {
  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.users", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    if ($scope.formData.language) {
      qry.push("language:"+$scope.formData.language);
    }
    qry = qry.join('+');
    UserSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response;
      //$scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('BookmarksCtrl', function($scope, $stateParams, Bookmark) {
  $scope.getArray = function() {
    var log = [];
    angular.forEach(Bookmark.getBookmarks(), function(value, key) {
      this.push(value);
    }, log);
    return log;
  };
  $scope.bookmarks = Bookmark.getBookmarks();
});

app.run(function() {
  FastClick.attach(document.body);
});
<commit_msg>handle repos with 0 commits<commit_after>var app = angular.module('github-email-thief', [
  'ui.router',
  'ui.bootstrap',
  'ngResource',
  'ngSanitize',
  'LocalStorageModule',
  'angular-growl',
  'ngCsv',
  'angulartics',
  'angulartics.google.analytics'
]);

app.constant('BaseGHUrl', 'https://api.github.com');
app.constant('PopularLanguages', [
  'JavaScript',
  'Python',
  'Java',
  'Ruby',
  'Scala',
  'Swift',
  'Objective-C',
  'C',
  'PHP',
  'C#',
  'Go',
  'C++',
  'Clojure',
  'CoffeeScript',
  'CSS',
  'Haskell',
  'HTML',
  'Lua',
  'Matlab',
  'Perl',
  'R',
  'Shell',
  'TeX',
  'VimL',
]);

app.config(function($locationProvider) {
  $locationProvider.html5Mode(true);
});

// http://stackoverflow.com/a/23087400/468653
app.config(function($httpProvider) {
  $httpProvider.interceptors.push(function($q) {
    var realEncodeURIComponent = window.encodeURIComponent;
    return {
      'request': function(config) {
         window.encodeURIComponent = function(input) {
           return realEncodeURIComponent(input).split("%2B").join("+");
         };
         return config || $q.when(config);
      },
      'response': function(config) {
         window.encodeURIComponent = realEncodeURIComponent;
         return config || $q.when(config);
      }
    };
  });
});

app.config(function(growlProvider) {
  growlProvider.globalTimeToLive({
    success: 1000,
    error: 2000,
    warning: 3000,
    info: 4000
  });
  growlProvider.globalDisableIcons(true);
});

app.config(function($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise("/");
    $stateProvider
      .state('base', {
        abstract: true,
        templateUrl: '/views/base.html',
        controller: 'BaseCtrl'
      })
      .state('home', {
        url: "/",
        parent: 'base',
        templateUrl: "views/home.html",
        controller: "HomeCtrl"
      })
      .state('search', {
        url: "/search",
        parent: 'base',
        templateUrl: "views/search.html",
        controller: "SearchCtrl"
      })
      .state('search.repos', {
        url: "/repos?language&page&query",
        templateUrl: "views/search_repos.html",
        controller: "SearchReposCtrl"
      })
      .state('search.codes', {
        url: "/codes?language&page&query",
        templateUrl: "views/search_codes.html",
        controller: "SearchCodesCtrl"
      })
      .state('search.users', {
        url: "/users?language&page&query",
        templateUrl: "views/search_users.html",
        controller: "SearchUsersCtrl"
      })
      .state('terms', {
        url: "/terms",
        parent: 'base',
        templateUrl: "views/terms.html"
      })
      .state('bookmarks', {
        url: "/bookmarks",
        parent: 'base',
        templateUrl: "views/bookmarks.html",
        controller: "BookmarksCtrl"
      });
  }
);

/** services **/
app.service('RepoSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/repositories',
    {},
    {
      get: {
        method: 'GET',
        transformResponse: function(data, headers){
            response = {}
            response.data = angular.fromJson(data);
            response.headers = headers();
            return response;
        }
      }
    });
});
app.service('CodeSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/code');
});
app.service('UserSearch', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/search/users');
});
app.service('UserRepo', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/users/:owner/repos');
});
app.service('Repo', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/repos/:owner/:repo/:commits');
});
app.service('Commit', function($resource, BaseGHUrl) {
  return $resource(BaseGHUrl + '/repos/:owner/:repo/commits/:sha');
});

app.service('Bookmark', function(localStorageService) {
  var userBookmarksKey = 'bookmarkedUsers';

  var addBookmark = function(data) {
    var bookmarks = localStorageService.get(userBookmarksKey);
    if (!bookmarks) {
      bookmarks = '{}';
    }
    bookmarks = angular.fromJson(bookmarks);
    bookmarks[data.email] = data;

    var newBookmarks = angular.toJson(bookmarks);
    localStorageService.set(userBookmarksKey, newBookmarks);
    return data;
  };
  var getBookmarks = function() {
    var bookmarks = localStorageService.get(userBookmarksKey);
    if (!bookmarks) {
      bookmarks = '{}';
    }
    return angular.fromJson(bookmarks);
  };
  return {
    addBookmark: addBookmark,
    getBookmarks: getBookmarks
  }
});
app.service('range', function() {
  // http://stackoverflow.com/a/8273091/468653
  return function(start, stop, step) {
    if (typeof stop == 'undefined') {
      stop = start;
      start = 0;
    }
    if (typeof step == 'undefined') {
      step = 1;
    }
    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
      return [];
    }
    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
      result.push(i);
    }
    return result;
  };
});

/** controllers **/
app.controller('BaseCtrl', function() { });
app.controller('HomeCtrl', function($scope, $state, PopularLanguages) {

  $scope.languages = PopularLanguages;
  $scope.goToSearch = function(lang) {
    $state.go('search.repos', {
      page: 1,
      language: lang
    });
  };
});

app.controller('SearchCtrl', function($scope, $modal, PopularLanguages, Repo, UserRepo, Bookmark, growl) {

  var uniqueEmails = function(results) {
    var s = new Set();
    for (var i = 0; i < results.length; i++) {
      var login = results[i].committer ? results[i].committer.login : null;
      var name = results[i].commit.committer ? results[i].commit.committer.name : null;
      var email = results[i].commit.committer ? results[i].commit.committer.email : null;
      s.add(JSON.stringify({
        login: login,
        name: name,
        email: email
      }));
    }
    var x = [];
    for (item of s) {
      x.push(JSON.parse(item));
    };
    return x;
  };

  var openResultsModal = function(data) {
    $scope.authors = uniqueEmails(data);
    $modal.open({
      scope: $scope,
      show: true,
      size: 'lg',
      windowClass: 'app-modal-window',
      templateUrl: 'views/result-modal.html'
    });
  };

  $scope.searchEmail = function(owner, repo) {
    Repo.query({owner: owner, repo: repo, commits: 'commits'}, function(data) {
      openResultsModal(data);
    }, function() {
      growl.error('There was an error retrieving commit data for this repo');
    });
  };

  var recursionDepth = 0;
  var MAX_RECURSION_DEPTH = 5;
  var checkReposForCommits = function(owner, repos) {
    if (recursionDepth >= MAX_RECURSION_DEPTH || recursionDepth > repos.length) {
      growl.error('Could not find a repository with sufficient commit data');
    };
    var repo = repos[recursionDepth];
    Repo.query({owner: owner, repo: repo.name, commits: 'commits'}, function(data) {
      openResultsModal(data);
    }, function() {
      recursionDepth++;
      checkReposForCommits(owner, repos);
    });
  };

  $scope.searchEmailByUser = function(owner) {
    recursionDepth = 0;
    UserRepo.query({owner: owner}, function(data) {
      if (data.length) {
        checkReposForCommits(owner, data);
      } else {
        growl.error('No repos found for ' + owner);
      };
    }, function() {
      growl.error("Error encountered in fetching "+owner+"'s list of Github repos");
    });
  };

  $scope.bookmarkUser = function(data) {
    Bookmark.addBookmark(data);
    growl.success('Bookmarked');
  };
});

app.controller('SearchReposCtrl', function($scope, $state, $stateParams, PopularLanguages, RepoSearch, Repo, $modal, range) {

  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.repos", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.language) {
      qry.push("language="+$scope.formData.language);
    }
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    qry = qry.join('+');
    RepoSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response.data;
      $scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('SearchCodesCtrl', function($scope, $state, $stateParams, PopularLanguages, CodeSearch, range) {
  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.codes", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    qry.push("in:file");
    if ($scope.formData.language) {
      qry.push("language:"+$scope.formData.language);
    }
    qry = qry.join('+');
    CodeSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response.data;
      $scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('SearchUsersCtrl', function($scope, $state, $stateParams, PopularLanguages, UserSearch, range) {
  $scope.languages = PopularLanguages;
  $scope.formData = {
    language: $stateParams.language,
    query: $stateParams.query,
    page: parseInt($stateParams.page)
  };
  $scope.range = range;
  $scope.refreshSearch = function() {
    $scope.formData.page = 1;
    $state.go("search.users", $scope.formData);
  };

  function refresh() {
    var qry = [];
    if ($scope.formData.query) {
      qry.push($scope.formData.query);
    }
    if ($scope.formData.language) {
      qry.push("language:"+$scope.formData.language);
    }
    qry = qry.join('+');
    UserSearch.get({
      q: qry,
      page: $scope.formData.page
    }, function(response) {
      $scope.data = response;
      //$scope.headers = response.headers;
      var maxResults = Math.min($scope.data.total_count, 1000);
      $scope.numOfPages = Math.ceil(maxResults/$scope.data.items.length);
    });
  };
  refresh();
});

app.controller('BookmarksCtrl', function($scope, $stateParams, Bookmark) {
  $scope.getArray = function() {
    var log = [];
    angular.forEach(Bookmark.getBookmarks(), function(value, key) {
      this.push(value);
    }, log);
    return log;
  };
  $scope.bookmarks = Bookmark.getBookmarks();
});

app.run(function() {
  FastClick.attach(document.body);
});
<|endoftext|>


<commit_before>var pamApp = angular.module('pamApp', ['ngRoute', 'ngSanitize', 'hc.marked']);

pamApp.config(function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: 'components/search.html',
        controller: 'SearchCtrl'
    }).
    when('/new', {
        templateUrl: 'components/new.html',
        controller: 'NewCtrl'
    }).
    when('/notes/:id', {
        templateUrl: 'components/note.html',
        controller: 'NoteCtrl'
    }).
    when('/notes/:id/edit', {
        templateUrl: 'components/edit.html',
        controller: 'EditCtrl'
    }).
    otherwise({
        redirectTo: '/'
    });
});

pamApp.config(function(markedProvider) {
    markedProvider.setOptions({
      renderer: new marked.Renderer(),
      gfm: true,
      tables: true,
      breaks: true,
      pedantic: false,
      sanitize: true,
      smartLists: true,
      smartypants: true,
      highlight: function(code, lang) {
        if (lang && code) {
            return hljs.highlight(lang, code).value;
        }
      }
    });
});

pamApp.directive('pamSubjectListRadio', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/subject/subjectlist-radio.html'
  };
})

pamApp.directive('pamSubjectListCheck', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/subject/subjectlist-check.html'
  };
})

pamApp.directive('pamImageList', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/image/imagelist.html'
  };
})

pamApp.directive('pamNoteList', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/note/notelist.html'
  };
})

pamApp.directive('pamData', ["marked", function(marked) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$watch(attrs.pamData, function(value) {
        if (value) {
          element[0].innerHTML = marked(htmlReplace(value));
        }
      })

      function htmlReplace(str) {
        return String(str)
            .replace(/&/g, '&')
            .replace(/"/g, '"')
            .replace(/'/g, '\'')
            .replace(/</g, '<')
            .replace(/>/g, '>')
            .replace(/

|
/g, "\n")
            .replace(/
/g, '')
            .replace(/ /g, ' ')
            .replace(/
<\/div>|
/g, "\n")
            .replace(/<\/div>/g, '');
      };
    }
  }
}])

pamApp.directive('pamTextbox', ['$routeParams', '$http', function($routeParams, $http) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {

      element.on('paste', function(e) {
        var items = (e.clipboardData || e.originalEvent.clipboardData).items;
        var textbox = element[0];
        if (items && items[0].type == "text/plain") {
            e.preventDefault();
            document.execCommand("insertHTML", false, stripNewLine(e.clipboardData.getData('text')));
        } else if (items) {
            var blob = items[0].getAsFile();
            var reader = new FileReader();
            reader.onload = function(event){
                var imgurl = event.target.result;
                $http.post('/notes/' + $routeParams.id, {
                    "imgType": imgurl.match(/data:(.*?);/)[1],
                    "imgContent": imgurl.match(/base64,(.*)/)[1]
                }).success( function(data){
                    // Replace image tag with handlebars ID of the POST image
                    data = data.replace(/"/g, '');
                    var result = "[![" + data + "](/notes/" + $routeParams.id + "/" + data + ")](" + $routeParams.id + "/" + data + ")";
                    document.execCommand("insertHTML", false, result);
                    getIMG();
                }).error(handleError)
            };
            reader.readAsDataURL(blob);
        } else if (e.clipboardData.getData('text')) {
            // paste text clipboard data and strip style editing
            e.preventDefault();
            document.execCommand("insertHTML", false, stripNewLine(e.clipboardData.getData('text')));
        } else {
            // else wait on window for paste event and POST contents
            window.setTimeout(imgPost, 0, true);
        }
      });

      function imgPost() {
        var html = element[0].innerHTML;
        // extract image type and base64 content and post to DB
        $http.post('/notes/' + $routeParams.id, {
            "imgType": html.match(/data:(.*?);/)[1],
            "imgContent": html.match(/base64,(.*?)"/)[1]
        }).success( function(data){
            // Replace image tag with handlebars ID of the POST image
            data = data.replace(/"/g, '')
            element[0].innerHTML = html.replace(//, "[![" + data + "](/notes/" + $routeParams.id + "/" + data + ")](" + $routeParams.id + "/" + data + ")");
            getIMG();
        })
      }

      function getIMG() {
          $http.get('/notes/' + $routeParams.id + '/img').
          success(function(data) {
              scope.imgList = data;
          })
      }

      // Replace \n with
 in textbox
      function stripNewLine(str) {
          str = str.replace(/\n/g, '
');
          return str;
      }
    }
  }
}])<commit_msg>Clean up<commit_after>var pamApp = angular.module('pamApp', ['ngRoute', 'hc.marked']);

pamApp.config(function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: 'components/search.html',
        controller: 'SearchCtrl'
    }).
    when('/new', {
        templateUrl: 'components/new.html',
        controller: 'NewCtrl'
    }).
    when('/notes/:id', {
        templateUrl: 'components/note.html',
        controller: 'NoteCtrl'
    }).
    when('/notes/:id/edit', {
        templateUrl: 'components/edit.html',
        controller: 'EditCtrl'
    }).
    otherwise({
        redirectTo: '/'
    });
});

pamApp.config(function(markedProvider) {
    markedProvider.setOptions({
      renderer: new marked.Renderer(),
      gfm: true,
      tables: true,
      breaks: true,
      pedantic: false,
      sanitize: true,
      smartLists: true,
      smartypants: true,
      highlight: function(code, lang) {
        if (lang && code) {
            return hljs.highlight(lang, code).value;
        }
      }
    });
});

pamApp.directive('pamSubjectListRadio', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/subject/subjectlist-radio.html'
  };
})

pamApp.directive('pamSubjectListCheck', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/subject/subjectlist-check.html'
  };
})

pamApp.directive('pamImageList', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/image/imagelist.html'
  };
})

pamApp.directive('pamNoteList', function() {
  return {
    restrict: 'E',
    transclude: true,
    templateUrl: 'shared/note/notelist.html'
  };
})

pamApp.directive('pamData', ["marked", function(marked) {
  return {
    restrict: "A",
    link: function(scope, element, attrs) {
      scope.$watch(attrs.pamData, function(value) {
        if (value) {
          element[0].innerHTML = marked(htmlReplace(value));
        }
      })

      function htmlReplace(str) {
        return String(str)
            .replace(/&/g, '&')
            .replace(/"/g, '"')
            .replace(/'/g, '\'')
            .replace(/</g, '<')
            .replace(/>/g, '>')
            .replace(/

|
/g, "\n")
            .replace(/
/g, '')
            .replace(/ /g, ' ')
            .replace(/
<\/div>|
/g, "\n")
            .replace(/<\/div>/g, '');
      };
    }
  }
}])

pamApp.directive('pamTextbox', ['$routeParams', '$http', function($routeParams, $http) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {

      element.on('paste', function(e) {
        var items = (e.clipboardData || e.originalEvent.clipboardData).items;
        var textbox = element[0];
        if (items && items[0].type == "text/plain") {
            e.preventDefault();
            document.execCommand("insertHTML", false, stripNewLine(e.clipboardData.getData('text')));
        } else if (items) {
            var blob = items[0].getAsFile();
            var reader = new FileReader();
            reader.onload = function(event){
                var imgurl = event.target.result;
                $http.post('/notes/' + $routeParams.id, {
                    "imgType": imgurl.match(/data:(.*?);/)[1],
                    "imgContent": imgurl.match(/base64,(.*)/)[1]
                }).success( function(data){
                    // Replace image tag with handlebars ID of the POST image
                    data = data.replace(/"/g, '');
                    var result = "[![" + data + "](/notes/" + $routeParams.id + "/" + data + ")](" + $routeParams.id + "/" + data + ")";
                    document.execCommand("insertHTML", false, result);
                    getIMG();
                }).error(handleError)
            };
            reader.readAsDataURL(blob);
        } else if (e.clipboardData.getData('text')) {
            // paste text clipboard data and strip style editing
            e.preventDefault();
            document.execCommand("insertHTML", false, stripNewLine(e.clipboardData.getData('text')));
        } else {
            // else wait on window for paste event and POST contents
            window.setTimeout(imgPost, 0, true);
        }
      });

      function imgPost() {
        var html = element[0].innerHTML;
        // extract image type and base64 content and post to DB
        $http.post('/notes/' + $routeParams.id, {
            "imgType": html.match(/data:(.*?);/)[1],
            "imgContent": html.match(/base64,(.*?)"/)[1]
        }).success( function(data){
            // Replace image tag with handlebars ID of the POST image
            data = data.replace(/"/g, '')
            element[0].innerHTML = html.replace(//, "[![" + data + "](/notes/" + $routeParams.id + "/" + data + ")](" + $routeParams.id + "/" + data + ")");
            getIMG();
        })
      }

      function getIMG() {
          $http.get('/notes/' + $routeParams.id + '/img').
          success(function(data) {
              scope.imgList = data;
          })
      }

      // Replace \n with
 in textbox
      function stripNewLine(str) {
          str = str.replace(/\n/g, '
');
          return str;
      }
    }
  }
}])<|endoftext|>


<commit_before>/**
 Copyright 2017 NCC Group PLC http://www.nccgroup.trust/

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

(function() {
  const OVERLAY_CLASS = 'ncc-image-checker-overlay';
  const URL_CLASS = 'ncc-image-checker-url';
  const BACKGROUND_IMAGE_URL_REGEX = /url\((.*)\)/i;
  const SVG_URL_REGEX = /\.svg/i;
  const SVG_DATA_URI_REGEX = /^data:.*\/svg/i;
  const MIN_IMAGE_SIZE = 10;
  const CHECKER_INTERVAL_MS = 500;
  let refreshImagesInterval;

  /**
   * Appends an overlay with images details over the whole web page
   *
   * @param {NodeList} [images=All nodes in the DOM] - The Node List to be analysed
   */
  function showImagesInfo(images) {
    images = images || document.getElementsByTagName('*');
    images = nodeListToArray(images);
    let body = document.getElementsByTagName('body')[0];
    getImages(images).forEach(image => {
      const MIN_IMAGE_CONTENT_WIDTH = 150;
      const MIN_IMAGE_CONTENT_HEIGHT = 70;
      const MIN_IMAGE_URL_HEIGHT = 120;

      if (!isSVG(image) &&
        image.naturalSize.width > MIN_IMAGE_SIZE &&
        image.naturalSize.height > MIN_IMAGE_SIZE) {
        let div = document.createElement('div');
        if (image.width > MIN_IMAGE_CONTENT_WIDTH && image.height > MIN_IMAGE_CONTENT_HEIGHT) {
          div.setAttribute('title', image.url);

          if (image.height > MIN_IMAGE_URL_HEIGHT) {
            let url = document.createElement('a');
            url.innerHTML = `<b>${getName(image.url)}</b><br>Host: ${getHost(image.url)}`;
            url.setAttribute('href', image.url);
            url.setAttribute('target', '_blank');
            url.classList.add(URL_CLASS);
            div.appendChild(url);
            styleElement(div, image);
            appendInfoToElement(div, image);
            body.appendChild(div);
          } else {
            appendInfoToElement(div, image);
            appendAnchorToBody(div, image);
          }
        } else {
          let info = `Coverage: ${getImageScale(image).toFixed(2)}x`;

          if (image.size && image.size > 0) {
            info += `, File Size: ${image.size} KB`;
          }

          info += `, URL: ${image.url}`;

          appendAnchorToBody(div, image, info);
        }
      }
    });
    setRefreshImagesWatcher();
  }

  /**
   * Refresh overlay
   */
  function refreshImagesInfo() {
    hideImagesInfo();
    showImagesInfo();
  }

  /**
   * Set event listeners
   */
  function setRefreshImagesWatcher() {
    let width = window.innerWidth;
    let height = window.innerHeight;
    let href = window.location.href;
    let scrollX = window.scrollX;
    let scrollY = window.scrollY;
    let update;

    clearInterval(refreshImagesInterval);
    refreshImagesInterval = setInterval(() => {
      update = false;
      /* istanbul ignore if */
      if (window.location.href !== href) {
        update = true;
        href = window.location.href;
      }
      if (window.innerHeight !== height || window.innerWidth !== width) {
        update = true;
        width = window.innerWidth;
        height = window.innerHeight;
      }
      if (window.scrollX !== scrollX || window.scrollY !== scrollY) {
        update = true;
        scrollX = window.scrollX;
        scrollY = window.scrollY;
      }
      if (update) {
        refreshImagesInfo();
      }
    }, CHECKER_INTERVAL_MS);
  }

  /**
   * Remove all overlays
   */
  function hideImagesInfo() {
    nodeListToArray(document.querySelectorAll('.ncc-image-checker-overlay')).map(o => o.remove());
    clearInterval(refreshImagesInterval);
  }

  /**
   * Are there images overlays?
   *
   * @returns {boolean}
   */
  function isImagesInfoActive() {
    return document.querySelectorAll('.ncc-image-checker-overlay').length > 0;
  }

  function appendInfoToElement(div, image) {
    div.setAttribute('title', image.url);
    let renderedP = document.createElement('p');
    renderedP.innerHTML = `Display: ${image.width} x ${image.height}`;
    div.appendChild(renderedP);

    let naturalP = document.createElement('p');
    naturalP.innerHTML = `Natural: ${image.naturalSize.width} x ${image.naturalSize.height}`;
    div.appendChild(naturalP);

    let optimalP = document.createElement('p');
    let scale = getImageScale(image).toFixed(1);
    optimalP.innerHTML = `Image coverage: ${scale}x`;
    div.appendChild(optimalP);

    if (image.size) {
      let sizeP = document.createElement('p');
      sizeP.innerHTML = image.size > 0 ? `File Size: ${image.size} KB` : 'File size unavailable';
      div.appendChild(sizeP);
    }

    let duration = getDuration(image.url);
    if (duration) {
      let durationP = document.createElement('p');
      durationP.innerHTML = duration > 0
        ? `Download duration: ${duration.toFixed(0)} ms`
        : 'Download duration unavailable';
      div.appendChild(durationP);
    }
  }

  function appendAnchorToBody(element, image, info) {
    let anchor = document.createElement('a');
    let title = info || element.title;
    styleElement(anchor, image);
    anchor.setAttribute('href', image.url);
    anchor.setAttribute('target', '_blank');
    anchor.setAttribute('title', title);
    anchor.appendChild(element);
    document.getElementsByTagName('body')[0].appendChild(anchor);
  }

  function styleElement(element, image) {
    element.style.width = `${image.width + 1}px`;
    element.style.height = `${image.height + 1}px`;
    element.style.top = `${image.position.top}px`;
    element.style.left = `${image.position.left}px`;
    element.style.backgroundColor = getBackgroundColor(getImageCoverage(image));
    element.classList.add(OVERLAY_CLASS);
  }

  function getBackgroundColor(percentage) {
    let col = percentage;
    if (percentage < 0) {
      col = 0;
    }
    if (percentage > 240) {
      col = 240;
    }
    col = 240 - col;
    return `hsla(${col}, 100%, 50%, .8)`;
  }

  function getImageCoverage(image) {
    let naturalArea = image.naturalSize.width * image.naturalSize.height;
    let renderArea = image.width * image.height;
    return (100 * (naturalArea / renderArea));
  }

  function getImageScale(image) {
    let widthRatio = image.naturalSize.width / image.width;
    let heightRatio = image.naturalSize.height / image.height;
    return Math.max(widthRatio, heightRatio);
  }

  // this is the last point element is a DOM element
  function getImages(domNodes) {
    let images = getAvailableImages(domNodes);
    return images.map(element => {
      let size = getSize(element);
      if (typeof size === 'number') {
        size = (size / 1024).toFixed(3);
      }
      return {
        url: getUrl(element),
        size: size,
        position: getElementTopLeft(element),
        height: element.offsetHeight,
        width: element.offsetWidth,
        naturalSize: getNaturalSize(element)
      };
    }).filter(byVisibleImage);
  }

  function getAvailableImages(elementsArray) {
    return elementsArray
      .filter(byProbableImage)
      .filter(byHasUrl);
  }

  function byVisibleImage(image) {
    return (image.height && image.width &&
    typeof image.position.top === 'number' &&
    typeof image.position.left === 'number');
  }

  function byProbableImage(element) {
    let style = window.getComputedStyle(element);

    if (style.visibility === 'hidden') {
      return false;
    }

    if (element.tagName.toLowerCase() === 'img') {
      return true;
    }

    if (style.backgroundImage) {
      let urlMatcher = BACKGROUND_IMAGE_URL_REGEX.exec(style.backgroundImage);

      if (urlMatcher && urlMatcher.length > 1) {
        return true;
      }
    }

    return false;
  }

  function byHasUrl(element) {
    return !!getUrl(element);
  }

  function getElementTopLeft(elem) {
    let location = {
      top: 0,
      left: 0
    };
    if (elem.getBoundingClientRect) {
      let boundingClientRect = elem.getBoundingClientRect();
      location.top = window.scrollY + boundingClientRect.top;
      location.left = window.scrollX + boundingClientRect.left;
    }
    return location;
  }

  function getNaturalSize(element) {
    if (element.naturalWidth) {
      return {
        width: element.naturalWidth,
        height: element.naturalHeight
      };
    } else {
      let image = new Image();
      image.src = getUrl(element);

      return {
        width: image.naturalWidth,
        height: image.naturalHeight
      };
    }
  }

  function getSize(element) {
    let performanceEntrySize = performance.getEntriesByName(getUrl(element))[0];
    if (performanceEntrySize) {
      return performanceEntrySize.encodedBodySize;
    }
  }

  function getDuration(element) {
    let performanceEntryDuration = performance.getEntriesByName(element)[0];
    if (performanceEntryDuration) {
      return performanceEntryDuration.duration;
    }
  }

  function getUrl(element) {
    if (element.currentSrc) {
      return element.currentSrc;
    } else if (element.src) {
      return element.src;
    } else {
      let bkg = window.getComputedStyle(element).backgroundImage;
      let url = BACKGROUND_IMAGE_URL_REGEX.exec(bkg);
      if (url) {
        return url[1].replace(/["]/g, '');
      }
    }
  }

  function getName(s) {
    s = s.replace(/^.*[\\/]/, '');
    if (s.indexOf('?') > 0) {
      s = s.substr(0, s.indexOf('?'));
    }
    return s;
  }

  function getHost(s) {
    let url = new URL(s);
    return url.hostname;
  }

  function isSVG(s) {
    return SVG_URL_REGEX.test(s.url) || SVG_DATA_URI_REGEX.test(s.url);
  }

  function nodeListToArray(nodeList) {
    let array = [];

    for (let i = 0; i < nodeList.length; i += 1) {
      array[i] = nodeList[i];
    }

    return array;
  }

  window.NCC = window.NCC || {};

  window.NCC.imageChecker = {
    showImagesInfo: showImagesInfo,
    hideImagesInfo: hideImagesInfo,
    isImagesInfoActive: isImagesInfoActive,
    _getImages: getImages,
    _nodeListToArray: nodeListToArray,
    _getUrl: getUrl,
    _getSize: getSize,
    _getNaturalSize: getNaturalSize,
    _getImageCoverage: getImageCoverage,
    _getElementTopLeft: getElementTopLeft,
    _getAvailableImages: getAvailableImages,
    _getBackgroundColor: getBackgroundColor
  };
}());
<commit_msg>[ISSUE-41][FEATURE] update positioning for body with position relative and margins<commit_after>/**
 Copyright 2017 NCC Group PLC http://www.nccgroup.trust/

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

(function() {
  const OVERLAY_CLASS = 'ncc-image-checker-overlay';
  const URL_CLASS = 'ncc-image-checker-url';
  const BACKGROUND_IMAGE_URL_REGEX = /url\((.*)\)/i;
  const SVG_URL_REGEX = /\.svg/i;
  const SVG_DATA_URI_REGEX = /^data:.*\/svg/i;
  const MIN_IMAGE_SIZE = 10;
  const CHECKER_INTERVAL_MS = 500;
  let refreshImagesInterval;

  /**
   * Appends an overlay with images details over the whole web page
   *
   * @param {NodeList} [images=All nodes in the DOM] - The Node List to be analysed
   */
  function showImagesInfo(images) {
    images = images || document.getElementsByTagName('*');
    images = nodeListToArray(images);
    let imagesInfoParentElement = getImagesInfoParentElement();
    getImages(images).forEach(image => {
      const MIN_IMAGE_CONTENT_WIDTH = 150;
      const MIN_IMAGE_CONTENT_HEIGHT = 70;
      const MIN_IMAGE_URL_HEIGHT = 120;

      if (!isSVG(image) &&
        image.naturalSize.width > MIN_IMAGE_SIZE &&
        image.naturalSize.height > MIN_IMAGE_SIZE) {
        let div = document.createElement('div');
        if (image.width > MIN_IMAGE_CONTENT_WIDTH && image.height > MIN_IMAGE_CONTENT_HEIGHT) {
          div.setAttribute('title', image.url);

          if (image.height > MIN_IMAGE_URL_HEIGHT) {
            let url = document.createElement('a');
            url.innerHTML = `<b>${getName(image.url)}</b><br>Host: ${getHost(image.url)}`;
            url.setAttribute('href', image.url);
            url.setAttribute('target', '_blank');
            url.classList.add(URL_CLASS);
            div.appendChild(url);
            styleElement(div, image);
            appendInfoToElement(div, image);
            imagesInfoParentElement.appendChild(div);
          } else {
            appendInfoToElement(div, image);
            appendAnchorToBody(div, image);
          }
        } else {
          let info = `Coverage: ${getImageScale(image).toFixed(2)}x`;

          if (image.size && image.size > 0) {
            info += `, File Size: ${image.size} KB`;
          }

          info += `, URL: ${image.url}`;

          appendAnchorToBody(div, image, info);
        }
      }
    });
    setRefreshImagesWatcher();
  }

  /**
   * Refresh overlay
   */
  function refreshImagesInfo() {
    hideImagesInfo();
    showImagesInfo();
  }

  /**
   * Set event listeners
   */
  function setRefreshImagesWatcher() {
    let width = window.innerWidth;
    let height = window.innerHeight;
    let href = window.location.href;
    let scrollX = window.scrollX;
    let scrollY = window.scrollY;
    let update;

    clearInterval(refreshImagesInterval);
    refreshImagesInterval = setInterval(() => {
      update = false;
      /* istanbul ignore if */
      if (window.location.href !== href) {
        update = true;
        href = window.location.href;
      }
      if (window.innerHeight !== height || window.innerWidth !== width) {
        update = true;
        width = window.innerWidth;
        height = window.innerHeight;
      }
      if (window.scrollX !== scrollX || window.scrollY !== scrollY) {
        update = true;
        scrollX = window.scrollX;
        scrollY = window.scrollY;
      }
      if (update) {
        refreshImagesInfo();
      }
    }, CHECKER_INTERVAL_MS);
  }

  /**
   * Remove all overlays
   */
  function hideImagesInfo() {
    nodeListToArray(document.querySelectorAll('.ncc-image-checker-overlay')).map(o => o.remove());
    clearInterval(refreshImagesInterval);
  }

  /**
   * Are there images overlays?
   *
   * @returns {boolean}
   */
  function isImagesInfoActive() {
    return document.querySelectorAll('.ncc-image-checker-overlay').length > 0;
  }

  function appendInfoToElement(div, image) {
    div.setAttribute('title', image.url);
    let renderedP = document.createElement('p');
    renderedP.innerHTML = `Display: ${image.width} x ${image.height}`;
    div.appendChild(renderedP);

    let naturalP = document.createElement('p');
    naturalP.innerHTML = `Natural: ${image.naturalSize.width} x ${image.naturalSize.height}`;
    div.appendChild(naturalP);

    let optimalP = document.createElement('p');
    let scale = getImageScale(image).toFixed(1);
    optimalP.innerHTML = `Image coverage: ${scale}x`;
    div.appendChild(optimalP);

    if (image.size) {
      let sizeP = document.createElement('p');
      sizeP.innerHTML = image.size > 0 ? `File Size: ${image.size} KB` : 'File size unavailable';
      div.appendChild(sizeP);
    }

    let duration = getDuration(image.url);
    if (duration) {
      let durationP = document.createElement('p');
      durationP.innerHTML = duration > 0
        ? `Download duration: ${duration.toFixed(0)} ms`
        : 'Download duration unavailable';
      div.appendChild(durationP);
    }
  }

  function appendAnchorToBody(element, image, info) {
    let anchor = document.createElement('a');
    let title = info || element.title;
    styleElement(anchor, image);
    anchor.setAttribute('href', image.url);
    anchor.setAttribute('target', '_blank');
    anchor.setAttribute('title', title);
    anchor.appendChild(element);
    getImagesInfoParentElement().appendChild(anchor);
  }

  function styleElement(element, image) {
    element.style.width = `${image.width + 1}px`;
    element.style.height = `${image.height + 1}px`;
    element.style.top = `${image.position.top}px`;
    element.style.left = `${image.position.left}px`;
    element.style.backgroundColor = getBackgroundColor(getImageCoverage(image));
    element.classList.add(OVERLAY_CLASS);
  }

  function getBackgroundColor(percentage) {
    let col = percentage;
    if (percentage < 0) {
      col = 0;
    }
    if (percentage > 240) {
      col = 240;
    }
    col = 240 - col;
    return `hsla(${col}, 100%, 50%, .8)`;
  }

  function getImageCoverage(image) {
    let naturalArea = image.naturalSize.width * image.naturalSize.height;
    let renderArea = image.width * image.height;
    return (100 * (naturalArea / renderArea));
  }

  function getImageScale(image) {
    let widthRatio = image.naturalSize.width / image.width;
    let heightRatio = image.naturalSize.height / image.height;
    return Math.max(widthRatio, heightRatio);
  }

  // this is the last point element is a DOM element
  function getImages(domNodes) {
    let images = getAvailableImages(domNodes);

    let imagesInfoParent = getImagesInfoParent();

    return images.map(element => {
      let size = getSize(element);
      if (typeof size === 'number') {
        size = (size / 1024).toFixed(3);
      }

      let position = getElementTopLeft(element);

      // adjust position according to the overlay parent margin
      // see https://github.com/nccgroup/image-checker/issues/41
      if (imagesInfoParent.style.position === 'relative') {
        position.top -= imagesInfoParent.margin.top;
        position.left -= imagesInfoParent.margin.left;
      }

      return {
        url: getUrl(element),
        size: size,
        position: position,
        height: element.offsetHeight,
        width: element.offsetWidth,
        naturalSize: getNaturalSize(element)
      };
    }).filter(byVisibleImage);
  }

  function getAvailableImages(elementsArray) {
    return elementsArray
      .filter(byProbableImage)
      .filter(byHasUrl);
  }

  function byVisibleImage(image) {
    return (image.height && image.width &&
    typeof image.position.top === 'number' &&
    typeof image.position.left === 'number');
  }

  function byProbableImage(element) {
    let style = window.getComputedStyle(element);

    if (style.visibility === 'hidden') {
      return false;
    }

    if (element.tagName.toLowerCase() === 'img') {
      return true;
    }

    if (style.backgroundImage) {
      let urlMatcher = BACKGROUND_IMAGE_URL_REGEX.exec(style.backgroundImage);

      if (urlMatcher && urlMatcher.length > 1) {
        return true;
      }
    }

    return false;
  }

  function byHasUrl(element) {
    return !!getUrl(element);
  }

  function getElementTopLeft(elem) {
    let location = {
      top: 0,
      left: 0
    };
    if (elem.getBoundingClientRect) {
      let boundingClientRect = elem.getBoundingClientRect();
      location.top = window.scrollY + boundingClientRect.top;
      location.left = window.scrollX + boundingClientRect.left;
    }
    return location;
  }

  function getImagesInfoParentElement() {
    return document.getElementsByTagName('body')[0];
  }

  function getImagesInfoParent() {
    let parentElement = getImagesInfoParentElement();
    let style = getComputedStyle(parentElement);
    let boundingClientRect = parentElement.getBoundingClientRect();
    let position = {
      top: boundingClientRect.top,
      left: boundingClientRect.left
    };
    let margin = {
      top: boundingClientRect.top + window.scrollY,
      left: boundingClientRect.left + window.scrollX
    };

    return {
      position: position,
      margin: margin,
      style: style
    };
  }

  function getNaturalSize(element) {
    if (element.naturalWidth) {
      return {
        width: element.naturalWidth,
        height: element.naturalHeight
      };
    } else {
      let image = new Image();
      image.src = getUrl(element);

      return {
        width: image.naturalWidth,
        height: image.naturalHeight
      };
    }
  }

  function getSize(element) {
    let performanceEntrySize = performance.getEntriesByName(getUrl(element))[0];
    if (performanceEntrySize) {
      return performanceEntrySize.encodedBodySize;
    }
  }

  function getDuration(element) {
    let performanceEntryDuration = performance.getEntriesByName(element)[0];
    if (performanceEntryDuration) {
      return performanceEntryDuration.duration;
    }
  }

  function getUrl(element) {
    if (element.currentSrc) {
      return element.currentSrc;
    } else if (element.src) {
      return element.src;
    } else {
      let bkg = window.getComputedStyle(element).backgroundImage;
      let url = BACKGROUND_IMAGE_URL_REGEX.exec(bkg);
      if (url) {
        return url[1].replace(/["]/g, '');
      }
    }
  }

  function getName(s) {
    s = s.replace(/^.*[\\/]/, '');
    if (s.indexOf('?') > 0) {
      s = s.substr(0, s.indexOf('?'));
    }
    return s;
  }

  function getHost(s) {
    let url = new URL(s);
    return url.hostname;
  }

  function isSVG(s) {
    return SVG_URL_REGEX.test(s.url) || SVG_DATA_URI_REGEX.test(s.url);
  }

  function nodeListToArray(nodeList) {
    let array = [];

    for (let i = 0; i < nodeList.length; i += 1) {
      array[i] = nodeList[i];
    }

    return array;
  }

  window.NCC = window.NCC || {};

  window.NCC.imageChecker = {
    showImagesInfo: showImagesInfo,
    hideImagesInfo: hideImagesInfo,
    isImagesInfoActive: isImagesInfoActive,
    _getImages: getImages,
    _nodeListToArray: nodeListToArray,
    _getUrl: getUrl,
    _getSize: getSize,
    _getNaturalSize: getNaturalSize,
    _getImageCoverage: getImageCoverage,
    _getElementTopLeft: getElementTopLeft,
    _getAvailableImages: getAvailableImages,
    _getBackgroundColor: getBackgroundColor,
    _getImagesInfoParent: getImagesInfoParent
  };
}());
<|endoftext|>


<commit_before>define(function(require, exports, module) {
  "use strict";

  var app = require("app");

  // External dependencies.
  var Backbone = require("backbone");

  var VoluntaryWork = require("modules/voluntary-work");
  var Types = require("modules/types");
  var Municipalities = require("modules/municipalities");

  // Defining the application router.
  module.exports = Backbone.Router.extend({
    initialize: function() {

      this.voluntaryWorks = new VoluntaryWork.Collection();
      this.types = new Types.Collection();
      this.municipalities = new Municipalities.Collection();

      this.views = {

      };
    },

    routes: {
      "": "list",
      "list": "list",
      "form": "form",
      "view/:id": "form"
    },

    list: function() {

      // Empty existing itemlist
      //$('ul.itemList').html('');
      if (!this.views.voluntaryWorkList) {
        this.views.voluntaryWorkList = new VoluntaryWork.Views.List({collection: this.voluntaryWorks});
        //console.log('list route created');
      } else {
        this.views.voluntaryWorkList.show();
        //console.log('list route displayed');
      }
    },

    form: function(id) {
      // If "id" is not present, new item creation is triggered

      console.log('form route triggered');
      var collection = this.voluntaryWorks;

      $('#voluntaryWorkDetails').empty();

      var router = this;
      var constructs = {
        collection: router.voluntaryWorks,
        linkedItems: {
          types: router.types,
          municipalities: router.municipalities
        }
      };

      var dependencies = [];

      // If id -> then it's edit, so we have to fetch model
      if (id) {
        // TODO: first look in the collection
        constructs.model = new VoluntaryWork.Model({id: id});
        dependencies.push(constructs.model.fetch());
      }

      if (this.municipalities.length < 1) {
        dependencies.push(this.municipalities.fetch());
      }

      if (this.types.length < 1) {
        dependencies.push(this.types.fetch());
      }

      $.when.apply(this, dependencies).done(function() {
        var itemView = new VoluntaryWork.Views.Form(constructs).render();

        // TODO: DOM manipulation should be done in View instead of router
        $('#voluntaryWorkDetails').html(itemView.$el);
      });

      $('#voluntaryWorkList').hide();
      $('#voluntaryWorkDetails').show();

    }

  });
});<commit_msg>WIP router.js<commit_after>define([
    'jquery', 'underscore', 'backbone', 'layoutmanager', 'app',
    //'views/details', 'views/form', 'views/listitem', 'views/list',
    'models/voluntary-work',
    'collections/voluntary-works', 'collections/worktypes', 'collections/municipalities'
  ], function(
    $, _, Backbone, Layout, app,
    //DetailsView, EditView, ListView, ListItemView,
    VoluntaryWork,
    VoluntaryWorks, WorkTypes, Municipalities
  ) {
  "use strict";

  var VoluntaryWorkRouter = Backbone.Router.extend({
    routes: {
      "": "list",
      "list": "list",
      "form": "form",
      "view/:id": "form"
    },

    initialize: function() {

      app.voluntaryWorks = new VoluntaryWorks();
      app.types = new WorkTypes();
      app.municipalities = new Municipalities();

      this.views = {

      };

      // For testing
      var router = this;
      _.extend(window, {
        app: app,
        router: router,
        models: {
          VoluntaryWork: VoluntaryWork
        },
        collections: {
          VoluntaryWorks: VoluntaryWorks,
          Municipalities: Municipalities,
          Types: WorkTypes
        }
      });
    },

    list: function() {

    }

    // list: function() {

    //   // Empty existing itemlist
    //   //$('ul.itemList').html('');
    //   if (!this.views.voluntaryWorkList) {
    //     this.views.voluntaryWorkList = new VoluntaryWork.Views.List({collection: this.voluntaryWorks});
    //     //console.log('list route created');
    //   } else {
    //     this.views.voluntaryWorkList.show();
    //     //console.log('list route displayed');
    //   }
    // },

    // form: function(id) {
    //   // If "id" is not present, new item creation is triggered

    //   console.log('form route triggered');
    //   var collection = this.voluntaryWorks;

    //   $('#voluntaryWorkDetails').empty();

    //   var router = this;
    //   var constructs = {
    //     collection: router.voluntaryWorks,
    //     linkedItems: {
    //       types: router.types,
    //       municipalities: router.municipalities
    //     }
    //   };

    //   var dependencies = [];

    //   // If id -> then it's edit, so we have to fetch model
    //   if (id) {
    //     // TODO: first look in the collection
    //     constructs.model = new VoluntaryWork.Model({id: id});
    //     dependencies.push(constructs.model.fetch());
    //   }

    //   if (this.municipalities.length < 1) {
    //     dependencies.push(this.municipalities.fetch());
    //   }

    //   if (this.types.length < 1) {
    //     dependencies.push(this.types.fetch());
    //   }

    //   $.when.apply(this, dependencies).done(function() {
    //     var itemView = new VoluntaryWork.Views.Form(constructs).render();

    //     // TODO: DOM manipulation should be done in View instead of router
    //     $('#voluntaryWorkDetails').html(itemView.$el);
    //   });

    //   $('#voluntaryWorkList').hide();
    //   $('#voluntaryWorkDetails').show();

    // }

  });

  return VoluntaryWorkRouter;
});
<|endoftext|>