@uportal/app-framework
Version:
Application Framework for uPortal
1,076 lines (978 loc) • 37.8 kB
JavaScript
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict';
define(['angular', 'moment'], function(angular, moment) {
return angular.module('portal.widgets.controllers', [])
/**
* Base widget controller (for /partials/widget-card.html).
* First point of access for all widget types -- determines
* the type and sets launch button url for un-typed (basic) widgets.
*/
.controller('WidgetCardController', [
'$scope', '$log', '$transclude', '$timeout', 'widgetService',
function($scope, $log, $transclude, $timeout, widgetService) {
/**
* Check for widget types that require extra configuration
* (including null/undefined case), default to provided
* widget type.
* @param {Object} widget - The widget object provided by widgetService
* @return {*} - A string for the widget type
*/
var widgetType = function widgetType(widget) {
// Check for types that need handling
switch (widget.widgetType) {
case 'list-of-links':
if (widget.widgetConfig.getLinksURL) {
widgetService.getWidgetJson(widget).then(
function(links) {
if (!(links.content && links.content.links)) {
$log.warn('Undefined dynamic links content for '
+ widget.fname + ': dynamic response was [' + links + ']');
}
widget.widgetConfig.links = links.content.links;
return links.content.links;
})
.catch(function(error) {
$log.warn('List of links widget ' + widget.fname + ' failed.');
$log.error(error);
});
}
return 'list-of-links';
case null:
// If widgetType doesn't exist, show a basic widget
return 'basic';
default:
return widget.widgetType;
}
};
/**
* Enable keyboard activation of transcluded remove button
* so it plays nice with the aria-role "button"
* @param {object} event The DOM event that called the function
*/
$scope.triggerRemoveButton = function(event) {
event.preventDefault();
// If user pressed enter key, manually trigger the remove button
if (event.keyCode === 13) {
var removeButton =
angular.element(event.target.querySelector('.widget-remove'));
// Make sure we correctly targeted a button
if (removeButton[0].tagName === 'BUTTON') {
// Break current $apply cycle to ensure this fires
$timeout(function() {
removeButton.triggerHandler('click');
}, 0);
}
}
};
/**
* Initial widget setup -- gets data for a single widget
* from the provided fname attribute, makes transcluded content
* focusable
* @param {string} fname
* @param {string} failSilently
*/
$scope.initializeWidget = function(fname, failSilently) {
// Initialize scope variables
$scope.widget = {};
$scope.widgetType = '';
$scope.tabindex = '-1';
if (fname) {
// Get widget data for provided app (fname)
widgetService.getSingleWidgetData(fname)
.then(function(data) {
// Set scope variables
if (data) {
$scope.widget = data;
$scope.widgetType = widgetType(data);
$scope.failSilently = failSilently;
}
// If there's a remove button, make it focusable by keyboard
if ($transclude.isSlotFilled('removeButton')) {
$scope.tabindex = '1';
}
return data;
})
.catch(function(error) {
$log.warn('WidgetCardController couldn\'t get data for: ' + fname);
$log.error(error);
});
} else {
$log.warn('WidgetCardController didn\'t get an fname.');
}
};
// Initialize the widget
$scope.initializeWidget($scope.fname, $scope.failSilently);
}])
/**
* Controller for un-typed (basic) widgets.
* Sets launch button url for these widgets.
*/
.controller('BasicWidgetController', [
'$scope', '$log', '$localStorage', 'widgetService',
function($scope, $log, $localStorage, widgetService) {
/**
* Set the launch button url for static content,
* exclusive mode, and basic widgets
* @return {*}
*/
$scope.renderUrl = function renderUrl() {
var widget = $scope.widget;
// Check for static content or exclusive mode portlets (rare)
if (widget.altMaxUrl == false) {
if (widget.staticContent != null) {
return 'static/' + widget.fname;
} else if (widget.renderOnWeb || $localStorage.webPortletRender) {
return 'exclusive/' + widget.fname;
}
}
return widget.url;
};
}])
// External Message Controller
.controller('WidgetExternalMessageController', [
'$scope', '$log', 'widgetService',
function($scope, $log, widgetService) {
var widget = $scope.widget;
widgetService.getWidgetExternalMessage(widget).then(
function(externalMessage) {
if (externalMessage && externalMessage.messageText) {
$scope.widget.externalMessageText = externalMessage.messageText;
if (externalMessage.learnMoreUrl) {
$scope.widget.externalMessageLearnMoreUrl =
externalMessage.learnMoreUrl;
}
}
return externalMessage;
}).catch(function() {
$log.warn('Could not get external widget message for ' + widget.fname);
});
},
])
// OPTION LINK widget type
.controller('OptionLinkController', [
'$scope', '$log', 'widgetService',
function($scope, $log, widgetService) {
/**
* Set up default configuration if no config exists
*/
var configInit = function() {
$scope.config = {
singleElement: false,
arrayName: 'array',
value: 'value',
display: 'display',
};
};
/**
* Fetch additional widget data
*/
var populateWidgetContent = function() {
var widget = $scope.widget;
var config = $scope.config;
if (widget.widgetURL && widget.widgetType) {
// Initialize widget data
widget.widgetData = [];
// Get data and set widget content
widgetService.getWidgetJson(widget).then(function(data) {
if (data) {
if (config.singleElement) {
// Set the default selected url
widget.selectedUrl =
widget.widgetData[config.value];
} else if (widget.widgetData[config.arrayName] &&
widget.widgetData[config.arrayName].length > 0) {
// If multiple values come back,
// set selected url to the first entry
widget.selectedUrl =
widget.widgetData[config.arrayName][0][config.value];
}
} else {
$log.warn(
'OptionLinkController couldn\'t get json for: ' +
widget.fname
);
}
return data;
}).catch(function() {
$log.warn('could not getWidgetJson');
});
}
};
// Set default values if no config was received
if (!$scope.config) {
configInit();
$log.warn('OptionLinkController didn\'t receive a widget config');
}
// Set up widget content
populateWidgetContent();
}])
// SEARCH WITH LINKS widget type
.controller('SearchWithLinksController', [
'$scope', '$sce', function($scope, $sce) {
// Have faith our entity files aren't trying to bamboozle us
$scope.secureURL = $sce.trustAsResourceUrl($scope.config.actionURL);
}])
// RSS widget type
.controller('RssWidgetController', [
'$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
/**
* Turn the provided date string into an actual
* Date so it can be filtered into something prettier.
* @param {string} dateString - A hideously ugly date string
* @return {*} A Date object or null
*/
$scope.getPrettyDate = function(dateString) {
// Create a new date if a date string was provided, otherwise return null
return dateString ? new Date(dateString) : null;
};
/**
* Remove leading/trailing spaces from RSS titles
* @param {string} title - The RSS title
* @return {string} The trimmed title
*/
$scope.trim = function(title) {
return title.trim();
};
/**
* Set defaults if any values are missing from provided widgetConfig
*/
var checkForWidgetConfig = function() {
if (!$scope.config) {
$scope.config = {};
}
if (!$scope.config.lim) {
$scope.config.lim = 5;
}
if (!$scope.config.titleLim) {
if ($scope.config.showdate) {
$scope.config.titleLim = 35;
} else {
$scope.config.titleLim = 45;
}
}
if (!$scope.config.showShowing) {
$scope.config.showShowing = false;
}
// Set sensible fallbacks in case widget
// config would create display problems.
if ($scope.config.lim > 6) {
$scope.config.lim = 6;
}
if ($scope.config.titleLim > 50) {
$scope.config.titleLim = 50;
}
};
/**
* Initialize rss widget
*/
var initializeRssWidget = function() {
// Trigger loading spinner
$scope.loading = true;
// Only initialize if widget's json provides all the stuff we need
if ($scope.widget && $scope.widget.widgetURL) {
// Make sure config has values
checkForWidgetConfig();
// Get rss feed from provided url
widgetService.getRssAsJson($scope.widget.widgetURL)
.then(function(result) {
// If we got data, load it and turn off loading spinner
$scope.data = result ? result : {};
$scope.loading = false;
// Check for errors or emptiness and update display as needed
if ($scope.data.status !== 'ok') {
$scope.error = true;
$scope.loading = false;
} else {
if (!$scope.data.items || $scope.data.items.length == 0) {
$scope.isEmpty = true;
$scope.loading = false;
$scope.error = true;
} else {
// Show 'show more' if the feed is longer than the display limit
if (!$scope.config.showShowing &&
$scope.data.items.length > $scope.config.lim) {
$scope.config.showShowing = true;
}
}
}
return result;
})
.catch(function(error) {
// If the service couldn't get data, display error messages
$log.warn('Couldn\'t get rss as JSON');
$log.error(error);
$scope.error = true;
$scope.isEmpty = true;
$scope.loading = false;
});
}
};
// Initialize the widget
initializeRssWidget();
}])
// ACTION ITEMS widget type
.controller('ActionItemsController', [
'$scope', '$log', '$window', '$filter', 'widgetService',
function($scope, $log, $window, $filter, widgetService) {
// Scope functions
/**
* Navigate to the url provided by the action item
* @param {string} url - A path provided by the action item
*/
$scope.goToAction = function(url) {
// Go there if the url exists
if (url) {
$window.location.href = url;
}
};
// Local functions
/**
* Gets the quantity for the provided item from its feedUrl, then
* builds a new actionItem object to add to scope.
* @param {Object} item - Object for a single action item
*/
var assembleActionItemsList = function(item) {
// Get number from provided url
widgetService.getActionItemQuantity(item.feedUrl)
.then(function(data) {
// Make sure we got a something back
if (data) {
// Make sure quantity is a number
if (!Number.isInteger(data.quantity)) {
// Log problem and don't include in list
$log.warn('ACTION ITEMS CONTROLLER: '
+ 'Got non-integer quantity from ' + item.feedUrl);
// Reduce display limit (only once) to make room for error
if (!$scope.hasQuantityError) {
$scope.itemsLimit -= 1;
}
// Show error
$scope.hasQuantityError = true;
// Add an error to the error array
$scope.actionItemErrors.push({
textPlural: item.textPlural,
actionUrl: item.actionUrl,
});
} else {
// Add an action item to scope array
$scope.actionItems.push({
textSingular: item.textSingular,
textPlural: item.textPlural,
actionUrl: item.actionUrl,
quantity: data.quantity,
});
}
} else {
// no data
// Add an error to the error array
$scope.actionItemErrors.push({
textPlural: item.textPlural,
actionUrl: item.actionUrl,
});
}
return data;
})
.catch(function(error) {
// Log a service failure error
$log.warn('ACTION ITEMS CONTROLLER: '
+ 'Problem getting action item data from: ' + item.feedUrl);
$scope.hasServiceError = true;
// Add an error to the error array
$scope.actionItemErrors.push({
textPlural: item.textPlural,
actionUrl: item.actionUrl,
});
$scope.loading = false;
});
};
/**
* Make sure each action item in widgetConfig has the necessary fields
* configured and passes on for assembly if so.
*/
var checkActionItemsConfigs = function() {
// For each entry in actionItems, get the number of items
for (var i = 0; i < $scope.config.actionItems.length; i++) {
// Make sure the current item has required fields configured
if ($scope.config.actionItems[i].textSingular
&& $scope.config.actionItems[i].textPlural
&& $scope.config.actionItems[i].actionUrl
&& $scope.config.actionItems[i].feedUrl) {
// Pass current item to function to call widgetService
assembleActionItemsList($scope.config.actionItems[i]);
} else {
// Log a missing-config error
$log.warn('ACTION ITEMS CONTROLLER: '
+ 'An action item was missing one or '
+ 'more required configuration options');
if (!$scope.config.actionItems[i].textPlural) {
$scope.config.actionItems[i].textPlural = 'misconfigured things';
}
// Add an error to the error array
$scope.actionItemErrors.push({
textPlural: $scope.config.actionItems[i].textPlural,
actionUrl: $scope.config.actionItems[i].actionUrl,
});
}
// If this is the last time through the loop, turn off loading spinner
// and reorder the list by quantity
if (i === $scope.config.actionItems.length - 1) {
$scope.actionItems =
$filter('orderBy')($scope.actionItems, 'quantity', true);
$scope.loading = false;
}
}
};
/**
* Initialize bindable members and kick off error checking and
* scope assembly.
*/
var initializeActionItems = function() {
// Initialize bindable members
$scope.actionItems = [];
$scope.actionItemErrors = [];
$scope.loading = true;
$scope.hasServiceError = false;
$scope.hasQuantityError = false;
$scope.itemsLimit = 3;
$scope.launchText =
$scope.config.launchText ? $scope.config.launchText : 'See all';
// Make sure we got a widget and necessary config
if ($scope.widget && $scope.config.actionItems
&& $scope.config.actionItems.length != 0) {
// Hand off to dedicated function
checkActionItemsConfigs();
} else {
// Action items empty or we're missing something else
$log.warn('ACTION ITEMS CONTROLLER: '
+ 'Action items widget has broken configuration');
// Display error on widget
$scope.hasServiceError = true;
$scope.loading = false;
}
};
initializeActionItems();
}])
// VARIABLE CONTENT widget type
.controller('TimeSensitiveContentController', [
'$scope', '$filter', '$log', function($scope, $filter, $log) {
var init = function() {
var callsToAction = [];
if ($scope.config.callsToAction
&& angular.isArray($scope.config.callsToAction)
&& $scope.config.callsToAction.length > 0) {
callsToAction = $scope.config.callsToAction;
}
var now = new Date();
var templateIndex = indexWithinTemplateRange(now, callsToAction);
if (templateIndex > -1) {
displayTimeSensitiveContent(callsToAction[templateIndex]);
}
};
var resolveDate = function(str, defaultEndOfDay) {
// if it is valid ISO 8601 pass through
var fullISODate = moment(str, moment.ISO_8601, true);
if (fullISODate.isValid()) {
return fullISODate.toDate();
}
// if it is a partial ISO string, fill in missing fields
var partialISODate = moment(str, ['YYYY-MM-DD', 'MM-DD'], true);
if (partialISODate.isValid()) {
if (defaultEndOfDay) {
return partialISODate.endOf('day').toDate();
} else {
return partialISODate.startOf('day').toDate();
}
}
// this is not a valid date
return moment.invalid().toDate();
};
var indexWithinTemplateRange = function(now, callsToAction) {
var result = -1;
// TODO: refactor as Array.prototype.findIndex when IE support is dropped
// Check if today falls within any of the provided date ranges
angular.forEach(callsToAction, function(callToAction, index) {
if (result === -1) { // We haven't found a call to action
var startDate =
resolveDate(callToAction.activeDateRange.templateLiveDate);
var endDate =
resolveDate(callToAction.activeDateRange.templateRetireDate, true);
if (moment().isBetween(startDate, endDate)) {
result = index;
}
}
});
return result;
};
var resolveTemplateStatus = function(now, actionStart, actionEnd) {
if (
!(
moment(now).isValid()
&& moment(actionStart).isValid()
&& moment(actionEnd).isValid()
)
) {
throw new TypeError('now, actionStart, and actionEnd must all be set');
}
if (moment(actionStart).isAfter(actionEnd)) {
throw new RangeError('actionStart must be before actionEnd');
}
var statuses = [
{
name: 'upcoming',
check: function() {
return moment(now).isBefore(actionStart);
},
},
{
name: 'ongoing',
check: function() {
return moment(now).isBetween(actionStart, actionEnd);
},
},
{
name: 'lastDay',
check: function() {
return moment(now).isBetween(actionStart, actionEnd)
&& 1 === resolveDaysLeft(now, actionEnd);
},
},
{
name: 'ended',
check: function() {
return moment(now).isAfter(actionEnd);
},
},
];
var result = '';
// TODO: rewrite as Array.prototype.find when IE support is dropped
angular.forEach(statuses, function(status) {
if (status.check()) {
result = status.name;
}
});
return result;
};
var resolveDaysLeft = function(now, actionEnd) {
// round dates up, rather than the default which rounds down
return Math.ceil(moment(actionEnd).diff(now, 'days', true));
};
/**
* Display time-sensitive content with provided configuration
* @param {*} config - The provided configuration for a call to action
*/
var displayTimeSensitiveContent = function(config) {
var dates = config.activeDateRange;
var now = new Date();
var actionStart = resolveDate(dates.takeActionStartDate);
var actionEnd = resolveDate(dates.takeActionEndDate, true);
$scope.templateStatus =
resolveTemplateStatus(now, actionStart, actionEnd);
$scope.daysLeft = resolveDaysLeft(now, actionEnd);
if (actionStart) {
$scope.activePeriodStartDate = actionStart.getTime();
}
if (actionEnd) {
$scope.activePeriodEndDate = actionEnd.getTime();
}
$scope.callToAction = config;
};
init();
}])
// CUSTOM & GENERIC widget types
.controller('CustomWidgetController', [
'$scope', '$log', 'widgetService', function($scope, $log, widgetService) {
/**
* Fetch additional widget data
*/
var populateWidgetContent = function() {
if ($scope.widget.widgetURL && $scope.widget.widgetType) {
// Trigger loading spinner
$scope.loading = true;
// Fetch additional widget data
widgetService.getWidgetJson($scope.widget).then(function(data) {
$scope.loading = false;
if (data) {
$scope.widget.widgetData = data;
$scope.content = $scope.widget.widgetData;
if (angular.isArray($scope.content) && $scope.content.length == 0) {
$scope.isEmpty = true;
} else if ($scope.widget.widgetConfig &&
$scope.widget.widgetConfig.evalString &&
eval($scope.widget.widgetConfig.evalString)) {
$scope.isEmpty = true;
}
} else {
$log.warn('CUSTOM WIDGET CONTROLLER: '
+ 'Got nothing back from widget fetch from: ' +
$scope.widget.widgetURL);
$scope.isEmpty = true;
}
return data;
}).catch(function() {
// After we get widget data, turn off loading spinner
$scope.loading = false;
});
}
};
/**
* Filter array for provided values of a given object --
* used Leave Balances widget
*
* @param {Array<Object>} array The array to filter
* @param {Object} object The array entry to search through
* @param {Array<String>} strings
* The string values to test against each entry
* @return {Array<Object>} An array containing only the desired
*/
$scope.filteredArray = function(array, object, strings) {
if (array && object && strings) {
return array.filter(function(entry) {
for (var i = 0; i < strings.length; i++) {
if (entry[object].indexOf(strings[i]) != -1) {
return true;
}
}
});
} else {
return [];
}
};
/**
* Initialize scope variables before getting widget content
* @param {string} template - The provided custom HTML template
*/
var initializeCustomWidget = function(template) {
$scope.content = [];
$scope.template = template;
$scope.isEmpty = false;
$scope.widget.widgetData = [];
populateWidgetContent();
};
// Initialize widget if we received a custom HTML template,
// otherwise display empty widget and log a warning
if ($scope.widget.widgetTemplate) {
initializeCustomWidget($scope.widget.widgetTemplate);
} else {
$scope.loading = false;
$scope.isEmpty = true;
$log.warn('CUSTOM WIDGET CONTROLLER: ' + $scope.widget.fname +
' said it\'s a custom/generic widget, but didn\'t provide a template.');
}
}])
// SWITCH widget type
.controller('SwitchWidgetController', [
'$scope', '$log', '$parse', 'widgetService',
function($scope, $log, $parse, widgetService) {
$scope.switching = true;
/**
* Fetch additional widget data
*/
var initializeSwitchWidget = function() {
// save widgetConfig as switchConfig so switch can then conditionally
// mess with widgetConfig in activating a different widget type
$scope.switchConfig = $scope.widget.widgetConfig;
if ($scope.widget.widgetURL && $scope.switchConfig.expression
&& $scope.switchConfig.cases) {
// Configured with a URL from which to get dynamic JSON,
// and an expression to extract from that JSON,
// and case(s) to match against,
// so load that dynamic JSON, do the parsing and selecting
widgetService.getWidgetJson($scope.widget).then(function(data) {
// returns the new $scope.widget.widgetType, e.g. 'list-of-links'
var parsedExpression = $parse($scope.switchConfig.expression);
var dynamicValueToMatch = parsedExpression(data);
/**
* True when the case under consideration has a matchValue matching
* dynamicValueToMatch. Used for finding the relevant case to switch
* to, if any.
* @param {*} caseToCheck
* @return {String} widgetType
*/
function matchesValue(caseToCheck) {
return caseToCheck.matchValue === dynamicValueToMatch;
}
var caseToActivate = $scope.switchConfig.cases.find(matchesValue);
// if none of the cases match, select the default case if configured
if (!caseToActivate && $scope.switchConfig &&
$scope.switchConfig.defaultCase) {
caseToActivate = $scope.switchConfig.defaultCase;
}
// a case was selected for activation, so let's activate it
if (caseToActivate) {
if (caseToActivate.widgetUrl) {
// switch the widget URL and re-fetch JSON using that new config
$scope.widget.widgetURL = caseToActivate.widgetUrl;
widgetService.getWidgetJson($scope.widget);
}
if (caseToActivate.widgetType) {
// switch to the new type
$scope.widget.widgetType = caseToActivate.widgetType;
} else {
$scope.widget.widgetType = 'basic';
}
if (caseToActivate.widgetConfig) {
// switch to the new config
$scope.widget.widgetConfig = caseToActivate.widgetConfig;
}
if ($scope.widget.widgetConfig
&& !$scope.widget.widgetConfig.launchText
&& $scope.switchConfig
&& $scope.switchConfig.launchText) {
// honor base launchText if activated case does not have its own
$scope.widget.widgetConfig.launchText =
$scope.switchConfig.launchText;
}
} else {
$log.debug($scope.widget.fname +
' did not switch to any case (not even a default case) ' +
'for activation, so falling back to being a basic widget.');
$scope.widget.widgetType = 'basic';
$scope.widget.widgetConfig = '';
}
$scope.switching = false;
return $scope.widget.widgetType;
}).catch(function(error) {
$log.error('SWITCH WIDGET CONTROLLER: ' + $scope.widget.fname +
' errored fetching or processing JSON [' + $scope.widget.widgetURL
+ '] to switch on; falling back on being a basic widget.');
$scope.widget.widgetType = 'basic';
$scope.widget.widgetConfig = '';
$scope.switching = false;
});
} else {
$log.warn('SWITCH WIDGET CONTROLLER: '
+ $scope.widget.fname + ' not configured with ' +
'URL from which to load JSON [' + $scope.widget.widgetURL + '],' +
' expression [' + $scope.switchConfig.expression + ']' +
' to parse from that JSON, or cases to match against,' +
' so falling back on being a basic widget.');
$scope.widget.widgetType = 'basic';
$scope.widget.widgetConfig = '';
$scope.switching = false;
}
};
initializeSwitchWidget();
}])
// WEATHER widget type
.controller('WeatherWidgetController', [
'$scope', '$log', '$q', 'widgetService', 'keyValueService',
function($scope, $log, $q, widgetService, keyValueService) {
// Local variables
var fetchKey = 'userWeatherPreference';
/**
* Change temperature unit of measurement
*/
$scope.cycleUnits = function() {
var userPreference = $scope.nextUnits;
var value = {};
// Switch temperature based on which unit is next
switch (userPreference) {
case 'F':
convertKelvinToFahrenheit();
$scope.currentUnits = 'F';
$scope.nextUnits = 'C';
break;
case 'C':
convertFahrenheitToCelsius();
$scope.currentUnits = 'C';
$scope.nextUnits = 'K';
break;
case 'K':
convertCelsiusToKelvin();
$scope.currentUnits = 'K';
$scope.nextUnits = 'F';
break;
default:
convertCelsiusToKelvin();
$scope.currentUnits = 'K';
$scope.nextUnits = 'F';
}
// Set user preference
value.userWeatherPreference = $scope.currentUnits;
// Remember the user's preferred unit of measurement
keyValueService.setValue(fetchKey, value);
};
/**
* Fetch additional widget data
*/
var populateWidgetContent = function() {
// Declare asynchronous promises to be executed in $q.all()
var widgetPromise = widgetService.getWidgetJson($scope.widget);
var userPreferencesPromise = keyValueService.getValue(fetchKey);
// Execute promises, then resolve when both have returned something
$q.all( [widgetPromise, userPreferencesPromise] ).then(function(data) {
// Turn off loading spinner
$scope.loading = false;
// If we got some data, populate widget content
if (
data && 2 === data.length && data[0] && data[1] &&
(!data[0].errors || (data[0].errors && 0 >= data[0].errors.length))
) {
// Set local variables
var allTheWeathers = data[0];
var myPref = data[1];
var userPreference = myPref.userWeatherPreference;
// Set scope variables
if (allTheWeathers) {
$scope.weatherData = allTheWeathers.weathers;
}
$scope.currentUnits = 'F';
$scope.nextUnits = 'C';
// If the user doesn't have a preference, default to Fahrenheit
if (
userPreference === null ||
userPreference === '' ||
angular.isUndefined(userPreference)
) {
userPreference = 'F';
}
// Cycle units until we match with the user's preference
while (userPreference != $scope.currentUnits) {
$scope.cycleUnits();
}
} else {
// Log any errors getting data
$scope.error = true;
$log.warn('WeatherWidgetController could\'t get widget json');
}
// Return to resolve $q.all()
return data;
}).catch(function() {
$scope.loading = false;
$scope.error = true;
$log.warn(
'WeatherWidgetController wrote a check it simply couldn\'t cash');
});
};
/**
* Convert from Fahrenheit to Celsius
*/
var convertFahrenheitToCelsius = function() {
var ratio = (5 / 9);
var offset = 32;
for (var i = 0; i < $scope.weatherData.length; i++) {
$scope.weatherData[i].currentWeather.temperature =
($scope.weatherData[i].currentWeather.temperature -
offset) * ratio;
for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
$scope.weatherData[i].forecast[j].highTemperature =
($scope.weatherData[i].forecast[j].highTemperature -
offset) * ratio;
$scope.weatherData[i].forecast[j].lowTemperature =
($scope.weatherData[i].forecast[j].lowTemperature -
offset) * ratio;
}
}
};
/**
* Convert from Celsius to Fahrenheit
*/
var convertCelsiusToFahrenheit = function() {
var ratio = (9 / 5);
var offset = 32;
for (var i = 0; i < $scope.weatherData.length; i++) {
$scope.weatherData[i].currentWeather.temperature =
($scope.weatherData[i].currentWeather.temperature *
ratio) + offset;
for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
$scope.weatherData[i].forecast[j].highTemperature =
($scope.weatherData[i].forecast[j].highTemperature *
ratio) + offset;
$scope.weatherData[i].forecast[j].lowTemperature =
($scope.weatherData[i].forecast[j].lowTemperature *
ratio) + offset;
}
}
};
/**
* Convert from Celsius to Kelvin
*/
var convertCelsiusToKelvin = function() {
var offset = 273;
for (var i = 0; i < $scope.weatherData.length; i++) {
$scope.weatherData[i].currentWeather.temperature =
($scope.weatherData[i].currentWeather.temperature + offset);
for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
$scope.weatherData[i].forecast[j].highTemperature =
($scope.weatherData[i].forecast[j].highTemperature + offset);
$scope.weatherData[i].forecast[j].lowTemperature =
($scope.weatherData[i].forecast[j].lowTemperature + offset);
}
}
};
/**
* Convert from Kelvin to Fahrenheit (via Celsius)
*/
var convertKelvinToFahrenheit = function() {
convertKelvinToCelsius();
convertCelsiusToFahrenheit();
};
/**
* Convert from Kelvin to Celsius
*/
var convertKelvinToCelsius = function() {
var offset = 273;
for (var i = 0; i < $scope.weatherData.length; i++) {
$scope.weatherData[i].currentWeather.temperature =
($scope.weatherData[i].currentWeather.temperature - offset);
for (var j = 0; j < $scope.weatherData[i].forecast.length; j++) {
$scope.weatherData[i].forecast[j].highTemperature =
($scope.weatherData[i].forecast[j].highTemperature - offset);
$scope.weatherData[i].forecast[j].lowTemperature =
($scope.weatherData[i].forecast[j].lowTemperature - offset);
}
}
};
// Initialize weather widget
$scope.loading = false;
if ($scope.widget.widgetURL) {
$scope.loading = true;
$scope.weatherData = [];
$scope.currentUnits = 'F';
$scope.nextUnits = 'C';
populateWidgetContent();
} else {
$log.warn('WeatherWidgetController did not receive a widgetURL');
}
}])
.controller('RemoteContentWidgetController', [
'$scope', '$http', '$log',
function($scope, $http, $log) {
var initRemoteContentWidget = function() {
$scope.loading = true;
$log.debug('entered initRemoteContentWidget()');
$http.get($scope.widget.widgetURL).then(function(result) {
$scope.remoteContent = result.data;
$scope.loading = false;
$log.debug(
'Got [' + result.data + '] from [' +
$scope.widget.widgetURL + ']' );
return result;
}).catch(function(error) {
$scope.remoteContentErrors = error;
$log.error(error);
return error;
}
);
};
initRemoteContentWidget();
}]
);
});