<commit_before>angular.module('pvta.services', ['ngResource'])

.factory('Avail', function () {
  return 'http://bustracker.pvta.com/infopoint/rest';
})

.factory('Vehicle', function ($resource, Avail) {
  return $resource(Avail + '/vehicles/get/:vehicleId');
})

.factory('Route', function ($resource, Avail) {
  return $resource(Avail + '/routedetails/get/:routeId');
})

.factory('Routes', function ($resource, Avail) {
  return $resource(Avail + '/routes/getvisibleroutes');
})

.factory('NearestStops', function ($resource, Avail) {
  return $resource(Avail + '/Stops/Nearest?latitude=:latitude&longitude=:longitude', {latitude: '@latitude', longitude: '@longitude'});
})

.factory('Stop', function ($resource, Avail) {
  return $resource(Avail + '/stops/get/:stopId');
})

.factory('Stops', function ($resource, Avail) {
  return $resource(Avail + '/stops/getallstops');
})

.factory('RouteVehicles', function ($resource, Avail) {
  return $resource(Avail + '/vehicles/getallvehiclesforroute?routeid=:id');
})

.factory('StopDeparture', function ($resource, Avail) {
  return $resource(Avail + '/stopdepartures/get/:stopId');
})

.factory('Messages', function ($resource, Avail) {
  return $resource(Avail + '/publicmessages/getcurrentmessages');
})

.factory('SimpleRoute', function ($resource, Avail) {
  return $resource(Avail + '/routes/get/:routeId');
})

.factory('Info', function () {
  return {

    versionNum: '0.6.0',
    versionName: 'Beta 3'
  };
})

.factory('StopList', function () {
  var stopsList = [];

  var pushEntireList = function (list) {
    stopsList = stopsList.concat(_.uniq(list, true, 'Name'));
    return stopsList;
  };

  var getEntireList = function () {
    if (stopsList !== undefined) {
      return stopsList;
    }
    else return 0;
  };

  var isEmpty = function () {
    if (stopsList.length === 0) return true;
    else return false;
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    isEmpty: isEmpty,
  };

})

.factory('RouteList', function () {
  var routesList = [];

  var pushEntireList = function (list) {
    // only store the route attributes we need
    routesList = _.map(list, function (route) {
      return _.pick(route, 'ShortName', 'LongName', 'Color', 'RouteId');
    });
    // sort routes by their number
    var routeNumber = /\d{1,2}/;
    routesList = _.sortBy(routesList, function (route) {
      matches = route.ShortName.match(routeNumber);
      return Number(_.first(matches));
    });
    return routesList;
  };

  var getEntireList = function () {
    if (!isEmpty()) {
      return routesList;
    }
    else return 0;
  };

  var isEmpty = function () {
    if (routesList.length == 0) return true;
    else return false;
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    isEmpty: isEmpty
  };

})

.factory('FavoriteRoutes', function () {
  var routes = [];
  var push = function (route) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      var newRoute = {RouteId: route.RouteId, LongName: route.LongName, ShortName: route.ShortName, Color: route.Color};
      if (routes) {
        routes.push(newRoute);
        localforage.setItem('favoriteRoutes', routes);
      }
      else {
        var favoriteRoutes = [newRoute];
        localforage.setItem('favoriteRoutes', favoriteRoutes);
      }
    });
  };

  var getAll = function () {
    return localforage.getItem('favoriteRoutes');
  };

  var remove = function (route) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      for (var i = 0; i < routes.length; i++) {
        if (routes[i].RouteId === route.RouteId) {
          routes.splice(i, 1);
        }
      }
      localforage.setItem('favoriteRoutes', routes, function (err, newRoutes) {
      });
    });
    removeOneRoute(route);
  };

  /* Checks to see if a route is included
   * in the favorites. Returns boolean.
   */
  function contains (route, cb) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      if (routes) {
        var r = _.where(routes, {RouteId: route.RouteId});
        if (r.length > 0) {
          cb(true);
        }
        else {
          cb(false);
        }
      }
      else {
        cb(false);
      }
    });
  }

  return {
    push: push,
    getAll: getAll,
    remove: remove,
    contains: contains
  };
})

.factory('FavoriteStops', function () {
  var stops = [];
  var push = function (stop) {
    localforage.getItem('favoriteStops', function (err, stops) {
      var newStop = {StopId: stop.StopId, Name: stop.Name};
      if (stops) {
        stops.push(newStop);
        localforage.setItem('favoriteStops', stops);
      }
      else {
        var favoriteStops = [newStop];
        localforage.setItem('favoriteStops', favoriteStops);
      }
    });
  };

  var getAll = function () {
    var ret = [];
    localforage.getItem('favoriteStops', function (err, value) {
    });
  };

  var remove = function (stop) {
    localforage.getItem('favoriteStops', function (err, stops) {
      for (var i = 0; i < stops.length; i++) {
        if (stops[i].StopId === stop.StopId) {
          stops.splice(i, 1);
        }
      }
      localforage.setItem('favoriteStops', stops, function (err, newStops) {
      });
    });
  };

  function contains (stop, cb) {
    localforage.getItem('favoriteStops', function (err, stops) {
      if (stops) {
        var r = _.where(stops, {StopId: stop.StopId});
        if (r.length > 0) {
          cb(true);
        }
        else {
          cb(false);
        }
      }
      else {
        cb(false);
      }
    });
  }
  return {
    push: push,
    getAll: getAll,
    remove: remove,
    contains: contains
  };
})

.factory('Trips', function () {
  var trips = [];
  var loadedTrip = null;
  var lastPoppedIndex = 0;
  var push = function (index) {
    lastPoppedIndex = index;
    loadedTrip = trips[index];
  };

  var pop = function () {
    toReturn = loadedTrip;
    loadedTrip = null;
    return toReturn;
  };

  var getAll =  function (callback) {
    localforage.getItem('savedTrips', function (err, value) {
      if (err) {
        console.log('Error loading trips.');
        callback([]);
        return;
      }
      if (value === null) {
        console.log('No trips loaded');
        callback([]);
        return;
      }
      trips = JSON.parse(value);
      for (var i = 0; i < trips.length; i = i + 1) {
        trips[i].time.datetime = new Date(trips[i].time.datetime);
      }
      callback(trips);
    });
  };

  var pop = function () {
    var toReturn = loadedTrip;
    loadedTrip = null;
    return toReturn;
  };

  var push = function (index) {
    loadedTrip = trips[index];
  };

  var set = function (trip) {//Sets a new trip object at the index of the last trip popped
    trips[lastPoppedIndex] = trip;
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  var add = function (trip) {
    trips.push(trip);
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  var remove = function (index) {
    trips.splice(index, 1);
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  return {
    getAll: getAll,
    push: push,
    pop: pop,
    set: set,
    add: add,
    remove: remove
  };
})

.factory('KML', function () {
  var kml = [];
  function push (shortName) {
    kml.push(shortName);
  }
  function pop () {
    if (kml.length == 1) {
      return kml.pop();
    }
    else {
      // Empty the array,
      // because anything else
      // will produce undesired
      // activity in MapController
      kml = [];
      return null;
    }
  }
  return {
    push: push,
    pop: pop
  };
})

.service('LatLong', function () {
  var latlong = [];
  return {
    push: function (lat, long) {
      var p = {lat: lat, long: long};
      latlong.push(p);
    },
    getAll: function () {
      if (latlong.length > 0) {
    var toReturn = latlong;
    latlong = [];
    return toReturn;
  }
      else {
    return null;
  }
    }
  };
})

.factory('Map', function ($cordovaGeolocation) {

  var map;
  var bounds;
  var currentLocation;
  var options = {timeout: 5000, enableHighAccuracy: true};

  function placeDesiredMarker (location, icon) {
    var neededMarker = new google.maps.Marker({
      map: map,
      icon: icon,
      animation: google.maps.Animation.DROP,
      position: location
    });
    bounds.extend(location);
    map.fitBounds(bounds);
    return neededMarker;
  }

  function plotCurrentLocation (cb) {
    $cordovaGeolocation.getCurrentPosition(options).then(function (position) {
      currentLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      addMapListener(placeDesiredMarker(currentLocation, 'http://www.google.com/mapfiles/kml/paddle/red-circle.png'),
        '
You are here!
');
      if (cb) { cb(currentLocation); }
    }, function () {});
    return currentLocation;
  }

  var windows = [];
  function addMapListener (marker, onClick) {
    google.maps.event.addListener(marker, 'click', function () {
      //this auto-closes any bubbles that may already be open
      //when you open another one, so that only one bubble can
      //be open at once
      _.each(windows, function (window) {
        window.close();
        windows.pop(window);
      });
      //infobubble is a utility class that is
      //much more styleable than Google's InfoWindow.
      //source located in www/bower_components/js-info-bubble
      var infoWindow = new google.maps.InfoWindow({
        content: onClick
      });
      windows.push(infoWindow);
      infoWindow.open(map, marker);
    });
  }

  function addKML (fileName) {
    var toAdd = 'http://bustracker.pvta.com/infopoint/Resources/Traces/' + fileName;
    var georssLayer = new google.maps.KmlLayer({
      url: toAdd
    });
    georssLayer.setMap(map);
  }

  return {
    placeDesiredMarker: placeDesiredMarker,
    init: function (incomingMap, incomingBounds) {
      map = incomingMap;
      bounds = incomingBounds;
    },
    plotCurrentLocation: plotCurrentLocation,
    addMapListener: addMapListener,
    addKML: addKML
  };
})

.factory('Recent', function (moment) {
  function recent (timestamp) {
    var now = moment();
    var diff = now.diff(timestamp, 'days');
    if (diff <= 5) return true;
    else return false;
  }
  return {
    recent: recent
  };
})

.factory('RouteForage', function (RouteList, moment, Recent, Routes, $q) {
  function getRouteList () {
    if (RouteList.isEmpty()) {
      return localforage.getItem('routes').then(function (routes) {
        if ((routes != null) && (routes.list.length > 0) && (Recent.recent(routes.time))) {
          return routes.list;
        }
        else {
          return Routes.query().$promise;
        }
      });
    }
    else return $q.when(RouteList.getEntireList());
  }
  function saveRouteList (list) {
    if (RouteList.isEmpty()) {
      RouteList.pushEntireList(list);
    }
    pushListToForage(list);
  }
  function pushListToForage (routes) {
    var toForage = {
      list: routes,
      time: moment()
    };
    localforage.setItem('routes', toForage, function (err, val) {if (err) console.log('localforage routes saving error: ' + err);});
  }
  return {
    get: getRouteList,
    save: saveRouteList
  };
})

.factory('StopsForage', function (StopList, Recent, Stops, NearestStops, $q) {
  function getStopList (lat, long) {
    if (StopList.isEmpty()) {
      return localforage.getItem('stops').then(function (stops) {
        if ((stops != null) && (stops.list.length > 0) && (Recent.recent(stops.time))) {
          return stops.list;
        }
        else {
          if (lat && long) {
            return NearestStops.query({latitude: lat, longitude: long}).$promise;
          }
          else {
            return Stops.query().$promise;
          }
        }
      });
    }
    else return $q.when(StopList.getEntireList());
  }
  function saveStopList (list) {
    if (StopList.isEmpty()) {
      StopList.pushEntireList(list);
    }
    pushListToForage(list);
  }
  function pushListToForage (stops) {
    var toForage = {
      list: stops,
      time: moment()
    };
    localforage.setItem('stops', toForage, function (err, val) {if (err)console.log('localforage stops saving error: ' + err); else console.log('done');});
  }
  function uniq (stops) {
    return _.uniq(stops, false, function (stop) {
      return stop.StopId;
    });
  }
  return {
    get: getStopList,
    save: saveStopList,
    uniq: uniq
  };
});
<commit_msg>linting<commit_after>angular.module('pvta.services', ['ngResource'])

.factory('Avail', function () {
  return 'http://bustracker.pvta.com/infopoint/rest';
})

.factory('Vehicle', function ($resource, Avail) {
  return $resource(Avail + '/vehicles/get/:vehicleId');
})

.factory('Route', function ($resource, Avail) {
  return $resource(Avail + '/routedetails/get/:routeId');
})

.factory('Routes', function ($resource, Avail) {
  return $resource(Avail + '/routes/getvisibleroutes');
})

.factory('NearestStops', function ($resource, Avail) {
  return $resource(Avail + '/Stops/Nearest?latitude=:latitude&longitude=:longitude', {latitude: '@latitude', longitude: '@longitude'});
})

.factory('Stop', function ($resource, Avail) {
  return $resource(Avail + '/stops/get/:stopId');
})

.factory('Stops', function ($resource, Avail) {
  return $resource(Avail + '/stops/getallstops');
})

.factory('RouteVehicles', function ($resource, Avail) {
  return $resource(Avail + '/vehicles/getallvehiclesforroute?routeid=:id');
})

.factory('StopDeparture', function ($resource, Avail) {
  return $resource(Avail + '/stopdepartures/get/:stopId');
})

.factory('Messages', function ($resource, Avail) {
  return $resource(Avail + '/publicmessages/getcurrentmessages');
})

.factory('SimpleRoute', function ($resource, Avail) {
  return $resource(Avail + '/routes/get/:routeId');
})

.factory('Info', function () {
  return {

    versionNum: '0.6.0',
    versionName: 'Beta 3'
  };
})

.factory('StopList', function () {
  var stopsList = [];

  var pushEntireList = function (list) {
    stopsList = stopsList.concat(_.uniq(list, true, 'Name'));
    return stopsList;
  };

  var getEntireList = function () {
    if (stopsList !== undefined) {
      return stopsList;
    }
    else return 0;
  };

  var isEmpty = function () {
    if (stopsList.length === 0) return true;
    else return false;
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    isEmpty: isEmpty,
  };

})

.factory('RouteList', function () {
  var routesList = [];

  var pushEntireList = function (list) {
    // only store the route attributes we need
    routesList = _.map(list, function (route) {
      return _.pick(route, 'ShortName', 'LongName', 'Color', 'RouteId');
    });
    // sort routes by their number
    var routeNumber = /\d{1,2}/;
    routesList = _.sortBy(routesList, function (route) {
      matches = route.ShortName.match(routeNumber);
      return Number(_.first(matches));
    });
    return routesList;
  };

  var getEntireList = function () {
    if (!isEmpty()) {
      return routesList;
    }
    else return 0;
  };

  var isEmpty = function () {
    if (routesList.length == 0) return true;
    else return false;
  };

  return {
    pushEntireList: pushEntireList,
    getEntireList: getEntireList,
    isEmpty: isEmpty
  };

})

.factory('FavoriteRoutes', function () {
  var routes = [];
  var push = function (route) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      var newRoute = {RouteId: route.RouteId, LongName: route.LongName, ShortName: route.ShortName, Color: route.Color};
      if (routes) {
        routes.push(newRoute);
        localforage.setItem('favoriteRoutes', routes);
      }
      else {
        var favoriteRoutes = [newRoute];
        localforage.setItem('favoriteRoutes', favoriteRoutes);
      }
    });
  };

  var getAll = function () {
    return localforage.getItem('favoriteRoutes');
  };

  var remove = function (route) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      for (var i = 0; i < routes.length; i++) {
        if (routes[i].RouteId === route.RouteId) {
          routes.splice(i, 1);
        }
      }
      localforage.setItem('favoriteRoutes', routes, function (err, newRoutes) {
      });
    });
    removeOneRoute(route);
  };

  /* Checks to see if a route is included
   * in the favorites. Returns boolean.
   */
  function contains (route, cb) {
    localforage.getItem('favoriteRoutes', function (err, routes) {
      if (routes) {
        var r = _.where(routes, {RouteId: route.RouteId});
        if (r.length > 0) {
          cb(true);
        }
        else {
          cb(false);
        }
      }
      else {
        cb(false);
      }
    });
  }

  return {
    push: push,
    getAll: getAll,
    remove: remove,
    contains: contains
  };
})

