<commit_before>modules.push({
  name: "schedule",
  check: function() {
    return (loggedIn && (PAGE == "/" || PAGE == "/home"));
  },
  run: function() {
    // HOMEPAGE CLASS INFO
    todayInfo = false;
    userInfo = false;
    schedule = false;

    var updateHomepageInfo = function (){
      var content = "
";

      if(schedule){
        var cInfo = schedule;
        $("#classInfo").html("");
        $("#cInfo-table tr").removeClass("sucess");

        //console.log(sInfo);
        if(cInfo.nowClass !== false){
          if(cInfo.nowClass.className == "Unstructured Time" && sessionStorage['user-status'] == "In Class"){
            sessionStorage['user-status'] = "available";
            sendStatus();
          }else if(cInfo.nowClass.className !== "Unstructured Time"){
            sessionStorage['user-status'] = "in class";
            sendStatus();
          }

          if(cInfo.justEnded !== false){
            content += "
"+cInfo.justEnded.className+" has just ended.
";
            if(cInfo.justStarted){
              content += "
Get to "+cInfo.nowClass.room+" for "+cInfo.nowClass.className+"
";
            }
          }else if(cInfo.nowClass.className == "Unstructured Time") {
            // FREE PERIOD
            content += "You currently have a Free Period for "+moment(cInfo.nowClass.endTime, "hh:mm A").fromNow(true)+"";
          }else{
            // Regular class
            content += "
You should currently be in "+cInfo.nowClass.room+" for "+cInfo.nowClass.className+"
";
          }
          $("#cInfo-table tbody tr td:contains('"+moment(cInfo.nowClass.className, "hh:mm A").format("h:mm A")+"')").parent().addClass("success");
          if(cInfo.nextClass !== false && cInfo.nextClass.className !== "Afternoon Advisement"){
            content += "
Your next class is "+cInfo.nextClass.className+" in "+cInfo.nextClass.room+" in "+moment(cInfo.nextClass.startTime, "hh:mm A").fromNow(true)+"
";
          }else{
            content += "
This is the last class of your day!
";
          }

          $("#classInfo").html(content);
        }else{
          // out of school
          if(sessionStorage['user-status'].toLowerCase() == "in class"){
            sessionStorage['user-status'] = "available";
            sendStatus();
          }
        }
      }else{
        if(sessionStorage['user-status'].toLowerCase() == "in class"){
          sessionStorage['user-status'] = "available";
          sendStatus();
        }
      }
    }

    var updateSidebarClassInfo = function (){
      var content = "";
      if(schedule){
        var cInfo = schedule;
        if(cInfo.nowClass !== false){
          $("#sidebar-class-info").show();
          $("#period-countdown").show();
          if(cInfo.nowClass.className == "Unstructured Time") {
            // FREE PERIOD
            $("#sidebar-now-class").text("Free Period");
          }else{
            // Regular class
            var cTitle = cInfo.nowClass.className;
            if(cTitle.indexOf(")") > -1)
              cTitle = cTitle.split(")")[0] +")";
            $("#sidebar-now-class").text(cTitle);
            $("#nowclass-room").text(cInfo.nowClass.room);
          }

          $("#todays-schedule li").removeClass("list-group-item-success");
          $("#todays-schedule li[data-mID='"+cInfo.nowClass.mID+"']").addClass("list-group-item-success");

          $("#period-countdown h3").text(moment(cInfo.nowClass.endTime, "hh:mm A").fromNow(true));

          if(cInfo.nextClass !== false && cInfo.nextClass.className !== "Afternoon Advisement"){
            $("#sd-nextc").show();
            $("#sidebar-next-class").text(cInfo.nextClass.className.split(")")[0]+")");
            if(cInfo.nextClass.room !== "Anywhere")
              $("#nextclass-room").text(cInfo.nextClass.room);
          }else{
            $("#sd-nextc").hide();
          }
        }else{
          $("#sidebar-class-info").hide();
          $("#period-countdown").hide();
        }
      }else{
        if(sessionStorage['user-status'].toLowerCase() == "in class"){
          sessionStorage['user-status'] = "available";
          sendStatus();
        }
      }
    }

    var hasClasses = false;
    var registered = true;
    var profileUserInfo = null;
    var updateProfileSchedule = function (){
      var profileUsername = window.location.href.split("/")[4];
      if(!profileUserInfo){
        $.get('/users/api/'+profileUsername, function(data){
          if(data){
            registered = data.registered;
            if(data.registered == false){
              $("#profile-schedule").hide();
              return;
            }
            hasClasses = true;
            profileUserInfo = data;
            updateProfileSchedule();
          }
        });
      }else{
        var cInfo = getCurrentClassInfo(profileUserInfo.todaysClassesInfo.periods);
        if(cInfo.inSchool == true && cInfo !== false){
          if(cInfo.nowClass !== false){
            $("#profile-schedule").show();
            $("#profile-schedule p").html(""+profileUserInfo.firstName+" currently has "+cInfo.nowClass.className+" in "+cInfo.nowClass.room+" until "+moment(cInfo.nowClass.endTime, "hh:mm A").format("h:mm A")+"");
          }
        }else{
          $("#profile-schedule").hide();
        }
      }
    }

    var sendabout = "";
    updateDayInfo = function (){
      if(todayInfo == undefined)
        return;

      schedule = getCurrentClassInfo(todayInfo.periods);
      var title = "Class has just Ended!";

      if(schedule.justStarted && schedule.nowClass.room !== sendabout){
        var n = schedule.nowClass;
        var text = "Next is "+n.className+" in "+n.room;
        if(schedule.justEnded == false){
          "Get to "+n.room+" for "+n.className+".";
        }
        if(n.className == "Unstructured Time")
          text = "You now have a free until "+n.endTime+"!";
        sendDesktopNotification("info", title, text);
        sendabout = n.room;
      }

      if($("#classInfo").length)
        updateHomepageInfo();

      if($("#profile-schedule").length && registered)
        updateProfileSchedule();

      updateSidebarClassInfo();
      console.log("Updated schedule info");
    }

    // Return an the scheduleobject for today with the periods filled in.
    var getCurrentClassInfo = function(periods){
      var mom = moment();
      var dayStart = moment("08:40 AM", "hh:mm A");
      var dayEnd = moment("02:50 PM", "hh:mm A");

      var now = false;
      var next = false;
      var justEnded = false;
      var justStarted = false;
      var inSchool = false;

      if(periods !== false && mom.isBetween(dayStart, dayEnd)){
        inSchool = true;
        // THERE ARE CLASSES SCHEDULED FOR TODAY

        // Loop through today's classes to find the currently ongoing
        now = periods.filter(function(period) {
          if(mom.isBetween(moment(period.startTime, "hh:mm A"), moment(period.endTime, "hh:mm A"))){
            return true;
          }
          return false;
        });
        if(now.length >= 1) now = now[0];
        // If there is no such period (and it is during the school day)

        // Try to find a class that just ended
        var cur = mom;
        var found = periods.filter(function(period) {
          var ended = moment(period.endTime, "hh:mm A");
          var buffer = moment(ended).add(3, 'minutes'); // Start of next period with buffer time
          if(cur.isBetween(ended, buffer)){
            return true;
          }
          return false;
        });
        if(found.length == 1){ justEnded = found[0];}else{justEnded = false;}
        // If none found set to false

        // Did the current class just start?
        var started = moment(now.startTime, "hh:mm A");
        var buffer = moment(started).add(3, 'minutes');
        if(cur.isBetween(started, buffer)) justStarted = true;

        // Get the next class
        next = ((periods.length-1 > periods.indexOf(now)) ? periods[periods.indexOf(now)+1] : false);
      }

      return {
        nowClass: now, // object of current class | false
        nextClass: next, // object of next class | false
        justStarted: justStarted, // boolean stating whether current class just started | false
        justEnded: justEnded, // object of class that just ended | false
        inSchool: inSchool // boolean stating whether the current time is during school hours
      };
    };

    // RUN
    userInfo = currentUser;
    todayInfo = userInfo.todaysClassesInfo;

    if(todayInfo !== undefined){
      schedule = getCurrentClassInfo(todayInfo.periods);
      updateDayInfo();
      setInterval(updateDayInfo, 60000);
    }else{
      $("#classInfo").remove();
      console.log("Not a school day, not updating class info.");
    }
  }
})
<commit_msg>Fixed sidebar schedule<commit_after>modules.push({
  name: "schedule",
  check: function() {
    return (loggedIn);
  },
  run: function() {
    // HOMEPAGE CLASS INFO
    todayInfo = false;
    userInfo = false;
    schedule = false;

    var updateHomepageInfo = function (){
      var content = "
";

      if(schedule){
        var cInfo = schedule;
        $("#classInfo").html("");
        $("#cInfo-table tr").removeClass("sucess");

        //console.log(sInfo);
        if(cInfo.nowClass !== false){
          if(cInfo.nowClass.className == "Unstructured Time" && sessionStorage['user-status'] == "In Class"){
            sessionStorage['user-status'] = "available";
            sendStatus();
          }else if(cInfo.nowClass.className !== "Unstructured Time"){
            sessionStorage['user-status'] = "in class";
            sendStatus();
          }

          if(cInfo.justEnded !== false){
            content += "
"+cInfo.justEnded.className+" has just ended.
";
            if(cInfo.justStarted){
              content += "
Get to "+cInfo.nowClass.room+" for "+cInfo.nowClass.className+"
";
            }
          }else if(cInfo.nowClass.className == "Unstructured Time") {
            // FREE PERIOD
            content += "You currently have a Free Period for "+moment(cInfo.nowClass.endTime, "hh:mm A").fromNow(true)+"";
          }else{
            // Regular class
            content += "
You should currently be in "+cInfo.nowClass.room+" for "+cInfo.nowClass.className+"
";
          }
          $("#cInfo-table tbody tr td:contains('"+moment(cInfo.nowClass.className, "hh:mm A").format("h:mm A")+"')").parent().addClass("success");
          if(cInfo.nextClass !== false && cInfo.nextClass.className !== "Afternoon Advisement"){
            content += "
Your next class is "+cInfo.nextClass.className+" in "+cInfo.nextClass.room+" in "+moment(cInfo.nextClass.startTime, "hh:mm A").fromNow(true)+"
";
          }else{
            content += "
This is the last class of your day!
";
          }

          $("#classInfo").html(content);
        }else{
          // out of school
          if(sessionStorage['user-status'].toLowerCase() == "in class"){
            sessionStorage['user-status'] = "available";
            sendStatus();
          }
        }
      }else{
        if(sessionStorage['user-status'].toLowerCase() == "in class"){
          sessionStorage['user-status'] = "available";
          sendStatus();
        }
      }
    }

    var updateSidebarClassInfo = function (){
      var content = "";
      if(schedule){
        var cInfo = schedule;
        if(cInfo.nowClass !== false){
          $("#sidebar-class-info").show();
          $("#period-countdown").show();
          if(cInfo.nowClass.className == "Unstructured Time") {
            // FREE PERIOD
            $("#sidebar-now-class").text("Free Period");
          }else{
            // Regular class
            var cTitle = cInfo.nowClass.className;
            if(cTitle.indexOf(")") > -1)
              cTitle = cTitle.split(")")[0] +")";
            $("#sidebar-now-class").text(cTitle);
            $("#nowclass-room").text(cInfo.nowClass.room);
          }

          $("#todays-schedule li").removeClass("list-group-item-success");
          $("#todays-schedule li[data-mID='"+cInfo.nowClass.mID+"']").addClass("list-group-item-success");

          $("#period-countdown h3").text(moment(cInfo.nowClass.endTime, "hh:mm A").fromNow(true));

          if(cInfo.nextClass !== false && cInfo.nextClass.className !== "Afternoon Advisement"){
            $("#sd-nextc").show();
            $("#sidebar-next-class").text(cInfo.nextClass.className.split(")")[0]+")");
            if(cInfo.nextClass.room !== "Anywhere")
              $("#nextclass-room").text(cInfo.nextClass.room);
          }else{
            $("#sd-nextc").hide();
          }
        }else{
          $("#sidebar-class-info").hide();
          $("#period-countdown").hide();
        }
      }else{
        if(sessionStorage['user-status'].toLowerCase() == "in class"){
          sessionStorage['user-status'] = "available";
          sendStatus();
        }
      }
    }

    var hasClasses = false;
    var registered = true;
    var profileUserInfo = null;
    var updateProfileSchedule = function (){
      var profileUsername = window.location.href.split("/")[4];
      if(!profileUserInfo){
        $.get('/users/api/'+profileUsername, function(data){
          if(data){
            registered = data.registered;
            if(data.registered == false){
              $("#profile-schedule").hide();
              return;
            }
            hasClasses = true;
            profileUserInfo = data;
            updateProfileSchedule();
          }
        });
      }else{
        var cInfo = getCurrentClassInfo(profileUserInfo.todaysClassesInfo.periods);
        if(cInfo.inSchool == true && cInfo !== false){
          if(cInfo.nowClass !== false){
            $("#profile-schedule").show();
            $("#profile-schedule p").html(""+profileUserInfo.firstName+" currently has "+cInfo.nowClass.className+" in "+cInfo.nowClass.room+" until "+moment(cInfo.nowClass.endTime, "hh:mm A").format("h:mm A")+"");
          }
        }else{
          $("#profile-schedule").hide();
        }
      }
    }

    var sendabout = "";
    updateDayInfo = function (){
      if(todayInfo == undefined)
        return;

      schedule = getCurrentClassInfo(todayInfo.periods);
      var title = "Class has just Ended!";

      if(schedule.justStarted && schedule.nowClass.room !== sendabout){
        var n = schedule.nowClass;
        var text = "Next is "+n.className+" in "+n.room;
        if(schedule.justEnded == false){
          "Get to "+n.room+" for "+n.className+".";
        }
        if(n.className == "Unstructured Time")
          text = "You now have a free until "+n.endTime+"!";
        sendDesktopNotification("info", title, text);
        sendabout = n.room;
      }

      if($("#classInfo").length)
        updateHomepageInfo();

      if($("#profile-schedule").length && registered)
        updateProfileSchedule();

      updateSidebarClassInfo();
      console.log("Updated schedule info");
    }

    // Return an the scheduleobject for today with the periods filled in.
    var getCurrentClassInfo = function(periods){
      var mom = moment();
      var dayStart = moment("08:40 AM", "hh:mm A");
      var dayEnd = moment("02:50 PM", "hh:mm A");

      var now = false;
      var next = false;
      var justEnded = false;
      var justStarted = false;
      var inSchool = false;

      if(periods !== false && mom.isBetween(dayStart, dayEnd)){
        inSchool = true;
        // THERE ARE CLASSES SCHEDULED FOR TODAY

        // Loop through today's classes to find the currently ongoing
        now = periods.filter(function(period) {
          if(mom.isBetween(moment(period.startTime, "hh:mm A"), moment(period.endTime, "hh:mm A"))){
            return true;
          }
          return false;
        });
        if(now.length >= 1) now = now[0];
        // If there is no such period (and it is during the school day)

        // Try to find a class that just ended
        var cur = mom;
        var found = periods.filter(function(period) {
          var ended = moment(period.endTime, "hh:mm A");
          var buffer = moment(ended).add(3, 'minutes'); // Start of next period with buffer time
          if(cur.isBetween(ended, buffer)){
            return true;
          }
          return false;
        });
        if(found.length == 1){ justEnded = found[0];}else{justEnded = false;}
        // If none found set to false

        // Did the current class just start?
        var started = moment(now.startTime, "hh:mm A");
        var buffer = moment(started).add(3, 'minutes');
        if(cur.isBetween(started, buffer)) justStarted = true;

        // Get the next class
        next = ((periods.length-1 > periods.indexOf(now)) ? periods[periods.indexOf(now)+1] : false);
      }

      return {
        nowClass: now, // object of current class | false
        nextClass: next, // object of next class | false
        justStarted: justStarted, // boolean stating whether current class just started | false
        justEnded: justEnded, // object of class that just ended | false
        inSchool: inSchool // boolean stating whether the current time is during school hours
      };
    };

    // RUN
    userInfo = currentUser;
    todayInfo = userInfo.todaysClassesInfo;

    if(todayInfo !== undefined){
      schedule = getCurrentClassInfo(todayInfo.periods);
      updateDayInfo();
      setInterval(updateDayInfo, 60000);
    }else{
      $("#classInfo").remove();
      console.log("Not a school day, not updating class info.");
    }
  }
})
<|endoftext|>