.factory('FavoriteStops', function () {
  var stops = [];
  var push = function (stop) {
    localforage.getItem('favoriteStops', function (err, stops) {
      var newStop = {StopId: stop.StopId, Name: stop.Name};
      if (stops) {
        stops.push(newStop);
        localforage.setItem('favoriteStops', stops);
      }
      else {
        var favoriteStops = [newStop];
        localforage.setItem('favoriteStops', favoriteStops);
      }
    });
  };

  var getAll = function () {
    var ret = [];
    localforage.getItem('favoriteStops', function (err, value) {
    });
  };

  var remove = function (stop) {
    localforage.getItem('favoriteStops', function (err, stops) {
      for (var i = 0; i < stops.length; i++) {
        if (stops[i].StopId === stop.StopId) {
          stops.splice(i, 1);
        }
      }
      localforage.setItem('favoriteStops', stops, function (err, newStops) {
      });
    });
  };

  function contains (stop, cb) {
    localforage.getItem('favoriteStops', function (err, stops) {
      if (stops) {
        var r = _.where(stops, {StopId: stop.StopId});
        if (r.length > 0) {
          cb(true);
        }
        else {
          cb(false);
        }
      }
      else {
        cb(false);
      }
    });
  }
  return {
    push: push,
    getAll: getAll,
    remove: remove,
    contains: contains
  };
})

.factory('Trips', function () {
  var trips = [];
  var loadedTrip = null;
  var lastPoppedIndex = 0;
  var push = function (index) {
    lastPoppedIndex = index;
    loadedTrip = trips[index];
  };

  var pop = function () {
    toReturn = loadedTrip;
    loadedTrip = null;
    return toReturn;
  };

  var getAll =  function (callback) {
    localforage.getItem('savedTrips', function (err, value) {
      if (err) {
        console.log('Error loading trips.');
        callback([]);
        return;
      }
      if (value === null) {
        console.log('No trips loaded');
        callback([]);
        return;
      }
      trips = JSON.parse(value);
      for (var i = 0; i < trips.length; i = i + 1) {
        trips[i].time.datetime = new Date(trips[i].time.datetime);
      }
      callback(trips);
    });
  };

  var pop = function () {
    var toReturn = loadedTrip;
    loadedTrip = null;
    return toReturn;
  };

  var push = function (index) {
    loadedTrip = trips[index];
  };

  var set = function (trip) {//Sets a new trip object at the index of the last trip popped
    trips[lastPoppedIndex] = trip;
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  var add = function (trip) {
    trips.push(trip);
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  var remove = function (index) {
    trips.splice(index, 1);
    localforage.setItem('savedTrips', JSON.stringify(trips), function (err, value) {
      if (err !== null) console.log('Error saving trips.');
    });
  };

  return {
    getAll: getAll,
    push: push,
    pop: pop,
    set: set,
    add: add,
    remove: remove
  };
})

.factory('KML', function () {
  var kml = [];
  function push (shortName) {
    kml.push(shortName);
  }
  function pop () {
    if (kml.length == 1) {
      return kml.pop();
    }
    else {
      // Empty the array,
      // because anything else
      // will produce undesired
      // activity in MapController
      kml = [];
      return null;
    }
  }
  return {
    push: push,
    pop: pop
  };
})

.service('LatLong', function () {
  var latlong = [];
  return {
    push: function (lat, long) {
      var p = {lat: lat, long: long};
      latlong.push(p);
    },
    getAll: function () {
      if (latlong.length > 0) {
        var toReturn = latlong;
        latlong = [];
        return toReturn;
      }
      else {
        return null;
      }
    }
  };
})

.factory('Map', function ($cordovaGeolocation) {

  var map;
  var bounds;
  var currentLocation;
  var options = {timeout: 5000, enableHighAccuracy: true};

  function placeDesiredMarker (location, icon) {
    var neededMarker = new google.maps.Marker({
      map: map,
      icon: icon,
      animation: google.maps.Animation.DROP,
      position: location
    });
    bounds.extend(location);
    map.fitBounds(bounds);
    return neededMarker;
  }

  function plotCurrentLocation (cb) {
    $cordovaGeolocation.getCurrentPosition(options).then(function (position) {
      currentLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
      addMapListener(placeDesiredMarker(currentLocation, 'http://www.google.com/mapfiles/kml/paddle/red-circle.png'),
        '
You are here!
');
      if (cb) { cb(currentLocation); }
    }, function () {});
    return currentLocation;
  }

  var windows = [];
  function addMapListener (marker, onClick) {
    google.maps.event.addListener(marker, 'click', function () {
      //this auto-closes any bubbles that may already be open
      //when you open another one, so that only one bubble can
      //be open at once
      _.each(windows, function (window) {
        window.close();
        windows.pop(window);
      });
      //infobubble is a utility class that is
      //much more styleable than Google's InfoWindow.
      //source located in www/bower_components/js-info-bubble
      var infoWindow = new google.maps.InfoWindow({
        content: onClick
      });
      windows.push(infoWindow);
      infoWindow.open(map, marker);
    });
  }

  function addKML (fileName) {
    var toAdd = 'http://bustracker.pvta.com/infopoint/Resources/Traces/' + fileName;
    var georssLayer = new google.maps.KmlLayer({
      url: toAdd
    });
    georssLayer.setMap(map);
  }

  return {
    placeDesiredMarker: placeDesiredMarker,
    init: function (incomingMap, incomingBounds) {
      map = incomingMap;
      bounds = incomingBounds;
    },
    plotCurrentLocation: plotCurrentLocation,
    addMapListener: addMapListener,
    addKML: addKML
  };
})

.factory('Recent', function (moment) {
  function recent (timestamp) {
    var now = moment();
    var diff = now.diff(timestamp, 'days');
    if (diff <= 5) return true;
    else return false;
  }
  return {
    recent: recent
  };
})

.factory('RouteForage', function (RouteList, moment, Recent, Routes, $q) {
  function getRouteList () {
    if (RouteList.isEmpty()) {
      return localforage.getItem('routes').then(function (routes) {
        if ((routes != null) && (routes.list.length > 0) && (Recent.recent(routes.time))) {
          return routes.list;
        }
        else {
          return Routes.query().$promise;
        }
      });
    }
    else return $q.when(RouteList.getEntireList());
  }
  function saveRouteList (list) {
    if (RouteList.isEmpty()) {
      RouteList.pushEntireList(list);
    }
    pushListToForage(list);
  }
  function pushListToForage (routes) {
    var toForage = {
      list: routes,
      time: moment()
    };
    localforage.setItem('routes', toForage, function (err, val) {if (err) console.log('localforage routes saving error: ' + err);});
  }
  return {
    get: getRouteList,
    save: saveRouteList
  };
})

.factory('StopsForage', function (StopList, Recent, Stops, NearestStops, $q) {
  function getStopList (lat, long) {
    if (StopList.isEmpty()) {
      return localforage.getItem('stops').then(function (stops) {
        if ((stops != null) && (stops.list.length > 0) && (Recent.recent(stops.time))) {
          return stops.list;
        }
        else {
          if (lat && long) {
            return NearestStops.query({latitude: lat, longitude: long}).$promise;
          }
          else {
            return Stops.query().$promise;
          }
        }
      });
    }
    else return $q.when(StopList.getEntireList());
  }
  function saveStopList (list) {
    if (StopList.isEmpty()) {
      StopList.pushEntireList(list);
    }
    pushListToForage(list);
  }
  function pushListToForage (stops) {
    var toForage = {
      list: stops,
      time: moment()
    };
    localforage.setItem('stops', toForage, function (err, val) {if (err)console.log('localforage stops saving error: ' + err); else console.log('done');});
  }
  function uniq (stops) {
    return _.uniq(stops, false, function (stop) {
      return stop.StopId;
    });
  }
  return {
    get: getStopList,
    save: saveStopList,
    uniq: uniq
  };
});
<|endoftext|>


<commit_before>/*
 * grunt-jsinspect
 *
 * Copyright (c) Stefan Judis and Juga Paazmaya
 * Licensed under the MIT license.
 */

'use strict';

var Inspector = require('jsinspect/lib/inspector');
var Reporter = require('jsinspect/lib/reporters');

module.exports = function(grunt) {

  grunt.registerMultiTask('jsinspect', 'Grunt task for jsinspect', function() {
    var done = this.async();
    var taskSucceeded = true;

    var options = this.options({
      threshold:   30,
      diff:        true,
      identifiers: false,
      failOnMatch: true,
      suppress:    100,
      reporter:    'default'
    });

    var inspector = new Inspector(this.filesSrc, {
      threshold:   options.threshold,
      diff:        options.diff,
      identifiers: options.identifiers
    });

    if (!Reporter.hasOwnProperty(options.reporter) ||
        typeof Reporter[options.reporter] !== 'function') {
      grunt.log.error('Sorry but the configured reporter "' + options.reporter +
        '" does not exist, thus exiting');
      done(false);

      return;
    }

    this.reporterType = new Reporter[options.reporter](inspector, {
      diff: options.diff,
      suppress: options.suppress
    });

    if (options.failOnMatch) {
      inspector.on('match', function() {
        taskSucceeded = false;
      });
    }

    inspector.on('end', function() {
      done(taskSucceeded);
    });

    inspector.run();

  });
};
<commit_msg>Handle failOnMatch threshold<commit_after>/*
 * grunt-jsinspect
 *
 * Copyright (c) Stefan Judis and Juga Paazmaya
 * Licensed under the MIT license.
 */

'use strict';

var Inspector = require('jsinspect/lib/inspector');
var Reporter = require('jsinspect/lib/reporters');

module.exports = function(grunt) {

  grunt.registerMultiTask('jsinspect', 'Grunt task for jsinspect', function() {
    var done = this.async();
    var taskSucceeded = true;
    var nbOfMatches = 0;

    var options = this.options({
      threshold:   30,
      diff:        true,
      identifiers: false,
      failOnMatch: true,
      suppress:    100,
      reporter:    'default'
    });

    var inspector = new Inspector(this.filesSrc, {
      threshold:   options.threshold,
      diff:        options.diff,
      identifiers: options.identifiers
    });

    if (!Reporter.hasOwnProperty(options.reporter) ||
        typeof Reporter[options.reporter] !== 'function') {
      grunt.log.error('Sorry but the configured reporter "' + options.reporter +
        '" does not exist, thus exiting');
      done(false);

      return;
    }

    this.reporterType = new Reporter[options.reporter](inspector, {
      diff: options.diff,
      suppress: options.suppress
    });

    if (typeof options.failOnMatch === 'number') {
      // Handle failOnMatch threshold.
      inspector.on('match', function() {
        nbOfMatches++;
        if (nbOfMatches >= options.failOnMatch) {
          taskSucceeded = false;
        }
      });
    } else if (options.failOnMatch) {
      // Handle failOnMatch boolean
      inspector.on('match', function() {
        taskSucceeded = false;
      });
    }

    inspector.on('end', function() {
      done(taskSucceeded);
    });

    inspector.run();

  });
};
<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs-extra');
var grunt = require('grunt');

function Utils(options) {
  this.options = options;
}

Utils.prototype.getListDependencies = function() {
  var options = this.options;
  var bower = [];

  if (fs.existsSync(options.bowerFile)) {
    var bowerFile = path.join(process.cwd(), options.bowerFile);
    var bowerJson = grunt.file.readJSON(bowerFile);

    if (options.dependencies) {
      bower = bower.concat(Object.keys(bowerJson.dependencies));
    }

    if (options.devDependencies) {
      bower = bower.concat(Object.keys(bowerJson.devDependencies));
    }

    if (options.peerDependencies) {
      bower = bower.concat(Object.keys(bowerJson.peerDependencies));
    }
  } else {
    grunt.log.warn('File bower.json was not found.');
  }

  return bower;
};

module.exports = Utils;
<commit_msg>use grunt.file.exists<commit_after>var path = require('path');
var grunt = require('grunt');

function Utils(options) {
  this.options = options;
}

Utils.prototype.getListDependencies = function() {
  var options = this.options;
  var bower = [];

  if (grunt.file.exists(options.bowerFile)) {
    var bowerFile = path.join(process.cwd(), options.bowerFile);
    var bowerJson = grunt.file.readJSON(bowerFile);

    if (options.dependencies) {
      bower = bower.concat(Object.keys(bowerJson.dependencies));
    }

    if (options.devDependencies) {
      bower = bower.concat(Object.keys(bowerJson.devDependencies));
    }

    if (options.peerDependencies) {
      bower = bower.concat(Object.keys(bowerJson.peerDependencies));
    }
  } else {
    grunt.log.warn('File bower.json was not found.');
  }

  return bower;
};

module.exports = Utils;
<|endoftext|>


<commit_before>/*
 * grunt-phantomas
 * https://github.com/stefanjudis/grunt-phantomas
 *
 * Copyright (c) 2013 stefan judis
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function( grunt ) {
  grunt.registerMultiTask(
    'phantomas',
    'Get metrics of your site and compare them.',
    function() {
      var done      = this.async();
      var options   = this.options( {
        additionStylesheet : false,
        indexPath          : './phantomas/',
        numberOfRuns       : 5,
        options            : {},
        url                : 'http://gruntjs.com/',
        buildUi            : true,
        group              : {
          'REQUESTS' : [
            'requests',
            'gzipRequests',
            'postRequests',
            'httpsRequests',
            'notFound',
            'multipleRequests',
            'maxRequestsPerDomain',
            'domains',
            'medianRequestsPerDomain',
            'redirects',
            'redirectsTime',
            'smallestResponse',
            'biggestResponse',
            'smallestLatency',
            'biggestLatency',
            'medianResponse',
            'medianLatency',
            'assetsNotGzipped',
            'assetsWithQueryString',
            'smallImages'
          ],
          'TIMINGS' : [
            'timeToFirstByte',
            'timeToLastByte',
            'timeToFirstCss',
            'timeToFirstJs',
            'timeToFirstImage',
            'fastestResponse',
            'slowestResponse',
            'onDOMReadyTime',
            'onDOMReadyTimeEnd',
            'windowOnLoadTime',
            'windowOnLoadTimeEnd',
            'httpTrafficCompleted',
            'timeBackend',
            'timeFrontend'
          ],
          'HTML' : [
            'bodyHTMLSize',
            'iframesCount',
            'imagesWithoutDimensions',
            'commentsSize',
            'hiddenContentSize',
            'whiteSpacesSize',
            'DOMelementsCount',
            'DOMelementMaxDepth',
            'nodesWithInlineCSS',
            'foo'
          ],
          'JAVASCRIPT' : [
            'eventsBound',
            'documentWriteCalls',
            'evalCalls',
            'jsErrors',
            'consoleMessages',
            'windowAlerts',
            'windowConfirms',
            'windowPrompts',
            'globalVariables',
            'localStorageEntries',
            'ajaxRequests'
          ],
          'DOM' : [
            'DOMqueries',
            'DOMqueriesById',
            'DOMqueriesByClassName',
            'DOMqueriesByTagName',
            'DOMqueriesByQuerySelectorAll',
            'DOMinserts',
            'DOMqueriesDuplicated'
          ],
          'HEADERS' : [
            'headersCount',
            'headersSentCount',
            'headersRecvCount',
            'headersSize',
            'headersSentSize',
            'headersRecvSize'
          ],
          'CACHING' : [
            'cacheHits',
            'cacheMisses',
            'cachePasses',
            'cachingNotSpecified',
            'cachingTooShort',
            'cachingDisabled'
          ],
          'COOKIES' : [
            'cookiesSent',
            'cookiesRecv',
            'domainsWithCookies',
            'documentCookiesLength',
            'documentCookiesCount'
          ],
          'COUNTS & SIZES' : [
            'contentLength',
            'bodySize',
            'htmlSize',
            'htmlCount',
            'cssSize',
            'cssCount',
            'jsSize',
            'jsCount',
            'jsonSize',
            'jsonCount',
            'imageSize',
            'imageCount',
            'webfontSize',
            'webfontCount',
            'base64Size',
            'base64Count',
            'otherCount',
            'otherSize'
          ],
          'JQUERY' : [
            'jQueryOnDOMReadyFunctions',
            'jQuerySizzleCalls'
          ]
        }
      } );
      var Phantomas = require(
                        './lib/phantomas'
                      );

      // let's kick things off
      var phantomas = new Phantomas( grunt, options, done );
      phantomas.kickOff();
    }
  );
};
<commit_msg>reordered default properties<commit_after>/*
 * grunt-phantomas
 * https://github.com/stefanjudis/grunt-phantomas
 *
 * Copyright (c) 2013 stefan judis
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function( grunt ) {
  grunt.registerMultiTask(
    'phantomas',
    'Get metrics of your site and compare them.',
    function() {
      var done      = this.async();
      var options   = this.options( {
        additionStylesheet : false,
        buildUi            : true,
        indexPath          : './phantomas/',
        numberOfRuns       : 5,
        options            : {},
        output             : 'json',
        url                : 'http://gruntjs.com/',
        group              : {
          'REQUESTS' : [
            'requests',
            'gzipRequests',
            'postRequests',
            'httpsRequests',
            'notFound',
            'multipleRequests',
            'maxRequestsPerDomain',
            'domains',
            'medianRequestsPerDomain',
            'redirects',
            'redirectsTime',
            'smallestResponse',
            'biggestResponse',
            'smallestLatency',
            'biggestLatency',
            'medianResponse',
            'medianLatency',
            'assetsNotGzipped',
            'assetsWithQueryString',
            'smallImages'
          ],
          'TIMINGS' : [
            'timeToFirstByte',
            'timeToLastByte',
            'timeToFirstCss',
            'timeToFirstJs',
            'timeToFirstImage',
            'fastestResponse',
            'slowestResponse',
            'onDOMReadyTime',
            'onDOMReadyTimeEnd',
            'windowOnLoadTime',
            'windowOnLoadTimeEnd',
            'httpTrafficCompleted',
            'timeBackend',
            'timeFrontend'
          ],
          'HTML' : [
            'bodyHTMLSize',
            'iframesCount',
            'imagesWithoutDimensions',
            'commentsSize',
            'hiddenContentSize',
            'whiteSpacesSize',
            'DOMelementsCount',
            'DOMelementMaxDepth',
            'nodesWithInlineCSS',
            'foo'
          ],
          'JAVASCRIPT' : [
            'eventsBound',
            'documentWriteCalls',
            'evalCalls',
            'jsErrors',
            'consoleMessages',
            'windowAlerts',
            'windowConfirms',
            'windowPrompts',
            'globalVariables',
            'localStorageEntries',
            'ajaxRequests'
          ],
          'DOM' : [
            'DOMqueries',
            'DOMqueriesById',
            'DOMqueriesByClassName',
            'DOMqueriesByTagName',
            'DOMqueriesByQuerySelectorAll',
            'DOMinserts',
            'DOMqueriesDuplicated'
          ],
          'HEADERS' : [
            'headersCount',
            'headersSentCount',
            'headersRecvCount',
            'headersSize',
            'headersSentSize',
            'headersRecvSize'
          ],
          'CACHING' : [
            'cacheHits',
            'cacheMisses',
            'cachePasses',
            'cachingNotSpecified',
            'cachingTooShort',
            'cachingDisabled'
          ],
          'COOKIES' : [
            'cookiesSent',
            'cookiesRecv',
            'domainsWithCookies',
            'documentCookiesLength',
            'documentCookiesCount'
          ],
          'COUNTS & SIZES' : [
            'contentLength',
            'bodySize',
            'htmlSize',
            'htmlCount',
            'cssSize',
            'cssCount',
            'jsSize',
            'jsCount',
            'jsonSize',
            'jsonCount',
            'imageSize',
            'imageCount',
            'webfontSize',
            'webfontCount',
            'base64Size',
            'base64Count',
            'otherCount',
            'otherSize'
          ],
          'JQUERY' : [
            'jQueryOnDOMReadyFunctions',
            'jQuerySizzleCalls'
          ]
        }
      } );
      var Phantomas = require(
                        './lib/phantomas'
                      );

      // let's kick things off
      var phantomas = new Phantomas( grunt, options, done );
      phantomas.kickOff();
    }
  );
};
<|endoftext|>


<commit_before>var Cic =
{
	canvas: undefined,

	info: undefined,

	graph: undefined,

	init: function ()
	{
		Cic.canvas = document.getElementById("cic");
		Cic.info = document.getElementById("cic_info");

		$jit.RGraph.Plot.NodeTypes.implement({
			'image': {
				'render': function(node, canvas) {
					var ctx = canvas.getCtx();
					var img = new Image();
					var pos = node.pos.getc(true);
					img.src = 'images/' + node.data.img_file;
					ctx.drawImage(img,pos.x-32,pos.y-70);
				},
				'contains': function(node, mousePos) {
						window.alert("skdfj");
                        var nodePos = node.pos.getc(true);
                        if(mousePos.x >=  (nodePos.x - node.data.width/2) &&
                                mousePos.x <= (nodePos.x + node.data.width/2) &&
                                mousePos.y >= (nodePos.y - node.data.height/2) &&
                                mousePos.y <= (nodePos.y + node.data.height/2) ) {
										window.alert("!!");
                                        return true;
                        }

                        return false;
                }
			}
		});

		Cic.graph = new $jit.RGraph(
			{
				injectInto: Cic.canvas,
				width: 600,
				height: 560,

				Edge:
				{
					color: $jit.util.rgbToHex([0, 100, 200])
				},

				Events:
				{
					enable: true,
					enableForEdges: true,
					onRightClick: function (obj, info, event)
					{
						var originNode = Cic.graph.graph.getClosestNodeToOrigin();

						if (obj.id != originNode.id)
						{
							var adjacence = Cic.graph.graph.getAdjacence(obj.id, originNode.id);

							if (adjacence && !adjacence.data.hidden && adjacence.data.html)
							{
								Cic.info.innerHTML = "
" + adjacence.nodeFrom.name + " ↔ " +
									adjacence.nodeTo.name + "
\n
Initiatives
";
								Cic.info.innerHTML += adjacence.data.html;
							}
							else
							{
								Cic.info.innerHTML = "";
							}
						}
					},

					onMouseEnter: function (obj, info, event)
					{
						Cic.canvas.style.cursor = "pointer";
					},

					onMouseLeave: function (obj, info, event)
					{
						Cic.canvas.style.cursor = "";
					},

					onClick: function (obj, info, event)
					{
						var closeNode = Cic.graph.graph.getClosestNodeToOrigin();
						if (closeNode.contains)
						{
							window.alert("aksdfj");
						}
					}
				},

				Navigation:
				{
					enable: true,
					panning: false,
					zooming: false,
				},

				Node:
				{
					type: "image",
					overridable: true
				},

				onBeforePlotLine: function (adj)
				{
					adj.Edge.color = (adj.data.weight == 1.0) ?
							$jit.util.rgbToHex([0, 150, 0]) :
							$jit.util.rgbToHex([200, 0, 0]);
					adj.Edge.lineWidth=2;
					if (adj.data.hidden)
					{
						adj.Edge.alpha = 0;
					}
					else if ((adj.nodeFrom._depth > 0) && (adj.nodeTo._depth > 0))
					{
						adj.Edge.alpha = .2;
					}
					else
					{
						adj.Edge.alpha = 1;
					}
				},

				onBeforePlotNode: function (node)
				{
					// TODO: figure out node alpha dynamic setting
				},

				onCreateLabel: function(element, node)
				{
					element.innerHTML = "
" + node.name + "
";

					element.onclick = function ()
					{
						Cic.graph.onClick(node.id);
					}

				},

				onPlaceLabel: function(element, node)
				{
					element.style.height = '90px';
					top_string = element.style.top;
					top_number = top_string.substring(0,top_string.indexOf('px'));
					element.style.top = (top_number - 70) + 'px';
				}
			}
		);
		$jit.RGraph.Label.HTML.implement({
                       placeLabel: function(tag, node, controller){
                               var pos = node.pos.getc(true),
                               canvas = this.viz.canvas,
                               ox = canvas.translateOffsetX,
                               oy = canvas.translateOffsetY,
                               sx = canvas.scaleOffsetX,
                               sy = canvas.scaleOffsetY,
                               radius = canvas.getSize();
                               var labelPos = {
                                       x: Math.round(pos.x * sx + ox + radius.width / 2.4), /* modified from "width / 2" to center label text-alignment */
                                       y: Math.round(pos.y * sy + oy + radius.height / 2)
                               };

                               var style = tag.style;
                               style.left = labelPos.x + 'px';
                               style.top = labelPos.y + 'px';
                               style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';

                               controller.onPlaceLabel(tag, node);
                       }
               });
		Cic.graph.loadJSON(CicData);
		Cic.graph.refresh();
		Cic.graph.config.levelDistance=200;
		Cic.graph.fx.sequence({
         onComplete: function() {
         /*alert('Instructions: Left-click on an actor to bring it to the center of the visualization tool then right-click on a second actor to view the S&I initiatives that share the relationship between the two actors');*/
         }
       });
	}
};<commit_msg>cleanup of experimental code<commit_after>var Cic =
{
	canvas: undefined,

	info: undefined,

	graph: undefined,

	init: function ()
	{
		Cic.canvas = document.getElementById("cic");
		Cic.info = document.getElementById("cic_info");

		$jit.RGraph.Plot.NodeTypes.implement({
			'image': {
				'render': function(node, canvas) {
					var ctx = canvas.getCtx();
					var img = new Image();
					var pos = node.pos.getc(true);
					img.src = 'images/' + node.data.img_file;
					ctx.drawImage(img,pos.x-32,pos.y-70);
				}
			}
		});

		Cic.graph = new $jit.RGraph(
			{
				injectInto: Cic.canvas,
				width: 600,
				height: 560,

				Edge:
				{
					color: $jit.util.rgbToHex([0, 100, 200])
				},

				Events:
				{
					enable: true,
					enableForEdges: true,
					onRightClick: function (obj, info, event)
					{
						var originNode = Cic.graph.graph.getClosestNodeToOrigin();

						if (obj.id != originNode.id)
						{
							var adjacence = Cic.graph.graph.getAdjacence(obj.id, originNode.id);

							if (adjacence && !adjacence.data.hidden && adjacence.data.html)
							{
								Cic.info.innerHTML = "
" + adjacence.nodeFrom.name + " ↔ " +
									adjacence.nodeTo.name + "
\n
Initiatives
";
								Cic.info.innerHTML += adjacence.data.html;
							}
							else
							{
								Cic.info.innerHTML = "";
							}
						}
					},

					onMouseEnter: function (obj, info, event)
					{
						Cic.canvas.style.cursor = "pointer";
					},

					onMouseLeave: function (obj, info, event)
					{
						Cic.canvas.style.cursor = "";
					}
				},

				Navigation:
				{
					enable: true,
					panning: false,
					zooming: false,
				},

				Node:
				{
					type: "image",
					overridable: true
				},

				onBeforePlotLine: function (adj)
				{
					adj.Edge.color = (adj.data.weight == 1.0) ?
							$jit.util.rgbToHex([0, 150, 0]) :
							$jit.util.rgbToHex([200, 0, 0]);
					adj.Edge.lineWidth=2;
					if (adj.data.hidden)
					{
						adj.Edge.alpha = 0;
					}
					else if ((adj.nodeFrom._depth > 0) && (adj.nodeTo._depth > 0))
					{
						adj.Edge.alpha = .2;
					}
					else
					{
						adj.Edge.alpha = 1;
					}
				},

				onBeforePlotNode: function (node)
				{
					// TODO: figure out node alpha dynamic setting
				},

				onCreateLabel: function(element, node)
				{
					element.innerHTML = "
" + node.name + "
";

					element.onclick = function ()
					{
						Cic.graph.onClick(node.id);
					}

				},

				onPlaceLabel: function(element, node)
				{
					element.style.height = '90px';
					top_string = element.style.top;
					top_number = top_string.substring(0,top_string.indexOf('px'));
					element.style.top = (top_number - 70) + 'px';
				}
			}
		);
		$jit.RGraph.Label.HTML.implement({
                       placeLabel: function(tag, node, controller){
                               var pos = node.pos.getc(true),
                               canvas = this.viz.canvas,
                               ox = canvas.translateOffsetX,
                               oy = canvas.translateOffsetY,
                               sx = canvas.scaleOffsetX,
                               sy = canvas.scaleOffsetY,
                               radius = canvas.getSize();
                               var labelPos = {
                                       x: Math.round(pos.x * sx + ox + radius.width / 2.4), /* modified from "width / 2" to center label text-alignment */
                                       y: Math.round(pos.y * sy + oy + radius.height / 2)
                               };

                               var style = tag.style;
                               style.left = labelPos.x + 'px';
                               style.top = labelPos.y + 'px';
                               style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';

                               controller.onPlaceLabel(tag, node);
                       }
               });
		Cic.graph.loadJSON(CicData);
		Cic.graph.refresh();
		Cic.graph.config.levelDistance=200;
		Cic.graph.fx.sequence({
         onComplete: function() {
         /*alert('Instructions: Left-click on an actor to bring it to the center of the visualization tool then right-click on a second actor to view the S&I initiatives that share the relationship between the two actors');*/
         }
       });
	}
};<|endoftext|>