<commit_before>import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { routeReducer, syncHistory } from 'react-router-redux';

// custom reducers
import searchReducer from '../reducers/search_reducer.js';

// add history to reducer and thunk to dispatch functions as actions
const ConfigureStore = (initialState, history) => {
  const reducerObj = {
    searchResults: searchReducer,
    routing: routeReducer,
  };
  const reducer = combineReducers(reducerObj);

  const store = compose(
    applyMiddleware(thunk),
    applyMiddleware(syncHistory(history))
  )(createStore)(reducer, initialState);
  return store;
};

export default ConfigureStore;
<commit_msg>dev test changes<commit_after>import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { routeReducer, syncHistory } from 'react-router-redux';

// custom reducers
import searchReducer from '../reducers/search_reducer.js';

// add history to reducer and thunk to dispatch functions as actions
const ConfigureStore = (initialState, history) => {
  const reducerObj = {
    searchResults: searchReducer,
    routing: routeReducer,
  };
  const reducer = combineReducers(reducerObj);
  if (process.env.NODE_ENV == 'development'){
    //redux debugging
    const store = compose(applyMiddleware(thunk), applyMiddleware(syncHistory(history)))(createStore)(reducer, initialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
    return store;
  }
  else{
    const store = compose(applyMiddleware(thunk), applyMiddleware(syncHistory(history)))(createStore)(reducer, initialState);
    return store;
  }

};

export default ConfigureStore;
<|endoftext|>


<commit_before>var $movingTooltip;
var $movingTooltipPointer;
var $movingTooltipFinalContent;
var $movingTooltipTempContent;
var $header;
var $articleIconsHost;
var $wikiBrowserHost;
var hoveredElements = 0;

$( document ).ready(function() {
	$movingTooltip = $('#moving-tooltip');
	$movingTooltipFinalContent = $('#visible-tooltip span');
	$movingTooltipTempContent = $('#hidden-tooltip span');
	$movingTooltipPointer = $('#tooltip-pointer');
	$header = $('#wikibrowser-header');
	$articleIconsHost = $('#wikibrowser-article-icons');
	$wikiBrowserHost = $('#wikibrowser-host');
	setUpInitialMouseEvents();
});

function setUpInitialMouseEvents() {
	var $elements = $("#wikibrowser-header .header-element");
	$.each($elements, function(index, element) {
		setUpMouseEvents($(element));
	});
}

function setUpMouseEvents($element) {
	$element.hover(headerElementMouseEnter, headerElementMouseLeave);
	$element.click(headerElementClick);
}

function createHeaderElementForArticle(articleName, pageID) {
	// Remove &redirect=no
	if (articleName.indexOf('&') > -1) {
		articleName = articleName.substring(0, articleName.indexOf('&'));
	}
	// Remove #anchor
	if (articleName.indexOf('#') > -1) {
		articleName = articleName.substring(0, articleName.indexOf('#'));
	}
	// '_' -> ' ' for nice tooltip and simpler regex
	articleName = articleName.replace(/_/g, " ");

	// Capture first letters of words
	var acronym = articleName.match(/\b([a-zA-Z])/g).join('');
	// Limit length of the acronym
	acronym = acronym.substring(0, 6);

	// Create the header element
	var $element = $("<a>", {
		target: "_blank",
		class: "header-element",
		alt: articleName
	})
	$element.html(acronym);
	$element.data("associated-page", pageID);
	setUpMouseEvents($element);
	$articleIconsHost.append($element);
}

function headerElementMouseEnter() {
	hoveredElements++;

	var $element = $(this);
	var newTooltipContent = $element.attr('alt');

	// Before taking measurements, update tooltip's contents
	$movingTooltipTempContent.html(newTooltipContent);

	var elementPosition = $element.offset().left;
	var elementWidth = $element.outerWidth(false);
	var tooltipWidth = $movingTooltipTempContent.outerWidth(true);
	var screenWidth = $header.width();

	var targetPosition = elementPosition + elementWidth * 0.5 - 6;
	var tooltipOffset = targetPosition - tooltipWidth * 0.5 - 10;
	// Movement is bound on the right side
	if (targetPosition + tooltipWidth * 0.5 > screenWidth - 30)
	{
		tooltipOffset = screenWidth - tooltipWidth - 30;
	}
	// Movement is bound on the left side
	if (targetPosition - tooltipWidth * 0.5 < 30)
	{
		tooltipOffset = 30;
	}

	$movingTooltip.stop().animate(
		{
			opacity: 1
		},
		{
			duration: 200,
			queue: false
		}
	);
	$movingTooltipPointer.stop().animate(
		{
			opacity: 1
		},
		{
			duration: 200,
			queue: false
		}
	);
	$movingTooltip.animate(
		{
			width: tooltipWidth,
			left: tooltipOffset
		},
		{
			duration: 400,
			queue: false
		}
	);
	$movingTooltipPointer.animate(
	{
			left: targetPosition,
		},
		{
			duration: 400,
			queue: false
		}
	);
	$movingTooltipFinalContent.animate(
		{
			opacity: '0'
		},
		{
			duration: 200,
			queue: false,
		 	always: function() {
		 		$movingTooltipFinalContent.animate(
		 			{
		 				opacity: '1'
		 			},
		 			{
		 				duration: 200,
		 				queue: false
		 			}
		 		);
		 		$movingTooltipFinalContent.html(newTooltipContent);
		 	}
		}
	);
}

function headerElementMouseLeave() {
	hoveredElements--;
	if (hoveredElements == 0) {
		$movingTooltip.stop().animate(
			{
				opacity: 0
			},
			{
 				duration: 200,
 				queue: false
			}
		);
		$movingTooltipPointer.stop().animate(
			{
				opacity: 0
			},
			{
 				duration: 200,
 				queue: false
			}
		);
 		$movingTooltipFinalContent.stop().animate(
 			{
 				opacity: '0'
 			},
 			{
 				duration: 200,
 				queue: false
 			}
 		);
	}
}

function headerElementClick() {
	var $element = $(this);
	var pageId = $element.data("associated-page");
	if (typeof pageId !== 'undefined')
	{
		scrollToPageId(pageId);
	}
}

function scrollToPageId(pageId) {
	var newPageOffset = $("#" + pageId).offset().left;
	var offsetDelta = $wikiBrowserHost.scrollLeft();
	// Subtract 600 to also show page to the left, if the view area is large enough
	if ($wikiBrowserHost.width() > 1200) {
		offsetDelta -= 600;
	}
	$wikiBrowserHost.animate({scrollLeft: offsetDelta + newPageOffset}, 400);
}
<commit_msg>When scrolling, target page is centered<commit_after>var $movingTooltip;
var $movingTooltipPointer;
var $movingTooltipFinalContent;
var $movingTooltipTempContent;
var $header;
var $articleIconsHost;
var $wikiBrowserHost;
var hoveredElements = 0;

$( document ).ready(function() {
	$movingTooltip = $('#moving-tooltip');
	$movingTooltipFinalContent = $('#visible-tooltip span');
	$movingTooltipTempContent = $('#hidden-tooltip span');
	$movingTooltipPointer = $('#tooltip-pointer');
	$header = $('#wikibrowser-header');
	$articleIconsHost = $('#wikibrowser-article-icons');
	$wikiBrowserHost = $('#wikibrowser-host');
	setUpInitialMouseEvents();
});

function setUpInitialMouseEvents() {
	var $elements = $("#wikibrowser-header .header-element");
	$.each($elements, function(index, element) {
		setUpMouseEvents($(element));
	});
}

function setUpMouseEvents($element) {
	$element.hover(headerElementMouseEnter, headerElementMouseLeave);
	$element.click(headerElementClick);
}

function createHeaderElementForArticle(articleName, pageID) {
	// Remove &redirect=no
	if (articleName.indexOf('&') > -1) {
		articleName = articleName.substring(0, articleName.indexOf('&'));
	}
	// Remove #anchor
	if (articleName.indexOf('#') > -1) {
		articleName = articleName.substring(0, articleName.indexOf('#'));
	}
	// '_' -> ' ' for nice tooltip and simpler regex
	articleName = articleName.replace(/_/g, " ");

	// Capture first letters of words
	var acronym = articleName.match(/\b([a-zA-Z])/g).join('');
	// Limit length of the acronym
	acronym = acronym.substring(0, 6);

	// Create the header element
	var $element = $("<a>", {
		target: "_blank",
		class: "header-element",
		alt: articleName
	})
	$element.html(acronym);
	$element.data("associated-page", pageID);
	setUpMouseEvents($element);
	$articleIconsHost.append($element);
}

function headerElementMouseEnter() {
	hoveredElements++;

	var $element = $(this);
	var newTooltipContent = $element.attr('alt');

	// Before taking measurements, update tooltip's contents
	$movingTooltipTempContent.html(newTooltipContent);

	var elementPosition = $element.offset().left;
	var elementWidth = $element.outerWidth(false);
	var tooltipWidth = $movingTooltipTempContent.outerWidth(true);
	var screenWidth = $header.width();

	var targetPosition = elementPosition + elementWidth * 0.5 - 6;
	var tooltipOffset = targetPosition - tooltipWidth * 0.5 - 10;
	// Movement is bound on the right side
	if (targetPosition + tooltipWidth * 0.5 > screenWidth - 30)
	{
		tooltipOffset = screenWidth - tooltipWidth - 30;
	}
	// Movement is bound on the left side
	if (targetPosition - tooltipWidth * 0.5 < 30)
	{
		tooltipOffset = 30;
	}

	$movingTooltip.stop().animate(
		{
			opacity: 1
		},
		{
			duration: 200,
			queue: false
		}
	);
	$movingTooltipPointer.stop().animate(
		{
			opacity: 1
		},
		{
			duration: 200,
			queue: false
		}
	);
	$movingTooltip.animate(
		{
			width: tooltipWidth,
			left: tooltipOffset
		},
		{
			duration: 400,
			queue: false
		}
	);
	$movingTooltipPointer.animate(
	{
			left: targetPosition,
		},
		{
			duration: 400,
			queue: false
		}
	);
	$movingTooltipFinalContent.animate(
		{
			opacity: '0'
		},
		{
			duration: 200,
			queue: false,
		 	always: function() {
		 		$movingTooltipFinalContent.animate(
		 			{
		 				opacity: '1'
		 			},
		 			{
		 				duration: 200,
		 				queue: false
		 			}
		 		);
		 		$movingTooltipFinalContent.html(newTooltipContent);
		 	}
		}
	);
}

function headerElementMouseLeave() {
	hoveredElements--;
	if (hoveredElements == 0) {
		$movingTooltip.stop().animate(
			{
				opacity: 0
			},
			{
 				duration: 200,
 				queue: false
			}
		);
		$movingTooltipPointer.stop().animate(
			{
				opacity: 0
			},
			{
 				duration: 200,
 				queue: false
			}
		);
 		$movingTooltipFinalContent.stop().animate(
 			{
 				opacity: '0'
 			},
 			{
 				duration: 200,
 				queue: false
 			}
 		);
	}
}

function headerElementClick() {
	var $element = $(this);
	var pageId = $element.data("associated-page");
	if (typeof pageId !== 'undefined')
	{
		scrollToPageId(pageId);
	}
}

function scrollToPageId(pageId) {
	var targetPageOffset = $("#" + pageId).offset().left;
	var offsetDelta = $wikiBrowserHost.scrollLeft();
	// Center the target page
	var freeSpace = $wikiBrowserHost.width() - 600;
	if (freeSpace > 0) {

		offsetDelta -= freeSpace * 0.5;
	}
	$wikiBrowserHost.animate({scrollLeft: offsetDelta + targetPageOffset}, 400);
}
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const FeedParser = require('feedparser');

const extractBlogEntries = function (results, blog, index) {
    const categories = blog.categories;
    const sourcexml = './feeds/' + blog.id + '.xml';

    const feedparser = new FeedParser();
    const stream = fs.createReadStream(sourcexml);

    stream.on('error', function (error) {
        console.log('generateEntries: BLOGID ' + blog.id + ' (READ ERROR) ' + error.message);
    })
    stream.pipe(feedparser);

    feedparser.on('error', function (error) {
        console.log('generateEntries: BLOGID ' + blog.id+  ' (PARSE ERROR) ' + error);
    });

    feedparser.on('readable', function () {
        // This is where the action is!
        const stream = this;

        const today = new Date();
        const cutoff = new Date(today.getTime() - 31 * 24 * 60 * 60 * 1000); // only posts from past 31 days (ignoring leap seconds etc.)
        let item;
        while ((item = stream.read())) {
            const itemDate = item.pubdate || item.date;
            if ((cutoff < itemDate) && !(today < itemDate)) {
                const entry = {
                    categories: categories,
                    date: itemDate,
                    postTitle: item.title,
                    // HACK for issue #43
                    url: item.link || item['atom:link']['@']['href'],
                    blogTitle: this.meta.title || '[UNTITLED BLOG]'
                };
                results.push(entry);
            }
        }
    });
    feedparser.on('finish', function () {
        runner(index+1);
    });
};

const runner = (index) => {
  if (FeedsJson.blogs[index]) {
    extractBlogEntries(results, FeedsJson.blogs[index], index);
  }
  else {
      fs.writeFileSync('./data/entries.json',JSON.stringify(results, null, 4));
      console.log('entries.json has been saved');
  }
}

const data = fs.readFileSync(path.resolve(__dirname, '../data/feeds.json'), 'utf8');
const FeedsJson = JSON.parse(data);
const results = [];

runner(0);
<commit_msg>[generateEntries] sort results before writing to file Fixes #90<commit_after>const fs = require('fs');
const path = require('path');
const FeedParser = require('feedparser');

const extractBlogEntries = function (results, blog, index) {
    const categories = blog.categories;
    const sourcexml = './feeds/' + blog.id + '.xml';

    const feedparser = new FeedParser();
    const stream = fs.createReadStream(sourcexml);

    stream.on('error', function (error) {
        console.log('generateEntries: BLOGID ' + blog.id + ' (READ ERROR) ' + error.message);
    })
    stream.pipe(feedparser);

    feedparser.on('error', function (error) {
        console.log('generateEntries: BLOGID ' + blog.id+  ' (PARSE ERROR) ' + error);
    });

    feedparser.on('readable', function () {
        // This is where the action is!
        const stream = this;

        const today = new Date();
        const cutoff = new Date(today.getTime() - 31 * 24 * 60 * 60 * 1000); // only posts from past 31 days (ignoring leap seconds etc.)
        let item;
        while ((item = stream.read())) {
            const itemDate = item.pubdate || item.date;
            if ((cutoff < itemDate) && !(today < itemDate)) {
                const entry = {
                    categories: categories,
                    date: itemDate,
                    postTitle: item.title,
                    // HACK for issue #43
                    url: item.link || item['atom:link']['@']['href'],
                    blogTitle: this.meta.title || '[UNTITLED BLOG]'
                };
                results.push(entry);
            }
        }
    });
    feedparser.on('finish', function () {
        runner(index+1);
    });
};

const runner = (index) => {
  if (FeedsJson.blogs[index]) {
    extractBlogEntries(results, FeedsJson.blogs[index], index);
  }
  else {
      // sort by descending date to ensure pages and feeds are written in the expected order
      results.sort((a,b) => b.date - a.date);
      fs.writeFileSync('./data/entries.json',JSON.stringify(results, null, 4));
      console.log('entries.json has been saved');
  }
}

const data = fs.readFileSync(path.resolve(__dirname, '../data/feeds.json'), 'utf8');
const FeedsJson = JSON.parse(data);
const results = [];

runner(0);
<|endoftext|>


<commit_before>// frontpagejs
// Radio Front Page

import React from 'react';
// React-Router
import {
  Router,
  Route,
  IndexRoute,
  browserHistory,
  IndexLink,
} from 'react-router';
import { connect } from 'react-redux';
import ReactGA from 'react-ga';
import { Grid, Col } from 'react-bootstrap';

// Redux Components
import ShowsContent from './components/ShowsContent';
import EventList from './components/EventList';
import DJList from './components/DJList/DJList';
import ShowPage from './components/ShowPage';
import EventPage from './components/EventPage';
import DJPage from './components/DJPage';

// Non-Redux Components
import TriangleCanvas from './components/TriangleCanvas';
import FrontPageNavbar from './components/FrontPageNavbar';
import StreamBar from './components/StreamBar/StreamBar';
import SocialPanel from './components/SocialPanel';
import ShowInfo from './components/ShowInfo';
import WaterFallContent from './components/WaterFallContent';
import Error404Page from './components/Error404Page';
import StreamIssuesPage from './components/StreamIssuesPage';
import AboutPage from './components/AboutPage';
import PromoBanner from './components/PromoBanner';
import SoccerUpdate from './components/SoccerUpdate';

// Common Components
import RectImage from '../common/RectImage';

import './frontpage.scss';

// Google analytics helper

ReactGA.initialize('UA-21406547-20');

function logPageView() {
  ReactGA.set({ page: window.location.pathname });
  ReactGA.pageview(window.location.pathname);
}

// Misc

// styling

// temp data
// const sampleFeaturedShow = {"title":"Pirate Radio","id":12,"day":"Mon","time":"10pm","djs":{"chris":"DJ Jamburglar"},"genre":"Psychedelic","blurb":"Life is a trip... Sail away to undiscovered psychic frontiers with underground psychedelic jams","public":true,"pages":[],"episodes":[]};

/**
Frontpage UI, including radio stream, navbar, and triangle background.
Expects children components for tab contents

@prop nowPlaying: show to display as live
@prop spotlight: show to display as spotlight
@prop children: components to display in content area
* */

// update social media links here
// img is the base name of the Font Awesome icon
// Move to a separate file later?
const socialMediaLinks = [
  {
    img: 'facebook',
    link: 'https://www.facebook.com/uclaradio',
  },
  {
    img: 'twitter',
    link: 'https://twitter.com/UCLAradio',
  },
  {
    img: 'instagram',
    link: 'https://www.instagram.com/uclaradio/',
  },
  {
    img: 'snapchat-ghost',
    link: 'https://www.snapchat.com/add/uclaradio',
  },
];

const FrontpageContent = React.createClass({
  render() {
    const showPlaying =
      this.props.nowPlaying && this.props.nowPlaying.title != null;
    return (

        {/* <TriangleCanvas xColors={theme.timezoneColorScheme()}> */}
        <TriangleCanvas xColors="Spectral">

            <Grid>
              <Col xs={12} md={3} className="frontpageCol">
                <IndexLink to="/" activeClassName="active">

                    <RectImage
                      maxWidth="350px"
                      src="/img/uclaradio-black.png"
                    />

                </IndexLink>
                <SocialPanel links={socialMediaLinks} size="2x" />
                <ShowInfo title="Current Show" show={this.props.nowPlaying} />
                {/* Show Spotlight */}
                <ShowInfo title="Spotlight" show={this.props.spotlight} />
              </Col>

              <Col xs={12} md={9} className="frontpageCol">
                <SoccerUpdate />
                {/* Show of the Month */}
                <PromoBanner />
                <FrontPageNavbar />
                {this.props.children}
              </Col>
            </Grid>

          <StreamBar
            currentShowTitle={showPlaying ? this.props.nowPlaying.title : null}
          />
        </TriangleCanvas>

    );
  },
});

/**
Redux container (wrapper) for FrontpageContent to provide props:
  nowPlaying, spotlight
* */

const mapStateToProps = state => {
  const props = {};

  // set nowPlaying & spotlight shows if found
  for (
    let showIndex = 0;
    showIndex < state.shows.shows.length;
    showIndex += 1
  ) {
    const show = state.shows.shows[showIndex];
    if (show.id === state.shows.nowPlayingID) {
      props.nowPlaying = show;
    }
    // note: only set spotlight show if different from current show
    if (
      show.id === state.shows.spotlightID &&
      show.id !== state.shows.nowPlayingID
    ) {
      props.spotlight = show;
    }
  }

  const spotlight = state.shows.shows.find(
    show => show.id == state.shows.spotlightID
  );
  if (spotlight && spotlight !== -1) {
    props.spotlight = spotlight;
  }

  return props;
};
const FrontpageContentContainer = connect(mapStateToProps)(FrontpageContent);

// react-router routes
// each path represents a page of our frontpage with a component
const routes = (
  <Route path="/" component={FrontpageContentContainer}>
    <IndexRoute component={WaterFallContent} />
    <Route path="/djs" component={DJList} />
    <Route path="/djs/:djName" component={DJPage} />
    <Route path="/events" component={EventList} />
    <Route path="/shows" components={ShowsContent} />
    <Route path="/shows/:showID" component={ShowPage} />
    <Route path="/events/:eventID" component={EventPage} />
    <Route path="/streamIssues" component={StreamIssuesPage} />
    <Route path="/about" component={AboutPage} />
    <Route path="*" component={Error404Page} />
  </Route>
);

/**
Wrapper component, allows to use react-router to switch pages
also sets some data in store

@prop updateNowPlaying: () => refresh now playing by requesting new data from server
@prop setSpotlightShowID: (showID) => update the spotlight to be a show with the provided showID
@prop updateShows: () => should fetch updated list of shows and update store
* */
const Frontpage = React.createClass({
  componentWillMount() {
    // refresh live show info now and every 30 seconds
    this.props.updateNowPlaying();
    this.interval = setInterval(this.props.updateNowPlaying, 30 * 1000);

    // update now playing and fetch initial shows data
    // 05/10/18 - "Geek Squad"
    this.props.setSpotlightShowID(462);
    this.props.updateShows();
  },
  componentWillUnmount() {
    clearInterval(this.interval);
  },
  render() {
    return (
      <Router history={browserHistory} onUpdate={logPageView}>
        {routes}
      </Router>
    );
  },
});

export default Frontpage;
<commit_msg>Set Spotlight Show to Half-Time<commit_after>// frontpagejs
// Radio Front Page

import React from 'react';
// React-Router
import {
  Router,
  Route,
  IndexRoute,
  browserHistory,
  IndexLink,
} from 'react-router';
import { connect } from 'react-redux';
import ReactGA from 'react-ga';
import { Grid, Col } from 'react-bootstrap';

// Redux Components
import ShowsContent from './components/ShowsContent';
import EventList from './components/EventList';
import DJList from './components/DJList/DJList';
import ShowPage from './components/ShowPage';
import EventPage from './components/EventPage';
import DJPage from './components/DJPage';

// Non-Redux Components
import TriangleCanvas from './components/TriangleCanvas';
import FrontPageNavbar from './components/FrontPageNavbar';
import StreamBar from './components/StreamBar/StreamBar';
import SocialPanel from './components/SocialPanel';
import ShowInfo from './components/ShowInfo';
import WaterFallContent from './components/WaterFallContent';
import Error404Page from './components/Error404Page';
import StreamIssuesPage from './components/StreamIssuesPage';
import AboutPage from './components/AboutPage';
import PromoBanner from './components/PromoBanner';
import SoccerUpdate from './components/SoccerUpdate';

// Common Components
import RectImage from '../common/RectImage';

import './frontpage.scss';

// Google analytics helper

ReactGA.initialize('UA-21406547-20');

function logPageView() {
  ReactGA.set({ page: window.location.pathname });
  ReactGA.pageview(window.location.pathname);
}

// Misc

// styling

// temp data
// const sampleFeaturedShow = {"title":"Pirate Radio","id":12,"day":"Mon","time":"10pm","djs":{"chris":"DJ Jamburglar"},"genre":"Psychedelic","blurb":"Life is a trip... Sail away to undiscovered psychic frontiers with underground psychedelic jams","public":true,"pages":[],"episodes":[]};

/**
Frontpage UI, including radio stream, navbar, and triangle background.
Expects children components for tab contents

@prop nowPlaying: show to display as live
@prop spotlight: show to display as spotlight
@prop children: components to display in content area
* */

// update social media links here
// img is the base name of the Font Awesome icon
// Move to a separate file later?
const socialMediaLinks = [
  {
    img: 'facebook',
    link: 'https://www.facebook.com/uclaradio',
  },
  {
    img: 'twitter',
    link: 'https://twitter.com/UCLAradio',
  },
  {
    img: 'instagram',
    link: 'https://www.instagram.com/uclaradio/',
  },
  {
    img: 'snapchat-ghost',
    link: 'https://www.snapchat.com/add/uclaradio',
  },
];

const FrontpageContent = React.createClass({
  render() {
    const showPlaying =
      this.props.nowPlaying && this.props.nowPlaying.title != null;
    return (

        {/* <TriangleCanvas xColors={theme.timezoneColorScheme()}> */}
        <TriangleCanvas xColors="Spectral">

            <Grid>
              <Col xs={12} md={3} className="frontpageCol">
                <IndexLink to="/" activeClassName="active">

                    <RectImage
                      maxWidth="350px"
                      src="/img/uclaradio-black.png"
                    />

                </IndexLink>
                <SocialPanel links={socialMediaLinks} size="2x" />
                <ShowInfo title="Current Show" show={this.props.nowPlaying} />
                {/* Show Spotlight */}
                <ShowInfo title="Spotlight" show={this.props.spotlight} />
              </Col>

              <Col xs={12} md={9} className="frontpageCol">
                <SoccerUpdate />
                {/* Show of the Month */}
                <PromoBanner />
                <FrontPageNavbar />
                {this.props.children}
              </Col>
            </Grid>

          <StreamBar
            currentShowTitle={showPlaying ? this.props.nowPlaying.title : null}
          />
        </TriangleCanvas>

    );
  },
});

/**
Redux container (wrapper) for FrontpageContent to provide props:
  nowPlaying, spotlight
* */

const mapStateToProps = state => {
  const props = {};

  // set nowPlaying & spotlight shows if found
  for (
    let showIndex = 0;
    showIndex < state.shows.shows.length;
    showIndex += 1
  ) {
    const show = state.shows.shows[showIndex];
    if (show.id === state.shows.nowPlayingID) {
      props.nowPlaying = show;
    }
    // note: only set spotlight show if different from current show
    if (
      show.id === state.shows.spotlightID &&
      show.id !== state.shows.nowPlayingID
    ) {
      props.spotlight = show;
    }
  }

  const spotlight = state.shows.shows.find(
    show => show.id == state.shows.spotlightID
  );
  if (spotlight && spotlight !== -1) {
    props.spotlight = spotlight;
  }

  return props;
};
const FrontpageContentContainer = connect(mapStateToProps)(FrontpageContent);

// react-router routes
// each path represents a page of our frontpage with a component
const routes = (
  <Route path="/" component={FrontpageContentContainer}>
    <IndexRoute component={WaterFallContent} />
    <Route path="/djs" component={DJList} />
    <Route path="/djs/:djName" component={DJPage} />
    <Route path="/events" component={EventList} />
    <Route path="/shows" components={ShowsContent} />
    <Route path="/shows/:showID" component={ShowPage} />
    <Route path="/events/:eventID" component={EventPage} />
    <Route path="/streamIssues" component={StreamIssuesPage} />
    <Route path="/about" component={AboutPage} />
    <Route path="*" component={Error404Page} />
  </Route>
);

/**
Wrapper component, allows to use react-router to switch pages
also sets some data in store

@prop updateNowPlaying: () => refresh now playing by requesting new data from server
@prop setSpotlightShowID: (showID) => update the spotlight to be a show with the provided showID
@prop updateShows: () => should fetch updated list of shows and update store
* */
const Frontpage = React.createClass({
  componentWillMount() {
    // refresh live show info now and every 30 seconds
    this.props.updateNowPlaying();
    this.interval = setInterval(this.props.updateNowPlaying, 30 * 1000);

    // update now playing and fetch initial shows data
    // 10/29/18 - "Half-Time"
    this.props.setSpotlightShowID(615);
    this.props.updateShows();
  },
  componentWillUnmount() {
    clearInterval(this.interval);
  },
  render() {
    return (
      <Router history={browserHistory} onUpdate={logPageView}>
        {routes}
      </Router>
    );
  },
});

export default Frontpage;
<|endoftext|>


<commit_before>

// UTILS + MISC  ///////////////////////////////////////////////////////

// retrieves parameter from the URL?query=string.
window.getURLParam = function(param) {
  var v = document.URL;
  var i = v.indexOf(param);
  if(i <= -1) return '';
  v = v.substr(i);
  i = v.indexOf("&");
  if(i >= 0) v = v.substr(0, i);
  return v.replace(param+"=","");
}

// read cookie by name.
// http://stackoverflow.com/a/5639455/1684530 by cwolves
var cookies;
window.readCookie = function(name,c,C,i){
  if(cookies) return cookies[name];
  c = document.cookie.split('; ');
  cookies = {};
  for(i=c.length-1; i>=0; i--){
    C = c[i].split('=');
    cookies[C[0]] = unescape(C[1]);
  }
  return cookies[name];
}

window.writeCookie = function(name, val) {
  document.cookie = name + "=" + val + '; expires=Thu, 31 Dec 2020 23:59:59 GMT; path=/';
}

// add thousand separators to given number.
// http://stackoverflow.com/a/1990590/1684530 by Doug Neiner.
window.digits = function(d) {
  return (d+"").replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1 ");
}

// posts AJAX request to Ingress API.
// action: last part of the actual URL, the rpc/dashboard. is
//         added automatically
// data: JSON data to post. method will be derived automatically from
//       action, but may be overridden. Expects to be given Hash.
//       Strings are not supported.
// success: method to call on success. See jQuery API docs for avail-
//          able arguments: http://api.jquery.com/jQuery.ajax/
// error: see above. Additionally it is logged if the request failed.
window.postAjax = function(action, data, success, error) {
  data = JSON.stringify($.extend({method: 'dashboard.'+action}, data));
  var remove = function(data, textStatus, jqXHR) { window.requests.remove(jqXHR); };
  var errCnt = function(jqXHR) { window.failedRequestCount++; window.requests.remove(jqXHR); };
  return $.ajax({
    // use full URL to avoid issues depending on how people set their
    // slash. See:
    // https://github.com/breunigs/ingress-intel-total-conversion/issues/56
    url: 'http://www.ingress.com/rpc/dashboard.'+action,
    type: 'POST',
    data: data,
    dataType: 'json',
    success: [remove, success],
    error: error ? [errCnt, error] : errCnt,
    contentType: 'application/json; charset=utf-8',
    beforeSend: function(req) {
      req.setRequestHeader('X-CSRFToken', readCookie('csrftoken'));
    }
  });
}

// converts unix timestamps to HH:mm:ss format if it was today;
// otherwise it returns YYYY-MM-DD
window.unixTimeToString = function(time, full) {
  if(!time) return null;
  var d = new Date(typeof time === 'string' ? parseInt(time) : time);
  var time = d.toLocaleTimeString();
  var date = d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate();
  if(typeof full !== 'undefined' && full) return date + ' ' + time;
  if(d.toDateString() == new Date().toDateString())
    return time;
  else
    return date;
}

window.unixTimeToHHmm = function(time) {
  if(!time) return null;
  var d = new Date(typeof time === 'string' ? parseInt(time) : time);
  var h = '' + d.getHours(); h = h.length === 1 ? '0' + h : h;
  var s = '' + d.getMinutes(); s = s.length === 1 ? '0' + s : s;
  return  h + ':' + s;
}

window.rangeLinkClick = function() {
  if(window.portalRangeIndicator)
    window.map.fitBounds(window.portalRangeIndicator.getBounds());
  if(window.isSmartphone)
    window.smartphone.mapButton.click();
}

window.reportPortalIssue = function(info) {
  var t = 'Redirecting you to a Google Help Page. Once there, click on “Contact Us” in the upper right corner.\n\nThe text box contains all necessary information. Press CTRL+C to copy it.';
  var d = window.portals[window.selectedPortal].options.details;

  var info = 'Your Nick: ' + PLAYER.nickname + '        '
    + 'Portal: ' + d.portalV2.descriptiveText.TITLE + '        '
    + 'Location: ' + d.portalV2.descriptiveText.ADDRESS
    +' (lat ' + (d.locationE6.latE6/1E6) + '; lng ' + (d.locationE6.lngE6/1E6) + ')';

  //codename, approx addr, portalname
  if(prompt(t, info) !== null)
    location.href = 'https://support.google.com/ingress?hl=en';
}

window._storedPaddedBounds = undefined;
window.getPaddedBounds = function() {
  if(_storedPaddedBounds === undefined) {
    map.on('zoomstart zoomend movestart moveend', function() {
      window._storedPaddedBounds = null;
    });
  }
  if(window._storedPaddedBounds) return window._storedPaddedBounds;

  var p = window.map.getBounds().pad(VIEWPORT_PAD_RATIO);
  window._storedPaddedBounds = p;
  return p;
}

window.renderLimitReached = function() {
  if(Object.keys(portals).length >= MAX_DRAWN_PORTALS) return true;
  if(Object.keys(links).length >= MAX_DRAWN_LINKS) return true;
  if(Object.keys(fields).length >= MAX_DRAWN_FIELDS) return true;
  return false;
}

window.getMinPortalLevel = function() {
  var z = map.getZoom();
  if(z >= 16) return 0;
  var conv = ['impossible', 8,7,7,6,6,5,5,4,4,3,3,2,2,1,1];
  return conv[z];
}

// returns number of pixels left to scroll down before reaching the
// bottom. Works similar to the native scrollTop function.
window.scrollBottom = function(elm) {
  if(typeof elm === 'string') elm = $(elm);
  return elm.get(0).scrollHeight - elm.innerHeight() - elm.scrollTop();
}

window.zoomToAndShowPortal = function(guid, latlng) {
  map.setView(latlng, 17);
  // if the data is available, render it immediately. Otherwise defer
  // until it becomes available.
  if(window.portals[guid])
    renderPortalDetails(guid);
  else
    urlPortal = guid;
}

// translates guids to entity types
window.getTypeByGuid = function(guid) {
  // portals end in “.11” or “.12“, links in “.9", fields in “.b”
  // .11 == portals
  // .12 == portals
  // .9  == links
  // .b  == fields
  // .c  == player/creator
  // .d  == chat messages
  //
  // others, not used in web:
  // .5  == resources (burster/resonator)
  // .6  == XM
  // .4  == media items, maybe all droppped resources (?)
  // resonator guid is [portal guid]-resonator-[slot]
  switch(guid.slice(33)) {
    case '11':
    case '12':
      return TYPE_PORTAL;

    case '9':
      return TYPE_LINK;

    case 'b':
      return TYPE_FIELD;

    case 'c':
      return TYPE_PLAYER;

    case 'd':
      return TYPE_CHAT;

    default:
      if(guid.slice(-11,-2) == 'resonator') return TYPE_RESONATOR;
      return TYPE_UNKNOWN;
  }
}

String.prototype.capitalize = function() {
    return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
}

// http://stackoverflow.com/a/646643/1684530 by Bergi and CMS
if (typeof String.prototype.startsWith !== 'function') {
  String.prototype.startsWith = function (str){
    return this.slice(0, str.length) === str;
  };
}

window.prettyEnergy = function(nrg) {
  return nrg> 1000 ? Math.round(nrg/1000) + ' k': nrg;
}

window.setPermaLink = function(elm) {
  var c = map.getCenter();
  var lat = Math.round(c.lat*1E6);
  var lng = Math.round(c.lng*1E6);
  var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + (map.getZoom()-1);
  $(elm).attr('href',  'http://www.ingress.com/intel?' + qry);
}

window.uniqueArray = function(arr) {
  return $.grep(arr, function(v, i) {
    return $.inArray(v, arr) === i;
  });
}

window.genFourColumnTable = function(blocks) {
  var t = $.map(blocks, function(detail, index) {
    if(!detail) return '';
    if(index % 2 === 0)
      return '
'+detail[1]+'	'+detail[0]+'	';
    else
      return '    '+detail[0]+'	'+detail[1]+'
';
  }).join('');
  if(t.length % 2 === 1) t + '
';
  return t;
}

// converts given text with newlines (\n) and tabs (\t) to a HTML
// table automatically.
window.convertTextToTableMagic = function(text) {
  // check if it should be converted to a table
  if(!text.match(/\t/)) return text.replace(/\n/g, '
');

  var data = [];
  var columnCount = 0;

  // parse data
  var rows = text.split('\n');
  $.each(rows, function(i, row) {
    data[i] = row.split('\t');
    if(data[i].length > columnCount) columnCount = data[i].length;
  });

  // build the table
  var table = '
';
  $.each(data, function(i, row) {
    table += '
';
    $.each(data[i], function(k, cell) {
      var attributes = '';
      if(k === 0 && data[i].length < columnCount) {
        attributes = ' colspan="'+(columnCount - data[i].length + 1)+'"';
      }
      table += ''+cell+'	';
    });
    table += '
';
  });
  table += '
';
  return table;
}
<commit_msg>don’t render resources outside the view port when the render limit is about to be hit<commit_after>

// UTILS + MISC  ///////////////////////////////////////////////////////

// retrieves parameter from the URL?query=string.
window.getURLParam = function(param) {
  var v = document.URL;
  var i = v.indexOf(param);
  if(i <= -1) return '';
  v = v.substr(i);
  i = v.indexOf("&");
  if(i >= 0) v = v.substr(0, i);
  return v.replace(param+"=","");
}

// read cookie by name.
// http://stackoverflow.com/a/5639455/1684530 by cwolves
var cookies;
window.readCookie = function(name,c,C,i){
  if(cookies) return cookies[name];
  c = document.cookie.split('; ');
  cookies = {};
  for(i=c.length-1; i>=0; i--){
    C = c[i].split('=');
    cookies[C[0]] = unescape(C[1]);
  }
  return cookies[name];
}

window.writeCookie = function(name, val) {
  document.cookie = name + "=" + val + '; expires=Thu, 31 Dec 2020 23:59:59 GMT; path=/';
}

// add thousand separators to given number.
// http://stackoverflow.com/a/1990590/1684530 by Doug Neiner.
window.digits = function(d) {
  return (d+"").replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1 ");
}

// posts AJAX request to Ingress API.
// action: last part of the actual URL, the rpc/dashboard. is
//         added automatically
// data: JSON data to post. method will be derived automatically from
//       action, but may be overridden. Expects to be given Hash.
//       Strings are not supported.
// success: method to call on success. See jQuery API docs for avail-
//          able arguments: http://api.jquery.com/jQuery.ajax/
// error: see above. Additionally it is logged if the request failed.
window.postAjax = function(action, data, success, error) {
  data = JSON.stringify($.extend({method: 'dashboard.'+action}, data));
  var remove = function(data, textStatus, jqXHR) { window.requests.remove(jqXHR); };
  var errCnt = function(jqXHR) { window.failedRequestCount++; window.requests.remove(jqXHR); };
  return $.ajax({
    // use full URL to avoid issues depending on how people set their
    // slash. See:
    // https://github.com/breunigs/ingress-intel-total-conversion/issues/56
    url: 'http://www.ingress.com/rpc/dashboard.'+action,
    type: 'POST',
    data: data,
    dataType: 'json',
    success: [remove, success],
    error: error ? [errCnt, error] : errCnt,
    contentType: 'application/json; charset=utf-8',
    beforeSend: function(req) {
      req.setRequestHeader('X-CSRFToken', readCookie('csrftoken'));
    }
  });
}

// converts unix timestamps to HH:mm:ss format if it was today;
// otherwise it returns YYYY-MM-DD
window.unixTimeToString = function(time, full) {
  if(!time) return null;
  var d = new Date(typeof time === 'string' ? parseInt(time) : time);
  var time = d.toLocaleTimeString();
  var date = d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate();
  if(typeof full !== 'undefined' && full) return date + ' ' + time;
  if(d.toDateString() == new Date().toDateString())
    return time;
  else
    return date;
}

window.unixTimeToHHmm = function(time) {
  if(!time) return null;
  var d = new Date(typeof time === 'string' ? parseInt(time) : time);
  var h = '' + d.getHours(); h = h.length === 1 ? '0' + h : h;
  var s = '' + d.getMinutes(); s = s.length === 1 ? '0' + s : s;
  return  h + ':' + s;
}

window.rangeLinkClick = function() {
  if(window.portalRangeIndicator)
    window.map.fitBounds(window.portalRangeIndicator.getBounds());
  if(window.isSmartphone)
    window.smartphone.mapButton.click();
}

window.reportPortalIssue = function(info) {
  var t = 'Redirecting you to a Google Help Page. Once there, click on “Contact Us” in the upper right corner.\n\nThe text box contains all necessary information. Press CTRL+C to copy it.';
  var d = window.portals[window.selectedPortal].options.details;

  var info = 'Your Nick: ' + PLAYER.nickname + '        '
    + 'Portal: ' + d.portalV2.descriptiveText.TITLE + '        '
    + 'Location: ' + d.portalV2.descriptiveText.ADDRESS
    +' (lat ' + (d.locationE6.latE6/1E6) + '; lng ' + (d.locationE6.lngE6/1E6) + ')';

  //codename, approx addr, portalname
  if(prompt(t, info) !== null)
    location.href = 'https://support.google.com/ingress?hl=en';
}

window._storedPaddedBounds = undefined;
window.getPaddedBounds = function() {
  if(_storedPaddedBounds === undefined) {
    map.on('zoomstart zoomend movestart moveend', function() {
      window._storedPaddedBounds = null;
    });
  }
  if(renderLimitReached(0.7)) return window.map.getBounds();
  if(window._storedPaddedBounds) return window._storedPaddedBounds;

  var p = window.map.getBounds().pad(VIEWPORT_PAD_RATIO);
  window._storedPaddedBounds = p;
  return p;
}

// returns true if the render limit has been reached. The default ratio
// is 1, which means it will tell you if there are more items drawn than
// acceptable. A value of 0.9 will tell you if 90% of the amount of
// acceptable entities have been drawn. You can use this to heuristi-
// cally detect if the render limit will be hit.
window.renderLimitReached = function(ratio) {
  ratio = ratio || 1;
  if(Object.keys(portals).length*ratio >= MAX_DRAWN_PORTALS) return true;
  if(Object.keys(links).length*ratio >= MAX_DRAWN_LINKS) return true;
  if(Object.keys(fields).length*ratio >= MAX_DRAWN_FIELDS) return true;
  return false;
}

window.getMinPortalLevel = function() {
  var z = map.getZoom();
  if(z >= 16) return 0;
  var conv = ['impossible', 8,7,7,6,6,5,5,4,4,3,3,2,2,1,1];
  return conv[z];
}

// returns number of pixels left to scroll down before reaching the
// bottom. Works similar to the native scrollTop function.
window.scrollBottom = function(elm) {
  if(typeof elm === 'string') elm = $(elm);
  return elm.get(0).scrollHeight - elm.innerHeight() - elm.scrollTop();
}

window.zoomToAndShowPortal = function(guid, latlng) {
  map.setView(latlng, 17);
  // if the data is available, render it immediately. Otherwise defer
  // until it becomes available.
  if(window.portals[guid])
    renderPortalDetails(guid);
  else
    urlPortal = guid;
}

// translates guids to entity types
window.getTypeByGuid = function(guid) {
  // portals end in “.11” or “.12“, links in “.9", fields in “.b”
  // .11 == portals
  // .12 == portals
  // .9  == links
  // .b  == fields
  // .c  == player/creator
  // .d  == chat messages
  //
  // others, not used in web:
  // .5  == resources (burster/resonator)
  // .6  == XM
  // .4  == media items, maybe all droppped resources (?)
  // resonator guid is [portal guid]-resonator-[slot]
  switch(guid.slice(33)) {
    case '11':
    case '12':
      return TYPE_PORTAL;

    case '9':
      return TYPE_LINK;

    case 'b':
      return TYPE_FIELD;

    case 'c':
      return TYPE_PLAYER;

    case 'd':
      return TYPE_CHAT;

    default:
      if(guid.slice(-11,-2) == 'resonator') return TYPE_RESONATOR;
      return TYPE_UNKNOWN;
  }
}

String.prototype.capitalize = function() {
    return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
}

// http://stackoverflow.com/a/646643/1684530 by Bergi and CMS
if (typeof String.prototype.startsWith !== 'function') {
  String.prototype.startsWith = function (str){
    return this.slice(0, str.length) === str;
  };
}

window.prettyEnergy = function(nrg) {
  return nrg> 1000 ? Math.round(nrg/1000) + ' k': nrg;
}

window.setPermaLink = function(elm) {
  var c = map.getCenter();
  var lat = Math.round(c.lat*1E6);
  var lng = Math.round(c.lng*1E6);
  var qry = 'latE6='+lat+'&lngE6='+lng+'&z=' + (map.getZoom()-1);
  $(elm).attr('href',  'http://www.ingress.com/intel?' + qry);
}

window.uniqueArray = function(arr) {
  return $.grep(arr, function(v, i) {
    return $.inArray(v, arr) === i;
  });
}

window.genFourColumnTable = function(blocks) {
  var t = $.map(blocks, function(detail, index) {
    if(!detail) return '';
    if(index % 2 === 0)
      return '
'+detail[1]+'	'+detail[0]+'	';
    else
      return '    '+detail[0]+'	'+detail[1]+'
';
  }).join('');
  if(t.length % 2 === 1) t + '
';
  return t;
}

// converts given text with newlines (\n) and tabs (\t) to a HTML
// table automatically.
window.convertTextToTableMagic = function(text) {
  // check if it should be converted to a table
  if(!text.match(/\t/)) return text.replace(/\n/g, '
');

  var data = [];
  var columnCount = 0;

  // parse data
  var rows = text.split('\n');
  $.each(rows, function(i, row) {
    data[i] = row.split('\t');
    if(data[i].length > columnCount) columnCount = data[i].length;
  });

  // build the table
  var table = '
';
  $.each(data, function(i, row) {
    table += '
';
    $.each(data[i], function(k, cell) {
      var attributes = '';
      if(k === 0 && data[i].length < columnCount) {
        attributes = ' colspan="'+(columnCount - data[i].length + 1)+'"';
      }
      table += ''+cell+'	';
    });
    table += '
';
  });
  table += '
';
  return table;
}
<|endoftext|>


<commit_before>//date.js - custom extended functions for date object
//TODO: change from extending to wrapper class

Date.prototype.setDateWithSimpleFormat = function (dateString) {
    //dateString to be in dd/mm/yyyy
    var ary = dateString.split('/');
    this.setDate(ary[0]);
    this.setMonth(ary[1] - 1);
    this.setYear(ary[2]);
    this.setHours(0, 0, 0, 0);
    return this;
};

Date.prototype.setTime = function (hr, min, sec, ms) {
    this.setHours(hr, min, sec, ms);
    return this;
};

Date.prototype.addDays = function (d) {
    this.setDate(this.getDate() + d);
    return this;
};

Date.prototype.addMinutes = function (h) {
    this.setMinutes(this.getMinutes() + h);
    return this;
};

Date.prototype.rounddownToNearestHalfHour = function () {
    let currentMin = this.getMinutes();
    if (currentMin == 0) {
        return this;
    } else if (currentMin <= 30) {
        this.setMinutes(0);
    } else if (currentMin <= 59) {
        this.setMinutes(30);
    }
    return this;
}

Date.prototype.roundupToNearestHalfHour = function () {
    let currentMin = this.getMinutes();
    if (currentMin == 0) {
        return this;
    } else if (currentMin <= 30) {
        this.setMinutes(30);
    } else if (currentMin <= 59) {
        this.setMinutes(currentMin + (60 - currentMin));
    }

    console.log('roundup ' + this);
    return this;
}

Date.prototype.isDateToday = function () {
    var today = new Date().getSimpleDate();
    return today === this.getSimpleDate();
};

Date.prototype.getFormattedTime = function () {
    var timeFormatOptions = { hour: "2-digit", minute: "2-digit" };
    return this.toLocaleTimeString("en-us", timeFormatOptions);
};

Date.prototype.getFormattedDate = function () {
    var timeFormatOptions = {
        year: "numeric", month: "long",
        day: "numeric"
    };
    return this.toLocaleString("en-us", timeFormatOptions);
};

Date.prototype.getFormattedDateTime = function () {
    return this.getFormattedDate() + ', ' + this.getFormattedTime();
};

Date.prototype.getISO8601TimeStamp = function (date) {
    var pad = function (amount, width) {
        var padding = "";
        while (padding.length < width - 1 && amount < Math.pow(10, width - padding.length - 1))
            padding += "0";
        return padding + amount.toString();
    }
    date = date ? date : this;
    var offset = date.getTimezoneOffset();
    return pad(date.getFullYear(), 4) + "-" +
        pad(date.getMonth() + 1, 2) + "-" + pad(date.getDate(), 2) +
        "T" + pad(date.getHours(), 2) + ":" + pad(date.getMinutes(), 2) + ":" +
        pad(date.getSeconds(), 2) + (offset > 0 ? "-" : "+") +
        pad(Math.floor(Math.abs(offset) / 60), 2) + ":" + pad(Math.abs(offset) % 60, 2);
};

Date.prototype.getISO8601DateWithDefinedTime = function (hour, min, sec, ms) {
    this.setHours(hour, min, sec, ms);
    return this.getISO8601TimeStamp(this);
};

Date.prototype.getISO8601DateWithDefinedTimeString = function (timeStr) {
    //08:30 AM
    var tmp = timeStr.split(' ');
    var timeAry = tmp[0].split(':');

    if (tmp[1] == 'PM' && timeAry[0] < 12) {
        timeAry[0] = 12 + parseInt(timeAry[0]);
    }
    this.setHours(timeAry[0], timeAry[1], 0, 0);
    return this.getISO8601TimeStamp(this);
};

Date.prototype.getCurrentMonthNamed = function () {
    var timeFormatOptions = { month: "long" };
    return this.toLocaleString("en-us", timeFormatOptions);
};

Date.prototype.getSimpleDate = function () {
    return this.getDate() + "/" + (this.getMonth() + 1) + "/" + this.getFullYear();
};

Date.prototype.getCurrentDay = function () {
    return this.getDate();
};

Date.prototype.daysInMonth = function () {
    var d = new Date(this.getFullYear(), this.getMonth() + 1, 0);
    return d.getDate();
};

Date.prototype.getMinuteDiff = function (timeCompared) {
    var timeDiff = Math.abs(timeCompared.getTime() - this.getTime());
    var minDiff = Math.ceil(timeDiff / 60000);
    return minDiff;
}
<commit_msg>Fixed Issue #5 - Rounding error of /any function.<commit_after>//date.js - custom extended functions for date object
//TODO: change from extending to wrapper class

Date.prototype.setDateWithSimpleFormat = function (dateString) {
    //dateString to be in dd/mm/yyyy
    var ary = dateString.split('/');
    this.setDate(ary[0]);
    this.setMonth(ary[1] - 1);
    this.setYear(ary[2]);
    this.setHours(0, 0, 0, 0);
    return this;
};

Date.prototype.setTime = function (hr, min, sec, ms) {
    this.setHours(hr, min, sec, ms);
    return this;
};

Date.prototype.addDays = function (d) {
    this.setDate(this.getDate() + d);
    return this;
};

Date.prototype.addMinutes = function (h) {
    this.setMinutes(this.getMinutes() + h);
    return this;
};

Date.prototype.rounddownToNearestHalfHour = function () {
    let currentMin = this.getMinutes();
    if (currentMin == 0) {
        return this;
    } else if (currentMin < 30) {
        this.setMinutes(0);
    } else if (currentMin <= 59) {
        this.setMinutes(30);
    }
    return this;
}

Date.prototype.roundupToNearestHalfHour = function () {
    let currentMin = this.getMinutes();
    if (currentMin == 0) {
        return this;
    } else if (currentMin <= 30) {
        this.setMinutes(30);
    } else if (currentMin <= 59) {
        this.setMinutes(currentMin + (60 - currentMin));
    }
    return this;
}

Date.prototype.isDateToday = function () {
    var today = new Date().getSimpleDate();
    return today === this.getSimpleDate();
};

Date.prototype.getFormattedTime = function () {
    var timeFormatOptions = { hour: "2-digit", minute: "2-digit" };
    return this.toLocaleTimeString("en-us", timeFormatOptions);
};

Date.prototype.getFormattedDate = function () {
    var timeFormatOptions = {
        year: "numeric", month: "long",
        day: "numeric"
    };
    return this.toLocaleString("en-us", timeFormatOptions);
};

Date.prototype.getFormattedDateTime = function () {
    return this.getFormattedDate() + ', ' + this.getFormattedTime();
};

Date.prototype.getISO8601TimeStamp = function (date) {
    var pad = function (amount, width) {
        var padding = "";
        while (padding.length < width - 1 && amount < Math.pow(10, width - padding.length - 1))
            padding += "0";
        return padding + amount.toString();
    }
    date = date ? date : this;
    var offset = date.getTimezoneOffset();
    return pad(date.getFullYear(), 4) + "-" +
        pad(date.getMonth() + 1, 2) + "-" + pad(date.getDate(), 2) +
        "T" + pad(date.getHours(), 2) + ":" + pad(date.getMinutes(), 2) + ":" +
        pad(date.getSeconds(), 2) + (offset > 0 ? "-" : "+") +
        pad(Math.floor(Math.abs(offset) / 60), 2) + ":" + pad(Math.abs(offset) % 60, 2);
};

Date.prototype.getISO8601DateWithDefinedTime = function (hour, min, sec, ms) {
    this.setHours(hour, min, sec, ms);
    return this.getISO8601TimeStamp(this);
};

Date.prototype.getISO8601DateWithDefinedTimeString = function (timeStr) {
    //08:30 AM
    var tmp = timeStr.split(' ');
    var timeAry = tmp[0].split(':');

    if (tmp[1] == 'PM' && timeAry[0] < 12) {
        timeAry[0] = 12 + parseInt(timeAry[0]);
    }
    this.setHours(timeAry[0], timeAry[1], 0, 0);
    return this.getISO8601TimeStamp(this);
};

Date.prototype.getCurrentMonthNamed = function () {
    var timeFormatOptions = { month: "long" };
    return this.toLocaleString("en-us", timeFormatOptions);
};

Date.prototype.getSimpleDate = function () {
    return this.getDate() + "/" + (this.getMonth() + 1) + "/" + this.getFullYear();
};

Date.prototype.getCurrentDay = function () {
    return this.getDate();
};

Date.prototype.daysInMonth = function () {
    var d = new Date(this.getFullYear(), this.getMonth() + 1, 0);
    return d.getDate();
};

Date.prototype.getMinuteDiff = function (timeCompared) {
    var timeDiff = Math.abs(timeCompared.getTime() - this.getTime());
    var minDiff = Math.ceil(timeDiff / 60000);
    return minDiff;
}
<|endoftext|>


<commit_before>/**
 * @module deep-freeze
 * @description Provides function to deep freeze objects.
 * @version 1.0.9
 * @author Anatoliy Gatt [anatoliy.gatt@aol.com]
 * @copyright Copyright (c) 2015 Anatoliy Gatt
 * @license MIT
 */

"use strict";

/**
 * @public
 * @function deepFreeze
 * @description Deep freeze object.
 * @param {Object} object - Object to deep freeze.
 * @returns {Object} - Deep frozen object.
 */

function deepFreeze(object) {
    if(object) {
        var property, propertyKey;
        object = Object.freeze(object);
        for (propertyKey in object) {
            if(object.hasOwnProperty(propertyKey)) {
                property = object[propertyKey];
                if (((typeof property !== "object") || !(property instanceof Object)) || Object.isFrozen(property)) {
                    continue;
                }
                deepFreeze(property);
            }
        }
    }
    return object;
}

/**
 * @public
 * @description Expose function to deep freeze objects.
 * @param {Object} object - Object to deep freeze.
 * @returns {Object} - Deep frozen object.
 */

module.exports = function(object) {
    return deepFreeze(object);
};<commit_msg>Replace " with ' in definitions of all strings in deep-freeze.js<commit_after>/**
 * @module deep-freeze
 * @description Provides function to deep freeze objects.
 * @version 1.0.9
 * @author Anatoliy Gatt [anatoliy.gatt@aol.com]
 * @copyright Copyright (c) 2015 Anatoliy Gatt
 * @license MIT
 */

'use strict';

/**
 * @public
 * @function deepFreeze
 * @description Deep freeze object.
 * @param {Object} object - Object to deep freeze.
 * @returns {Object} - Deep frozen object.
 */

function deepFreeze(object) {
    if(object) {
        var property, propertyKey;
        object = Object.freeze(object);
        for (propertyKey in object) {
            if(object.hasOwnProperty(propertyKey)) {
                property = object[propertyKey];
                if (((typeof property !== 'object') || !(property instanceof Object)) || Object.isFrozen(property)) {
                    continue;
                }
                deepFreeze(property);
            }
        }
    }
    return object;
}

/**
 * @public
 * @description Expose function to deep freeze objects.
 * @param {Object} object - Object to deep freeze.
 * @returns {Object} - Deep frozen object.
 */

module.exports = function(object) {
    return deepFreeze(object);
};<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import {PieChart, BarChart} from 'react-d3';
import _ from 'lodash';
import moment from 'moment';
import Component from '../components/component.react';
import {getStats, getStatsForMonth, getStatsForWeek} from './actions';

class Stats extends Component {

  constructor(props) {
    super(props);

    this.state = {

      lastMonth: Array.from(Array(100).keys()).map(() => {
        return {
          value: Math.floor((Math.random() * 190) + 1)
        };
      })
    };
  }

  componentDidMount() {
    const { application } = this.props;
    const appUUID = (application) ? application.uuid : null;

    getStats(appUUID);
    getStatsForWeek(appUUID);
    getStatsForMonth(appUUID);
  }

  translateAllStatsToGraph(data) {
    const count = _.values(data).reduce((a, b) => a + b);

    return Object.keys(data).map(key => {
      const value = (data[key]/count)*100;

      return {
        label: `${key.toUpperCase()} (${data[key]})`,
        value: value || 0
      };
    });
  }

  translateWeekStatsToGraph(data) {
    return ['sent', 'inbox', 'outbox'].map(field => {
      return {
        name: `${field} messages`,
        values: Object.keys(data).map(date => {
          return {
            x: moment(date).format('dddd'),
            y: data[date][field]
          };
        })
      }
    });
  }

  translateMonthStatsToGraph(data) {
    return ['sent', 'inbox', 'outbox'].map(field => {
      return {
        name: `${field} messages`,
        values: Object.keys(data).map(date => {
          return {
            x: moment(date).format('M.D'),
            y: data[date][field]
          };
        })
      }
    });
  }

  render() {
    const { stats } = this.props;
    const { all, week, month } = stats.toJS();

    return (

          {this.renderStats(all)}

          {this.renderStatsForWeek(week)}

          {this.renderStatsForMonth(month)}

    );
  }

  renderStats(data) {
    if (getStats.pending || !data)
      return 'Loading all statistics ...';

    return (
      <PieChart className="left"
                data={this.translateAllStatsToGraph(data)}
                width={400}
                height={300}
                radius={100}
                innerRadius={20}
                sectorBorderColor="white"
                title="Messages"/>
    );
  }

  renderStatsForWeek(data) {
    if (getStatsForWeek.pending || !data)
      return 'Loading week statistics ...';

    return (
      <BarChart className="left"
                data={this.translateWeekStatsToGraph(data)}
                width={500}
                height={300}
                fill={'#3182bd'}
                title='Last week' />
    );
  }

  renderStatsForMonth(data) {
    if (getStatsForMonth.pending || !data)
      return 'Loading week statistics ...';

    return (
      <BarChart className="left"
                data={this.translateMonthStatsToGraph(data)}
                width={900}
                height={300}
                xAxisClassName='no-legend'
                fill={'#3182bd'}
                title='Last month' />
    );
  }

}

export default Stats;
<commit_msg>Statistics: removing not necessary constructor<commit_after>'use strict';

import React from 'react';
import {PieChart, BarChart} from 'react-d3';
import _ from 'lodash';
import moment from 'moment';
import Component from '../components/component.react';
import {getStats, getStatsForMonth, getStatsForWeek} from './actions';

class Stats extends Component {

  componentDidMount() {
    const { application } = this.props;
    const appUUID = (application) ? application.uuid : null;

    getStats(appUUID);
    getStatsForWeek(appUUID);
    getStatsForMonth(appUUID);
  }

  translateAllStatsToGraph(data) {
    const count = _.values(data).reduce((a, b) => a + b);

    return Object.keys(data).map(key => {
      const value = (data[key]/count)*100;

      return {
        label: `${key.toUpperCase()} (${data[key]})`,
        value: value || 0
      };
    });
  }

  translateWeekStatsToGraph(data) {
    return ['sent', 'inbox', 'outbox'].map(field => {
      return {
        name: `${field} messages`,
        values: Object.keys(data).map(date => {
          return {
            x: moment(date).format('dddd'),
            y: data[date][field]
          };
        })
      }
    });
  }

  translateMonthStatsToGraph(data) {
    return ['sent', 'inbox', 'outbox'].map(field => {
      return {
        name: `${field} messages`,
        values: Object.keys(data).map(date => {
          return {
            x: moment(date).format('M.D'),
            y: data[date][field]
          };
        })
      }
    });
  }

  render() {
    const { stats } = this.props;
    const { all, week, month } = stats.toJS();

    return (

          {this.renderStats(all)}

          {this.renderStatsForWeek(week)}

          {this.renderStatsForMonth(month)}

    );
  }

  renderStats(data) {
    if (getStats.pending || !data)
      return 'Loading all statistics ...';

    return (
      <PieChart className="left"
                data={this.translateAllStatsToGraph(data)}
                width={400}
                height={300}
                radius={100}
                innerRadius={20}
                sectorBorderColor="white"
                title="Messages"/>
    );
  }

  renderStatsForWeek(data) {
    if (getStatsForWeek.pending || !data)
      return 'Loading week statistics ...';

    return (
      <BarChart className="left"
                data={this.translateWeekStatsToGraph(data)}
                width={500}
                height={300}
                fill={'#3182bd'}
                title='Last week' />
    );
  }

  renderStatsForMonth(data) {
    if (getStatsForMonth.pending || !data)
      return 'Loading week statistics ...';

    return (
      <BarChart className="left"
                data={this.translateMonthStatsToGraph(data)}
                width={900}
                height={300}
                xAxisClassName='no-legend'
                fill={'#3182bd'}
                title='Last month' />
    );
  }

}

export default Stats;
<|endoftext|>


<commit_before>const Hoek = require('hoek');
const Hash = require('../lib/hash');
const Utilities = require('../lib/utilities');

const internals = {};

exports = module.exports = internals.definitions = function (settings) {

    this.settings = settings;
};

/**
 * appends a new definition object to a given collection, returns a string reference name
 *
 * @param  {string} definitionName
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @param  {Object} settings
 * @return {String}
 */
internals.definitions.prototype.append = function (definitionName, definition, currentCollection, settings) {

    let out = null;

    definition = internals.formatProperty(definition);

    // remove required if its not an array
    //if (definition.required && !Array.isArray(definition.required)) {
    //    delete definition.required;
    //}

    // remove unneeded properties
    delete definition.name;

    // find existing definition by this definitionName
    let foundDefinition = currentCollection[definitionName];
    if (foundDefinition) {
        // deep compare objects
        if (Hoek.deepEqual(foundDefinition, definition)) {
            // return existing definitionName if existing object is exactly the same
            out = definitionName;
        } else {
            // create new definition
            out = internals.append(definitionName, definition, currentCollection, true, settings);
        }
    } else {
        // create new definition
        out = internals.append(definitionName, definition, currentCollection, false, settings);
    }

    return out;
};

/**
 * Internal - appends a new definition object to a given collection, returns a string reference name
 *
 * @param  {string} definitionName
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @param  {Object} settings
 * @return {String}
 */
internals.append = function (definitionName, definition, currentCollection, forceDynamicName, settings) {

    let out;
    let foundDefinitionName;

    delete definition.optional;

    // find definitionName by matching hash of object
    if (settings.reuseDefinitions) {
        foundDefinitionName = internals.hasDefinition(definition, currentCollection);
    }
    if (foundDefinitionName) {
        out = foundDefinitionName;
    } else {
        // else create a new item using definitionName or next model number
        if (forceDynamicName) {
            if (settings.definitionPrefix === 'useLabel') {
                out = internals.nextModelName(definitionName + ' ', currentCollection);
            }
            else {
                out = internals.nextModelName('Model ', currentCollection);
            }
        }
        else {
            out = definitionName || internals.nextModelName('Model ', currentCollection);
        }
        currentCollection[out] = definition;
    }
    return out;
};

/**
 * formats a parameter for use in a definition object
 *
 * @param  {Object} parameter
 * @return {Object}
 */
internals.formatProperty = function (obj) {

    // add $ref directly to parent and delete schema
//    if (obj.schema) {
//        obj.$ref = obj.schema.$ref;
//        delete obj.schema;
//    }

    // remove emtpy properties
    obj = Utilities.deleteEmptyProperties(obj);

    // remove unneeded properties
    delete obj.name;

    return obj;
};

/**
 * creates a hash of a object
 *
 * @param  {Object} obj
 * @return {String}
 */
internals.hash = function (obj) {

    const str = JSON.stringify(obj);
    return Hash(str);
};

/**
 * creates a new unique model name
 *
 * @param  {String} nextModelNamePrefix
 * @param  {Object} currentCollection
 * @return {String}
 */
internals.nextModelName = function (nextModelNamePrefix, currentCollection) {
    let highest = 0;
    let key;
    for (key in currentCollection) {
        if (Utilities.startsWith(key, nextModelNamePrefix)) {
            let num = parseInt(key.replace(nextModelNamePrefix, ''), 10) || 0;
            if (num && num > highest) {
                highest = num;
            }
        }
    }
    return nextModelNamePrefix + (highest + 1);
};

/**
 * returns whether a collection already has a definition, returns key if found
 *
 *
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @return {String || Undefined}
 */
internals.hasDefinition = function (definition, currentCollection) {

    let key;
    let hash = internals.hash(definition);

    for (key in currentCollection) {
        let obj = currentCollection[key];

        if (hash === internals.hash(obj)) {
            return key;
        }
    }
    return null;
};
<commit_msg>linter fix<commit_after>const Hoek = require('hoek');
const Hash = require('../lib/hash');
const Utilities = require('../lib/utilities');

const internals = {};

exports = module.exports = internals.definitions = function (settings) {

    this.settings = settings;
};

/**
 * appends a new definition object to a given collection, returns a string reference name
 *
 * @param  {string} definitionName
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @param  {Object} settings
 * @return {String}
 */
internals.definitions.prototype.append = function (definitionName, definition, currentCollection, settings) {

    let out = null;

    definition = internals.formatProperty(definition);

    // remove required if its not an array
    //if (definition.required && !Array.isArray(definition.required)) {
    //    delete definition.required;
    //}

    // remove unneeded properties
    delete definition.name;

    // find existing definition by this definitionName
    let foundDefinition = currentCollection[definitionName];
    if (foundDefinition) {
        // deep compare objects
        if (Hoek.deepEqual(foundDefinition, definition)) {
            // return existing definitionName if existing object is exactly the same
            out = definitionName;
        } else {
            // create new definition
            out = internals.append(definitionName, definition, currentCollection, true, settings);
        }
    } else {
        // create new definition
        out = internals.append(definitionName, definition, currentCollection, false, settings);
    }

    return out;
};

/**
 * Internal - appends a new definition object to a given collection, returns a string reference name
 *
 * @param  {string} definitionName
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @param  {Object} settings
 * @return {String}
 */
internals.append = function (definitionName, definition, currentCollection, forceDynamicName, settings) {

    let out;
    let foundDefinitionName;

    delete definition.optional;

    // find definitionName by matching hash of object
    if (settings.reuseDefinitions) {
        foundDefinitionName = internals.hasDefinition(definition, currentCollection);
    }
    if (foundDefinitionName) {
        out = foundDefinitionName;
    } else {
        // else create a new item using definitionName or next model number
        if (forceDynamicName) {
            if (settings.definitionPrefix === 'useLabel') {
                out = internals.nextModelName(definitionName + ' ', currentCollection);
            }
            else {
                out = internals.nextModelName('Model ', currentCollection);
            }
        }
        else {
            out = definitionName || internals.nextModelName('Model ', currentCollection);
        }
        currentCollection[out] = definition;
    }
    return out;
};

/**
 * formats a parameter for use in a definition object
 *
 * @param  {Object} parameter
 * @return {Object}
 */
internals.formatProperty = function (obj) {

    // add $ref directly to parent and delete schema
    //    if (obj.schema) {
    //        obj.$ref = obj.schema.$ref;
    //            delete obj.schema;
    //    }

    // remove emtpy properties
    obj = Utilities.deleteEmptyProperties(obj);

    // remove unneeded properties
    delete obj.name;

    return obj;
};

/**
 * creates a hash of a object
 *
 * @param  {Object} obj
 * @return {String}
 */
internals.hash = function (obj) {

    const str = JSON.stringify(obj);
    return Hash(str);
};

/**
 * creates a new unique model name
 *
 * @param  {String} nextModelNamePrefix
 * @param  {Object} currentCollection
 * @return {String}
 */
internals.nextModelName = function (nextModelNamePrefix, currentCollection) {
    let highest = 0;
    let key;
    for (key in currentCollection) {
        if (Utilities.startsWith(key, nextModelNamePrefix)) {
            let num = parseInt(key.replace(nextModelNamePrefix, ''), 10) || 0;
            if (num && num > highest) {
                highest = num;
            }
        }
    }
    return nextModelNamePrefix + (highest + 1);
};

/**
 * returns whether a collection already has a definition, returns key if found
 *
 *
 * @param  {Object} definition
 * @param  {Object} currentCollection
 * @return {String || Undefined}
 */
internals.hasDefinition = function (definition, currentCollection) {

    let key;
    let hash = internals.hash(definition);

    for (key in currentCollection) {
        let obj = currentCollection[key];

        if (hash === internals.hash(obj)) {
            return key;
        }
    }
    return null;
};
<|endoftext|>