<commit_before>/*
 * grunt-cfp-static
 * https://github.com/devoxx/grunt-cfp-static
 *
 * Copyright (c) 2013 Jo Voordeckers
 * Licensed under the MIT license.
 */

'use strict';

module.exports.renderEvent = function(grunt, files, output, event) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks
  var Handlebars = require('handlebars'),
      _ = grunt.util._,
      http = require('client-http'),
      q = require('q'),
      moment = require('moment'),

      // Placeholders for optional MOCK JSON data
      eventJSON,
      tracksJSON,
      presentationsJSON,
      schedulesJSON,
      speakersJSON,

      // Uncomment to skip remote REST calls using DV12 data
      // eventJSON = require('./event.json'),
      // tracksJSON = require('./tracks.json'),
      // presentationsJSON = require('./presentations.json'),
      // schedulesJSON = require('./schedule.json') ,
      // speakersJSON = require('./speakers.json'),

      eventUrl = event.url + '/events/' + event.id,
      schedulesUrl = event.url + '/events/' + event.id + '/schedule',
      tracksUrl = event.url + '/events/' + event.id + '/tracks',
      speakersUrl = event.url + '/events/' + event.id + '/speakers',
      presentationsUrl = event.url + '/events/' + event.id + '/presentations'
      ;

    var _defer = q.defer();

    function getMockdataOrCallREST(mockData, url) {
        var defer = q.defer();
        if (typeof(mockData) !== 'undefined') {
            defer.resolve(mockData);
        } else {
            http.get(url, function(data) {
                defer.resolve(JSON.parse(data));
            });
        }
        return defer.promise;
    }

    function getEvent(){
        return getMockdataOrCallREST(eventJSON, eventUrl);
    }

    function getSchedules(){
        return getMockdataOrCallREST(schedulesJSON, schedulesUrl);
    }

    function getTracks(){
        return getMockdataOrCallREST(tracksJSON, tracksUrl);
    }

    function getSpeakers(){
        return getMockdataOrCallREST(speakersJSON, speakersUrl);
    }

    function getPresentations(){
        return getMockdataOrCallREST(presentationsJSON, presentationsUrl);
    }

    function toUrl(name) {
        var cleaned = name.toLowerCase() // change everything to lowercase
            .replace(/^\s+|\s+$/g, "") // trim leading and trailing spaces
            .replace(/[_|\s]+/g, "-") // change all spaces and underscores to a hyphen
            .replace(/[^a-z0-9-]+/g, "") // remove all non-alphanumeric characters except the hyphen
            .replace(/[-]+/g, "-") // replace multiple instances of the hyphen with a single instance
            .replace(/^-+|-+$/g, ""); // trim leading and trailing hyphens
        return event.key + "-" + cleaned + ".html";
    }

    /**
     * Enhance JSON returned by the CFP
     */
    function prepareData(eventDetails, schedules, tracks, speakers, presentations) {

        console.log("Preparing JSON data for rendering.");

        function trackSpeaker(speakerId, trackIdVar, day) {

            var speaker = _.find(speakers, function(speaker) { return speaker.id == speakerId} );

            if (!speaker) {
                console.error("Missing speaker for presentation speaker mapping", speakerId);
                return;
            }

            // for isotope
            speaker.tracks = speaker.tracks || [];
            speaker.tracks.push(trackIdVar);
            speaker.tracks = _.uniq(speaker.tracks);
            speaker.days = speaker.days || [];
            speaker.days.push(day);
            speaker.days = _.uniq(speaker.days);
        }

        function getTrack(trackname) {
            var track = _.find(tracks, function(track){ return track.name == trackname; });
            return track ? track : "missingTrack";
        }

        function toUrl(name) {
            var cleaned = name.toLowerCase() // change everything to lowercase
                .replace(/^\s+|\s+$/g, "") // trim leading and trailing spaces
                .replace(/[_|\s]+/g, "-") // change all spaces and underscores to a hyphen
                .replace(/[^a-z0-9-]+/g, "") // remove all non-alphanumeric characters except the hyphen
                .replace(/[-]+/g, "-") // replace multiple instances of the hyphen with a single instance
                .replace(/^-+|-+$/g, ""); // trim leading and trailing hyphens
            return event.key + "-" + cleaned + ".html";
        }

        _.each(speakers, function(speaker){
            speaker.page = toUrl(speaker.firstName + " " + speaker.lastName);
        });

        _.each(tracks, function(track){
            track.icon = event.trackMapping[new String(track.id)];
        });

        _.each(presentations, function(pres){

            var track = getTrack(pres.track);

            if (track) {
                pres.trackIcon = event.trackMapping[new String(track.id)];
                pres.trackDescription = track.description;
            }

            var trackIdVar = "track-" + track.id;

            var schedule = _.find(schedules, function(schedule){ return schedule.presentationId == pres.id; });

            var dayIdVar = "day-";

            if (schedule) {
                dayIdVar += moment(schedule.fromTime).day();
            } else {
                dayIdVar += "missingSchedule";
                console.log("Missing schedule for pres", pres.id);
            }

            // for isotope
            pres.dayIdVar = dayIdVar;
            pres.trackIdVar = trackIdVar;

            // for rendering
            pres.schedule = schedule;

            _.each(pres.speakers, function(speaker) { // Add tracks and days to speaker
                trackSpeaker(speaker.speakerId, trackIdVar, dayIdVar);
            });

        });

        presentations = _.filter(presentations, function(pres){ return pres.schedule; });

        _.each(presentations, function(pres){ // Augment presentations with full speakers

            var newSpeakers = [];

            _.each(pres.speakers, function(presSpeaker) {

                var fullSpeaker = _.find(speakers, function(speaker){ return speaker.id == presSpeaker.speakerId});

                if (!fullSpeaker) {
                    console.error("Missing full speaker for pres ", pres.id, "speaker", presSpeaker);
                    return;
                }

                newSpeakers.push(fullSpeaker);

            });

            pres.speakers = newSpeakers; // Strip the missing ones

            var speaker = pres.speakers[0];

            if (!speaker) {
                console.error("Missing speaker presId", pres.id, "speaker", pres.speaker);
                return;
            }

            delete pres.speaker;
            pres.page = toUrl(speaker.page) + "?presId=" + pres.id;

        });

        _.each(speakers, function(speaker){

            if (speaker.imageURI === "http://www.devoxx.be/img/thumbnail.gif") {
                speaker.imageURI = "/images_dummy/no_avatar.gif";
            }

            var newPrezos = [];

            _.each(speaker.talks, function(speakerPres){

                var fullPres = _.find(presentations, function(pres){ return pres.id == speakerPres.presentationId; })

                if (!fullPres) {
                    console.error("Missing full presentation for speakermapping", speakerPres);
                    return;
                }

                newPrezos.push(fullPres);

            });

            newPrezos = _.sortBy(newPrezos, function(prezo){ return prezo.schedule.fromTime; });

            speaker.talks = newPrezos;

        });

        presentations = _.sortBy(presentations, function(pres){ return pres.schedule.fromTime; });

        var fullSchedule = _.groupBy(presentations, "dayIdVar");

        return {
            eventDetails: eventDetails,
            schedules: schedules,
            tracks: tracks,
            speakers: speakers,
            presentations: presentations,
            fullSchedule: fullSchedule
        }

    }

    function renderData(eventDetails, schedules, tracks, speakers, presentations) {

        console.log("Prepare template engine...");

        var defer = q.defer();

        var srcFile = grunt.file.read(files + '/index.hbr.html');

        var partialSpeakers = grunt.file.read(files + '/speakersBody.hbr.html');
        var partialSpeaker = grunt.file.read(files + '/speakerBody.hbr.html');
        var partialSpeakerRow = grunt.file.read(files + '/speakerRow.hbr.html');
        var partialSchedule = grunt.file.read(files + '/scheduleBody.hbr.html');

        var template = Handlebars.compile(srcFile);

        Handlebars.registerPartial("speakersBody", partialSpeakers);
        Handlebars.registerPartial("speakerBody", partialSpeaker);
        Handlebars.registerPartial("speakerRow", partialSpeakerRow);
        Handlebars.registerPartial("scheduleBody", partialSchedule);
        Handlebars.registerHelper("nlbr", function(text) {
            text = Handlebars.Utils.escapeExpression(text);
            text = text.toString();
            text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
            return new Handlebars.SafeString(text);
        });
        // Workaround ../ not working in regular partials
        Handlebars.registerHelper('include', function(templatename, options){
            var partial = Handlebars.partials[templatename];
            if (typeof partial === "string") {
                partial = Handlebars.compile(partial);
                Handlebars.partials[templatename] = partial;
            }
            var context = _.extend({}, this, options.hash);
            return new Handlebars.SafeString(partial(context));
        });

        Handlebars.Utils.log = function(log) {
            console.log("HBR:", log);
        }
        Handlebars.registerHelper("log", Handlebars.Utils.log)

        Handlebars.registerHelper("schedule", function(schedule) {

            var fromTime = moment(schedule.fromTime);
            var toTime = moment(schedule.toTime);
            return new Handlebars.SafeString(fromTime.format("ddd") + " " + fromTime.format("HH:mm") + " - " + toTime.format("HH:mm"));
        });

        Handlebars.registerHelper('first', function(context, options) {
          return options.fn(context[0]);
        });

        Handlebars.registerHelper('ifequal', function (val1, val2, options) {
            if (val1 === val2) {
                return options.fn(this);
            } else {
                return options.inverse(this);
            }
        });

        var prepared = prepareData(eventDetails, schedules, tracks, speakers, presentations);

        try {

            console.log("Rendering HTML...");

            var allSpeakers = function () {

                // /dv13-speakers.html

                var destFile = output + '/' + toUrl("speakers");

                var data = {
                    isSpeakers: true,
                    eventDetails: prepared.eventDetails,
                    tracks: prepared.tracks,
                    speakers: prepared.speakers
                };

                var html = template(data);

                grunt.file.write(destFile, html);

            }

            var eachSpeaker = function () {

                // /dv13-romain-guy.html?presId=123 < link to a pres within a speaker page

                _.each(prepared.speakers, function(speaker){

                    var destFile = output + '/' + speaker.page;

                    var data = {
                        isSpeaker: true,
                        speaker: speaker
                    };

                    var html = template(data);

                    grunt.file.write(destFile, html);

                });

            }

            var allPresentations = function() {

                // /dv13-schedule.html

                var destFile = output + '/' + toUrl("schedule");

                var data = {
                    isSchedule: true,
                    eventDetails: prepared.eventDetails,
                    tracks: prepared.tracks,
                    fullSchedule: prepared.fullSchedule
                };

                var html = template(data);

                grunt.file.write(destFile, html);

            }

            allSpeakers();
            eachSpeaker();
            allPresentations();

            console.log("Done.");

        } catch(e) {
            defer.reject(e);
        }

        defer.resolve();

        return defer.promise;
    }

    console.log("Rendering event ID/Key:", event.id, event.key, "fetching JSON data...");

    q.all([getEvent(), getSchedules(), getTracks(), getSpeakers(), getPresentations()])
        .spread(renderData)
        .then(function(){ console.log("Rendering done:", event.id); })
        .then(_defer.resolve)
        .fail(_defer.reject);

    return _defer.promise;

};<commit_msg>Don't filter unscheduled presentations<commit_after>/*
 * grunt-cfp-static
 * https://github.com/devoxx/grunt-cfp-static
 *
 * Copyright (c) 2013 Jo Voordeckers
 * Licensed under the MIT license.
 */

'use strict';

module.exports.renderEvent = function(grunt, files, output, event) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks
  var Handlebars = require('handlebars'),
      _ = grunt.util._,
      http = require('client-http'),
      q = require('q'),
      moment = require('moment'),

      // Placeholders for optional MOCK JSON data
      eventJSON,
      tracksJSON,
      presentationsJSON,
      schedulesJSON,
      speakersJSON,

      // Uncomment to skip remote REST calls using DV12 data
      // eventJSON = require('./event.json'),
      // tracksJSON = require('./tracks.json'),
      // presentationsJSON = require('./presentations.json'),
      // schedulesJSON = require('./schedule.json') ,
      // speakersJSON = require('./speakers.json'),

      eventUrl = event.url + '/events/' + event.id,
      schedulesUrl = event.url + '/events/' + event.id + '/schedule',
      tracksUrl = event.url + '/events/' + event.id + '/tracks',
      speakersUrl = event.url + '/events/' + event.id + '/speakers',
      presentationsUrl = event.url + '/events/' + event.id + '/presentations'
      ;

    var _defer = q.defer();

    function getMockdataOrCallREST(mockData, url) {
        var defer = q.defer();
        if (typeof(mockData) !== 'undefined') {
            defer.resolve(mockData);
        } else {
            http.get(url, function(data) {
                defer.resolve(JSON.parse(data));
            });
        }
        return defer.promise;
    }

    function getEvent(){
        return getMockdataOrCallREST(eventJSON, eventUrl);
    }

    function getSchedules(){
        return getMockdataOrCallREST(schedulesJSON, schedulesUrl);
    }

    function getTracks(){
        return getMockdataOrCallREST(tracksJSON, tracksUrl);
    }

    function getSpeakers(){
        return getMockdataOrCallREST(speakersJSON, speakersUrl);
    }

    function getPresentations(){
        return getMockdataOrCallREST(presentationsJSON, presentationsUrl);
    }

    function toUrl(name) {
        var cleaned = name.toLowerCase() // change everything to lowercase
            .replace(/^\s+|\s+$/g, "") // trim leading and trailing spaces
            .replace(/[_|\s]+/g, "-") // change all spaces and underscores to a hyphen
            .replace(/[^a-z0-9-]+/g, "") // remove all non-alphanumeric characters except the hyphen
            .replace(/[-]+/g, "-") // replace multiple instances of the hyphen with a single instance
            .replace(/^-+|-+$/g, ""); // trim leading and trailing hyphens
        return event.key + "-" + cleaned + ".html";
    }

    /**
     * Enhance JSON returned by the CFP
     */
    function prepareData(eventDetails, schedules, tracks, speakers, presentations) {

        console.log("Preparing JSON data for rendering.");

        function trackSpeaker(speakerId, trackIdVar, day) {

            var speaker = _.find(speakers, function(speaker) { return speaker.id == speakerId} );

            if (!speaker) {
                console.error("Missing speaker for presentation speaker mapping", speakerId);
                return;
            }

            // for isotope
            speaker.tracks = speaker.tracks || [];
            speaker.tracks.push(trackIdVar);
            speaker.tracks = _.uniq(speaker.tracks);
            speaker.days = speaker.days || [];
            speaker.days.push(day);
            speaker.days = _.uniq(speaker.days);
        }

        function getTrack(trackname) {
            var track = _.find(tracks, function(track){ return track.name == trackname; });
            return track ? track : "missingTrack";
        }

        function toUrl(name) {
            var cleaned = name.toLowerCase() // change everything to lowercase
                .replace(/^\s+|\s+$/g, "") // trim leading and trailing spaces
                .replace(/[_|\s]+/g, "-") // change all spaces and underscores to a hyphen
                .replace(/[^a-z0-9-]+/g, "") // remove all non-alphanumeric characters except the hyphen
                .replace(/[-]+/g, "-") // replace multiple instances of the hyphen with a single instance
                .replace(/^-+|-+$/g, ""); // trim leading and trailing hyphens
            return event.key + "-" + cleaned + ".html";
        }

        _.each(speakers, function(speaker){
            speaker.page = toUrl(speaker.firstName + " " + speaker.lastName);
        });

        _.each(tracks, function(track){
            track.icon = event.trackMapping[new String(track.id)];
        });

        _.each(presentations, function(pres){

            var track = getTrack(pres.track);

            if (track) {
                pres.trackIcon = event.trackMapping[new String(track.id)];
                pres.trackDescription = track.description;
            }

            var trackIdVar = "track-" + track.id;

            var schedule = _.find(schedules, function(schedule){ return schedule.presentationId == pres.id; });

            var dayIdVar = "day-";

            if (schedule) {
                dayIdVar += moment(schedule.fromTime).day();
            } else {
                dayIdVar += "missingSchedule";
                console.log("Missing schedule for pres", pres.id);
            }

            // for isotope
            pres.dayIdVar = dayIdVar;
            pres.trackIdVar = trackIdVar;

            // for rendering
            pres.schedule = schedule;

            _.each(pres.speakers, function(speaker) { // Add tracks and days to speaker
                trackSpeaker(speaker.speakerId, trackIdVar, dayIdVar);
            });

        });

        _.each(presentations, function(pres){ // Augment presentations with full speakers

            var newSpeakers = [];

            _.each(pres.speakers, function(presSpeaker) {

                var fullSpeaker = _.find(speakers, function(speaker){ return speaker.id == presSpeaker.speakerId});

                if (!fullSpeaker) {
                    console.error("Missing full speaker for pres ", pres.id, "speaker", presSpeaker);
                    return;
                }

                newSpeakers.push(fullSpeaker);

            });

            pres.speakers = newSpeakers; // Strip the missing ones

            var speaker = pres.speakers[0];

            if (!speaker) {
                console.error("Missing speaker presId", pres.id, "speaker", pres.speaker);
                return;
            }

            delete pres.speaker;
            pres.page = toUrl(speaker.page) + "?presId=" + pres.id;

        });

        _.each(speakers, function(speaker){

            if (speaker.imageURI === "http://www.devoxx.be/img/thumbnail.gif") {
                speaker.imageURI = "/images_dummy/no_avatar.gif";
            }

            var newPrezos = [];

            _.each(speaker.talks, function(speakerPres){

                var fullPres = _.find(presentations, function(pres){ return pres.id == speakerPres.presentationId; })

                if (!fullPres) {
                    console.error("Missing full presentation for speakermapping", speakerPres);
                    return;
                }

                newPrezos.push(fullPres);

            });

            newPrezos = _.sortBy(newPrezos, function(prezo){ return prezo.schedule ? prezo.schedule.fromTime : ""; });

            speaker.talks = newPrezos;

        });

        presentations = _.sortBy(presentations, function(pres){ return pres.schedule ? pres.schedule.fromTime : ""; });

        var fullSchedule = _.groupBy(presentations, "dayIdVar");

        return {
            eventDetails: eventDetails,
            schedules: schedules,
            tracks: tracks,
            speakers: speakers,
            presentations: presentations,
            fullSchedule: fullSchedule
        }

    }

    function renderData(eventDetails, schedules, tracks, speakers, presentations) {

        console.log("Prepare template engine...");

        var defer = q.defer();

        var srcFile = grunt.file.read(files + '/index.hbr.html');

        var partialSpeakers = grunt.file.read(files + '/speakersBody.hbr.html');
        var partialSpeaker = grunt.file.read(files + '/speakerBody.hbr.html');
        var partialSpeakerRow = grunt.file.read(files + '/speakerRow.hbr.html');
        var partialSchedule = grunt.file.read(files + '/scheduleBody.hbr.html');

        var template = Handlebars.compile(srcFile);

        Handlebars.registerPartial("speakersBody", partialSpeakers);
        Handlebars.registerPartial("speakerBody", partialSpeaker);
        Handlebars.registerPartial("speakerRow", partialSpeakerRow);
        Handlebars.registerPartial("scheduleBody", partialSchedule);
        Handlebars.registerHelper("nlbr", function(text) {
            text = Handlebars.Utils.escapeExpression(text);
            text = text.toString();
            text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
            return new Handlebars.SafeString(text);
        });
        // Workaround ../ not working in regular partials
        Handlebars.registerHelper('include', function(templatename, options){
            var partial = Handlebars.partials[templatename];
            if (typeof partial === "string") {
                partial = Handlebars.compile(partial);
                Handlebars.partials[templatename] = partial;
            }
            var context = _.extend({}, this, options.hash);
            return new Handlebars.SafeString(partial(context));
        });

        Handlebars.Utils.log = function(log) {
            console.log("HBR:", log);
        }
        Handlebars.registerHelper("log", Handlebars.Utils.log)

        Handlebars.registerHelper("schedule", function(schedule) {

            var fromTime = moment(schedule.fromTime);
            var toTime = moment(schedule.toTime);
            return new Handlebars.SafeString(fromTime.format("ddd") + " " + fromTime.format("HH:mm") + " - " + toTime.format("HH:mm"));
        });

        Handlebars.registerHelper('first', function(context, options) {
          return options.fn(context[0]);
        });

        Handlebars.registerHelper('ifequal', function (val1, val2, options) {
            if (val1 === val2) {
                return options.fn(this);
            } else {
                return options.inverse(this);
            }
        });

        var prepared = prepareData(eventDetails, schedules, tracks, speakers, presentations);

        try {

            console.log("Rendering HTML...");

            var allSpeakers = function () {

                // /dv13-speakers.html

                var destFile = output + '/' + toUrl("speakers");

                var data = {
                    isSpeakers: true,
                    eventDetails: prepared.eventDetails,
                    tracks: prepared.tracks,
                    speakers: prepared.speakers
                };

                var html = template(data);

                grunt.file.write(destFile, html);

            }

            var eachSpeaker = function () {

                // /dv13-romain-guy.html?presId=123 < link to a pres within a speaker page

                _.each(prepared.speakers, function(speaker){

                    var destFile = output + '/' + speaker.page;

                    var data = {
                        isSpeaker: true,
                        speaker: speaker
                    };

                    var html = template(data);

                    grunt.file.write(destFile, html);

                });

            }

            var allPresentations = function() {

                // /dv13-schedule.html

                var destFile = output + '/' + toUrl("schedule");

                var data = {
                    isSchedule: true,
                    eventDetails: prepared.eventDetails,
                    tracks: prepared.tracks,
                    fullSchedule: prepared.fullSchedule
                };

                var html = template(data);

                grunt.file.write(destFile, html);

            }

            allSpeakers();
            eachSpeaker();
            allPresentations();

            console.log("Done.");

        } catch(e) {
            defer.reject(e);
        }

        defer.resolve();

        return defer.promise;
    }

    console.log("Rendering event ID/Key:", event.id, event.key, "fetching JSON data...");

    q.all([getEvent(), getSchedules(), getTracks(), getSpeakers(), getPresentations()])
        .spread(renderData)
        .then(function(){ console.log("Rendering done:", event.id); })
        .then(_defer.resolve)
        .fail(_defer.reject);

    return _defer.promise;

};<|endoftext|>


<commit_before>/**
 * grunt-sass2scss
 * https://github.com/paazmaya/grunt-sass2scss
 *
 * Copyright (c) Juga Paazmaya <paazmaya@yahoo.com> (https://paazmaya.fi)
 * Licensed under the MIT license.
 */

'use strict';

const path = require('path'),
  exec = require('child_process').exec;

const MATCH_SASS = /\.sass$/;

module.exports = function exportGrunt(grunt) {

  const fileOpts = {
    encoding: 'utf8'
  };

  grunt.registerMultiTask('sass2scss', 'Convert Sass files to SCSS files with sass2scss', function register() {

    const options = this.options({
      binPath: 'sass2scss',
      args: []
    });
    const done = this.async();
    const mapList = [];

    // Add possibly missing dashes, since they are mentioned as optional
    options.args = options.args.map((item) => {
      if (item.indexOf('-') === 0) {
        return item;
      }
      if (item.length === 1) {
        return '-' + item;
      }

      return '--' + item;
    });

    const executor = function executor(map) {
      grunt.log.verbose.writeln('Converting file: ' + map.src);

      const command = [options.binPath].concat(
        options.args, ['<', map.src]
      ).join(' ');
      exec(command, (error, stdout) => {
        if (error) {
          grunt.log.warn('Failure with sass2scss. ' + error);
        }
        else {
          grunt.file.write(map.dest, stdout, fileOpts);
        }
        iterate();
      });
    };

    const iterate = function iterate() {
      if (mapList.length === 0) {
        done();

        return;
      }
      const map = mapList.shift();
      executor(map);
    };

    this.files.forEach((f) => {
      const src = f.src.filter((filepath) => {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');

          return false;
        }

        return true;
      });

      src.forEach((source) => {

        let destination = f.dest;
        if (typeof destination !== 'string') {
          // Assume that destination to be written next to source
          destination = source.replace(MATCH_SASS, '.scss');
        }
        else if (destination.indexOf('.scss') === -1) {
          // Assume it is a directory
          destination = path.join(destination, path.basename(source).replace(MATCH_SASS, '.scss'));
        }

        mapList.push({
          src: source,
          dest: destination
        });
      });
    });

    iterate();

  });

};
<commit_msg>Add u modified to regular expressions<commit_after>/**
 * grunt-sass2scss
 * https://github.com/paazmaya/grunt-sass2scss
 *
 * Copyright (c) Juga Paazmaya <paazmaya@yahoo.com> (https://paazmaya.fi)
 * Licensed under the MIT license.
 */

'use strict';

const path = require('path'),
  exec = require('child_process').exec;

const MATCH_SASS = /\.sass$/u;

module.exports = function exportGrunt(grunt) {

  const fileOpts = {
    encoding: 'utf8'
  };

  grunt.registerMultiTask('sass2scss', 'Convert Sass files to SCSS files with sass2scss', function register() {

    const options = this.options({
      binPath: 'sass2scss',
      args: []
    });
    const done = this.async();
    const mapList = [];

    // Add possibly missing dashes, since they are mentioned as optional
    options.args = options.args.map((item) => {
      if (item.indexOf('-') === 0) {
        return item;
      }
      if (item.length === 1) {
        return '-' + item;
      }

      return '--' + item;
    });

    const executor = function executor(map) {
      grunt.log.verbose.writeln('Converting file: ' + map.src);

      const command = [options.binPath].concat(
        options.args, ['<', map.src]
      ).join(' ');
      exec(command, (error, stdout) => {
        if (error) {
          grunt.log.warn('Failure with sass2scss. ' + error);
        }
        else {
          grunt.file.write(map.dest, stdout, fileOpts);
        }
        iterate();
      });
    };

    const iterate = function iterate() {
      if (mapList.length === 0) {
        done();

        return;
      }
      const map = mapList.shift();
      executor(map);
    };

    this.files.forEach((f) => {
      const src = f.src.filter((filepath) => {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');

          return false;
        }

        return true;
      });

      src.forEach((source) => {

        let destination = f.dest;
        if (typeof destination !== 'string') {
          // Assume that destination to be written next to source
          destination = source.replace(MATCH_SASS, '.scss');
        }
        else if (destination.indexOf('.scss') === -1) {
          // Assume it is a directory
          destination = path.join(destination, path.basename(source).replace(MATCH_SASS, '.scss'));
        }

        mapList.push({
          src: source,
          dest: destination
        });
      });
    });

    iterate();

  });

};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
  var _ = require('lodash'),
    request = require('request'),
    SauceTunnel = require('sauce-tunnel'),
    Q = require('q'),
    rqst = request.defaults({
      jar: false
    });

  //these result parsers return true if the tests all passed
  var resultParsers = {
    jasmine: function(result){
      return result.passed;
    },
    qunit: function(result){
      if (result.passed === undefined){ return undefined; }
      return result.passed == result.total;
    },
    mocha: function(result){
      if (result.passes === undefined){ return undefined; }
      return result.failures === 0;
    },
    'YUI Test': function(result){
      if (result.passed === undefined){ return undefined; }
      return result.passed == result.total;
    }
  };

  var TestResult = function(jobId, user, key, framework, testInterval){
    var url = 'https://saucelabs.com/rest/v1/' + user + '/js-tests/status';
    var deferred = Q.defer();

    var requestParams = {
      method: 'post',
      url: url,
      auth: {
        user: user,
        pass: key
      },
      json: true,
      body: {
        "js tests": [jobId],
      }
    };

    var checkStatus = function(){

      rqst(requestParams, function(error, response, body){

        if (error){
          deferred.resolve({
            passed: undefined,
            result: {
              message: "Error connecting to api to get test status: " + error.toString()
            }
          });
          return;
        }

        var testInfo = body['js tests'][0];

        if (testInfo.status == "test error"){
          deferred.resolve({
            passed: undefined,
            result: {
              message: "Test Error"
            }
          });
          return;
        }

        if (!body.completed){
          setTimeout(checkStatus ,testInterval);
        } else {
          testInfo.passed = testInfo.result ? resultParsers[framework](testInfo.result) : false;
          deferred.resolve(testInfo);
        }

      });
    };

    checkStatus();

    return deferred.promise;
  };

  var TestRunner = function(user, key, testInterval) {
    this.user = user;
    this.key = key;
    this.url = 'https://saucelabs.com/rest/v1/' + this.user + '/js-tests';
    this.testInterval = testInterval;
    this.results = [];
  };

  TestRunner.prototype.runTests = function(browsers, urls, framework, tunnelIdentifier, testname, tags, build, onTestComplete, callback){

    var me = this;
    var numberOfJobs = browsers.length * urls.length;

    var addResultPromise = function(promise){
      me.results.push(promise);
      grunt.log.writeln(me.results.length, "/", numberOfJobs, 'tests started');
      if (me.results.length == numberOfJobs){

        Q.all(me.results).then(function(results){
          results = results.map(function(result){
            return result.valueOf().passed;
          });

          callback(results);
        });
      }
    };

    urls.forEach(function(url){
      me.runTest(browsers, url, framework, tunnelIdentifier, testname, tags, build, function(taskIds){

        taskIds.forEach(function(taskId){
          var resultPromise = new TestResult(taskId, me.user, me.key, framework, me.testInterval);
          addResultPromise(resultPromise);
          resultPromise.then(function(result){

            var alteredResult = onTestComplete(result);
            if (alteredResult !== undefined){
              result.passed = alteredResult;
            }

            grunt.log.subhead("\nTested %s", url);
            grunt.log.writeln("Platform: %s", result.platform);

            if (tunnelIdentifier && unsupportedPort(url)) {
              grunt.log.writeln("Warning: This url might use a port that is not proxied by Sauce Connect.".yellow);
            }

            if (result.passed === undefined){
              grunt.log.error(result.result.message);
            } else {
              grunt.log.writeln("Passed: %s", result.passed);
            }
            grunt.log.writeln("Url %s", result.url);

          }, function(e){
            grunt.log.error('some error? %s', e);
          });
        });
      });
    });
  };

  TestRunner.prototype.runTest = function(browsers, url, framework, tunnelIdentifier, testname, tags, build, callback){

    var parsePlatforms = function(browsers){
      return browsers.map(function(browser){
        return [browser.platform || "", browser.browserName || "", browser.version || ""];
      });
    };

    var requestParams = {
      method: 'post',
      url: this.url,
      auth: {
        user: this.user,
        pass: this.key
      },
      json: true,
      body: {
        platforms: parsePlatforms(browsers),
        url: url,
        framework: framework,
        name: testname,
        tags: tags,
        build: build
      }
    };

    if (tunnelIdentifier){
      requestParams.body.tunnel = "tunnel-identifier:" + tunnelIdentifier;
    }

    rqst(requestParams, function(error, response, body){

      if (error){
        grunt.log.error("Could not connect to Sauce Labs api: %s", error);
        throw error;
      }

      if (!body['js tests'] || !body['js tests'].length){
          grunt.log.error('Error starting tests through Sauce API: %s', JSON.stringify(body));
          throw new Error('Could not start tests through Sauce API');
      }

      callback(body['js tests']);

    });
  };

  var defaultsObj = {
    username: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(),
    tunneled: true,
    tunnelTimeout: 120,
    testInterval: 1000 * 5,
    testReadyTimeout: 1000 * 5,
    onTestComplete: function() {

    },
    testname: "",
    tags: [],
    browsers: [{}]
  };

  function defaults(data) {
    var result = data;
    result.pages = result.url || result.urls;
    if (!_.isArray(result.pages)) {
      result.pages = [result.pages];
    }

    return result;
  }

  function configureLogEvents(tunnel) {
    var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
    methods.forEach(function (method) {
      tunnel.on('log:'+method, function (text) {
        grunt.log[method](text);
      });
      tunnel.on('verbose:'+method, function (text) {
        grunt.verbose[method](text);
      });
    });
  }

  function runTask(arg, framework, callback){

    var test = new TestRunner(arg.username, arg.key, arg.testInterval);

    if (arg.tunneled){
      var tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, arg.tunneled, arg.tunnelTimeout);
      grunt.log.writeln("=> Starting Tunnel to Sauce Labs".inverse.bold);
      configureLogEvents(tunnel);

      tunnel.start(function(isCreated) {
        if (!isCreated) {
          grunt.log.error("Could not create tunnel to Sauce Labs");
          callback(false);
          return;
        }
        grunt.log.ok("Connected to Saucelabs");

        test.runTests(arg.browsers, arg.pages, framework, arg.identifier, arg.testname, arg.tags, arg.build, arg.onTestComplete, function(status){
          status = status.every(function(passed){ return passed; });
          grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status);
          grunt.log.writeln("=> Stopping Tunnel to Sauce Labs".inverse.bold);
          tunnel.stop(function() {
            callback(status);
          });
        });
      });

    } else {
      test.runTests(arg.browsers, arg.pages, framework, null, arg.testname, arg.tags, arg.build, arg.onTestComplete, function(status){
        status = status.every(function(passed){ return passed; });
        grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status);
        callback(status);
      });
    }

  }

  function unsupportedPort(url) {
    // Not all ports are proxied by Sauce Connect. List of supported ports is
    // available at https://saucelabs.com/docs/connect#localhost
    var portRegExp = /:(\d+)\//;
    var matches = portRegExp.exec(url);
    var port = matches ? parseInt(matches[1], 10) : null;
    var supportedPorts = [80, 443, 888, 2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001, 3030,
      3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 4567, 5000, 5001, 5050, 5555, 5432, 6000,
      6001, 6060, 6666, 6543, 7000, 7070, 7774, 7777, 8000, 8001, 8003, 8031, 8080, 8081, 8765,
      8888, 9000, 9001, 9080, 9090, 9876, 9877, 9999, 49221, 55001];

    if (port) {
      return supportedPorts.indexOf(port) === -1;
    }

    return false;
  }

  grunt.registerMultiTask('saucelabs-jasmine', 'Run Jasmine test cases using Sauce Labs browsers', function() {
    var done = this.async(),
      arg = defaults(this.options(defaultsObj));

    runTask(arg, 'jasmine', done);

  });

  grunt.registerMultiTask('saucelabs-qunit', 'Run Qunit test cases using Sauce Labs browsers', function() {
    var done = this.async(),
      arg = defaults(this.options(defaultsObj));

    runTask(arg, 'qunit', done);

  });

  grunt.registerMultiTask('saucelabs-yui', 'Run YUI test cases using Sauce Labs browsers', function() {
    var done = this.async(),
    arg = defaults(this.options(defaultsObj));

    runTask(arg, 'YUI Test', done);

  });

  grunt.registerMultiTask('saucelabs-mocha', 'Run Mocha test cases using Sauce Labs browsers', function() {
    var done = this.async(),
      arg = defaults(this.options(defaultsObj));

    runTask(arg, 'mocha', done);

  });
};
<commit_msg>Add a task for custom frameworks<commit_after>module.exports = function(grunt) {
  var _ = require('lodash'),
    request = require('request'),
    SauceTunnel = require('sauce-tunnel'),
    Q = require('q'),
    rqst = request.defaults({
      jar: false
    });

  //these result parsers return true if the tests all passed
  var resultParsers = {
    jasmine: function(result){
      return result.passed;
    },
    qunit: function(result){
      if (result.passed === undefined){ return undefined; }
      return result.passed == result.total;
    },
    mocha: function(result){
      if (result.passes === undefined){ return undefined; }
      return result.failures === 0;
    },
    'YUI Test': function(result){
      if (result.passed === undefined){ return undefined; }
      return result.passed == result.total;
    },
    custom: function(result){
      if (result.passed === undefined){ return undefined; }
      return result.failed === 0;
    }
  };

  var TestResult = function(jobId, user, key, framework, testInterval){
    var url = 'https://saucelabs.com/rest/v1/' + user + '/js-tests/status';
    var deferred = Q.defer();

    var requestParams = {
      method: 'post',
      url: url,
      auth: {
        user: user,
        pass: key
      },
      json: true,
      body: {
        "js tests": [jobId]
      }
    };

    var checkStatus = function(){

      rqst(requestParams, function(error, response, body){

        if (error){
          deferred.resolve({
            passed: undefined,
            result: {
              message: "Error connecting to api to get test status: " + error.toString()
            }
          });
          return;
        }

        var testInfo = body['js tests'][0];

        if (testInfo.status == "test error"){
          deferred.resolve({
            passed: undefined,
            result: {
              message: "Test Error"
            }
          });
          return;
        }

        if (!body.completed){
          setTimeout(checkStatus ,testInterval);
        } else {
          testInfo.passed = testInfo.result ? resultParsers[framework](testInfo.result) : false;
          deferred.resolve(testInfo);
        }

      });
    };

    checkStatus();

    return deferred.promise;
  };

  var TestRunner = function(user, key, testInterval) {
    this.user = user;
    this.key = key;
    this.url = 'https://saucelabs.com/rest/v1/' + this.user + '/js-tests';
    this.testInterval = testInterval;
    this.results = [];
  };

  TestRunner.prototype.runTests = function(browsers, urls, framework, tunnelIdentifier, testname, tags, build, onTestComplete, callback){

    var me = this;
    var numberOfJobs = browsers.length * urls.length;

    var addResultPromise = function(promise){
      me.results.push(promise);
      grunt.log.writeln(me.results.length, "/", numberOfJobs, 'tests started');
      if (me.results.length == numberOfJobs){

        Q.all(me.results).then(function(results){
          results = results.map(function(result){
            return result.valueOf().passed;
          });

          callback(results);
        });
      }
    };

    urls.forEach(function(url){
      me.runTest(browsers, url, framework, tunnelIdentifier, testname, tags, build, function(taskIds){

        taskIds.forEach(function(taskId){
          var resultPromise = new TestResult(taskId, me.user, me.key, framework, me.testInterval);
          addResultPromise(resultPromise);
          resultPromise.then(function(result){

            var alteredResult = onTestComplete(result);
            if (alteredResult !== undefined){
              result.passed = alteredResult;
            }

            grunt.log.subhead("\nTested %s", url);
            grunt.log.writeln("Platform: %s", result.platform);

            if (tunnelIdentifier && unsupportedPort(url)) {
              grunt.log.writeln("Warning: This url might use a port that is not proxied by Sauce Connect.".yellow);
            }

            if (result.passed === undefined){
              grunt.log.error(result.result.message);
            } else {
              grunt.log.writeln("Passed: %s", result.passed);
            }
            grunt.log.writeln("Url %s", result.url);

          }, function(e){
            grunt.log.error('some error? %s', e);
          });
        });
      });
    });
  };

  TestRunner.prototype.runTest = function(browsers, url, framework, tunnelIdentifier, testname, tags, build, callback){

    var parsePlatforms = function(browsers){
      return browsers.map(function(browser){
        return [browser.platform || "", browser.browserName || "", browser.version || ""];
      });
    };

    var requestParams = {
      method: 'post',
      url: this.url,
      auth: {
        user: this.user,
        pass: this.key
      },
      json: true,
      body: {
        platforms: parsePlatforms(browsers),
        url: url,
        framework: framework,
        name: testname,
        tags: tags,
        build: build
      }
    };

    if (tunnelIdentifier){
      requestParams.body.tunnel = "tunnel-identifier:" + tunnelIdentifier;
    }

    rqst(requestParams, function(error, response, body){

      if (error){
        grunt.log.error("Could not connect to Sauce Labs api: %s", error);
        throw error;
      }

      if (!body['js tests'] || !body['js tests'].length){
          grunt.log.error('Error starting tests through Sauce API: %s', JSON.stringify(body));
          throw new Error('Could not start tests through Sauce API');
      }

      callback(body['js tests']);

    });
  };

  var defaultsObj = {
    username: process.env.SAUCE_USERNAME,
    key: process.env.SAUCE_ACCESS_KEY,
    identifier: Math.floor((new Date()).getTime() / 1000 - 1230768000).toString(),
    tunneled: true,
    tunnelTimeout: 120,
    testInterval: 1000 * 5,
    testReadyTimeout: 1000 * 5,
    onTestComplete: function() {

    },
    testname: "",
    tags: [],
    browsers: [{}]
  };

  function defaults(data) {
    var result = data;
    result.pages = result.url || result.urls;
    if (!_.isArray(result.pages)) {
      result.pages = [result.pages];
    }

    return result;
  }

  function configureLogEvents(tunnel) {
    var methods = ['write', 'writeln', 'error', 'ok', 'debug'];
    methods.forEach(function (method) {
      tunnel.on('log:'+method, function (text) {
        grunt.log[method](text);
      });
      tunnel.on('verbose:'+method, function (text) {
        grunt.verbose[method](text);
      });
    });
  }

  function runTask(arg, framework, callback){

    var test = new TestRunner(arg.username, arg.key, arg.testInterval);

    if (arg.tunneled){
      var tunnel = new SauceTunnel(arg.username, arg.key, arg.identifier, arg.tunneled, arg.tunnelTimeout);
      grunt.log.writeln("=> Starting Tunnel to Sauce Labs".inverse.bold);
      configureLogEvents(tunnel);

      tunnel.start(function(isCreated) {
        if (!isCreated) {
          grunt.log.error("Could not create tunnel to Sauce Labs");
          callback(false);
          return;
        }
        grunt.log.ok("Connected to Saucelabs");

        test.runTests(arg.browsers, arg.pages, framework, arg.identifier, arg.testname, arg.tags, arg.build, arg.onTestComplete, function(status){
          status = status.every(function(passed){ return passed; });
          grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status);
          grunt.log.writeln("=> Stopping Tunnel to Sauce Labs".inverse.bold);
          tunnel.stop(function() {
            callback(status);
          });
        });
      });

    } else {
      test.runTests(arg.browsers, arg.pages, framework, null, arg.testname, arg.tags, arg.build, arg.onTestComplete, function(status){
        status = status.every(function(passed){ return passed; });
        grunt.log[status ? 'ok' : 'error']("All tests completed with status %s", status);
        callback(status);
      });
    }
  }

  function unsupportedPort(url) {
    // Not all ports are proxied by Sauce Connect. List of supported ports is
    // available at https://saucelabs.com/docs/connect#localhost
    var portRegExp = /:(\d+)\//;
    var matches = portRegExp.exec(url);
    var port = matches ? parseInt(matches[1], 10) : null;
    var supportedPorts = [80, 443, 888, 2000, 2001, 2020, 2109, 2222, 2310, 3000, 3001, 3030,
      3210, 3333, 4000, 4001, 4040, 4321, 4502, 4503, 4567, 5000, 5001, 5050, 5555, 5432, 6000,
      6001, 6060, 6666, 6543, 7000, 7070, 7774, 7777, 8000, 8001, 8003, 8031, 8080, 8081, 8765,
      8888, 9000, 9001, 9080, 9090, 9876, 9877, 9999, 49221, 55001];

    if (port) {
      return supportedPorts.indexOf(port) === -1;
    }

    return false;
  }

  Object.keys(resultParsers).forEach(function(framework) {
    grunt.registerMultiTask('saucelabs-' + framework, 'Run ' + framework + 'test cases using Sauce Labs browsers', function() {
      var done = this.async(),
        arg = defaults(this.options(defaultsObj));

      runTask(arg, framework, done);
    });
  });
};
<|endoftext|>