Labs for culture
2012 - 2019
Fully featured social network of a kind and of that time. A pioneer project with nowadays quite popular so called decoupling Drupal where as only its back-end is used (or even not) yet front-end is any of popular frameworks such as React or Angular. This was a collaboration with a great developer mind GF to whom I owe a big credit for many inputs and tricks! We did all on existing Drupal 6 old and not that simple database and structure making it completely bare and providing JSON routes picked by and rendered by AngularJS.
/* "App.js" file in AngularJS */
/* AngularJS "App.js" */
// Define default Angular app modules
var app = angular.module('labsApp', ['ui.router', 'ngAnimate', 'ngSanitize',
'angular-loading-bar', 'labs.factories', 'labs.filters'
]);
// Dev/Production global switch
if (!DEV) {
// Include cached templates
app.requires.unshift('labs.templates');
}
// Set some common, global configurations
app.config(['$httpProvider', '$locationProvider', 'cfpLoadingBarProvider',
function ($httpProvider, $locationProvider, cfpLoadingBarProvider) {
// Enable HTML5 mode @see .htaccess
$locationProvider.html5Mode(true);
/*
$locationProvider.html5Mode({
enabled: true,
requireBase: false,
rewriteLinks: false
});
*/
// Loading bar - turn off http loader spinner, leave only the bar
cfpLoadingBarProvider.parentSelector = 'header';
// OK for now
$httpProvider.defaults.cache = true;
/*
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.withCredentials = false;
delete $httpProvider.defaults.headers.common["X-Requested-With"];
$httpProvider.defaults.headers.common["Accept"] = "application/json";
$httpProvider.defaults.headers.common["Content-Type"] = "application/json";
*/
}
]);
// Run
app.run(['$location', '$rootScope', '$interval', '$state', '$stateParams',
'debounce', 'LabsPlugins', 'LabsBackend', 'LabsRoutes', 'LabsTemplates',
function ($location, $rootScope, $interval, $state, $stateParams,
debounce, LabsPlugins, LabsBackend, LabsRoutes, LabsTemplates) {
// Set information about actual (sub) domain globally
$rootScope.domain = $location.protocol() + '://' + $location.host();
// Make those objects available in HTML templates
$rootScope.$stateParams = $stateParams;
$rootScope.$state = $state;
$rootScope.$location = $location;
// Set templates definitions/paths globally @see src/config/default.json
$rootScope.elementsTemplates = angular.isObject(LabsTemplates.elements) ?
LabsTemplates.elements : {};
/**
* Main Events
*/
$rootScope.$on('$locationChangeStart', function (e, newUrl, oldUrl,
newState, oldState) {
// Register and integrate plugins "src/plugins/"
if (!$rootScope.plugins) {
var external = angular.isObject(LabsRoutes.externalDomain) ?
LabsRoutes.externalDomain.id : null;
LabsPlugins.init($rootScope, LabsRoutes, external).then(
function (response) {
if (angular.isObject($rootScope.plugins)) {
angular.forEach($rootScope.plugins, function (plugin,
key) {
if (angular.isObject(plugin) && plugin.defaultPath) {
// Assign isPlugin variable "on the fly" so that we know that we are on the plugin's pages/routes
if (newUrl.indexOf(plugin.defaultPath) > -1) {
$rootScope.isPlugin = key;
}
}
});
}
});
}
});
// Log errors here (i.e. in particular for resolve methods in ui-router states)
$rootScope.$on('$stateChangeError', function (event, toState, toParams,
fromState, fromParams, error) {
console.log(error);
if (error === 'accessDenied' || error ===
'authenticationRequired') {
event.preventDefault();
if (error === 'accessDenied') {
$state.go(LabsRoutes.defaults.defaultState);
} else if (error === 'authenticationRequired') {
// This means that user is authenticated (logged in) but does not have a permission for this lab/page/content
var loggedIn = angular.isObject($rootScope.session) &&
angular.isObject($rootScope.session.account) ? $rootScope.session
.account.uid : null;
$state.go('page', {
title: 'access-denied'
});
var debounceModal = debounce(2400, function () {
if (!loggedIn) { // Show login modal for anonymous
$rootScope.modal.show = true;
} else {
// Otherwise redirect existing but not authorized user to a default (front) page
$state.go(LabsRoutes.defaults.defaultState);
}
});
debounceModal();
}
} else if (error === 'pageNotFound') {
$state.go('page', {
title: '404'
});
var debounceHome = debounce(6400, function () {
$state.go(LabsRoutes.defaults.defaultState);
});
debounceHome();
}
});
// Reload the whole interface within interval
if (!$rootScope.isReloading) {
$interval(function () {
$rootScope.isReloading = true;
$rootScope.clearCache({
caches: null,
reloadState: true,
skipLabs: false
});
}, 3600000); // One hour
// }, 20000); // DEV: 20 seconds
}
}
]);
/* Google Maps API in AngularJS */
// Add this plugin to required app modules
app.requires.push('step');
var step = angular.module('step', ['step.config'])
.run(['$rootScope', 'labsCache', 'stepConfig', 'Parse', 'AuthService', 'UserRoles', function($rootScope, labsCache, stepConfig, Parse, AuthService, UserRoles) {
// Define Config object for this plugin
$rootScope.stepConfig = stepConfig;
/*
$rootScope.$on('$locationChangeSuccess', function(e, newUrl, oldUrl, newState, oldState) {
var urlElements = Parse.url(newUrl);
if (urlElements.query && urlElements.query.indexOf('action=') > -1) {
var authorized = AuthService.isAuthorized(UserRoles[2], $rootScope.session.account);
if (!authorized) {
$rootScope.modal.show = true;
}
}
});
*/
}])
// Maps cache definition
.service('mapCache', ['$cacheFactory', function($cacheFactory) {
return $cacheFactory('map-cache');
}])
// Default page loading, in case of refresh and values were previously entered or Draft
// Use predictions service to assign those existing values to Google places
.factory('LabsGmaps', ['$q', function($q) {
if (!google || google === undefined) {
return {};
}
var self = {
mapStyle: [ { "featureType": "administrative", "elementType": "labels.text.fill", "stylers": [ { "color": "#444444" } ] }, { "featureType": "landscape", "elementType": "all", "stylers": [ { "color": "#f2f2f2" } ] }, { "featureType": "poi", "elementType": "all", "stylers": [ { "visibility": "on" } ] }, { "featureType": "poi", "elementType": "geometry.fill", "stylers": [ { "saturation": "-100" }, { "lightness": "57" } ] }, { "featureType": "poi", "elementType": "geometry.stroke", "stylers": [ { "lightness": "1" } ] }, { "featureType": "poi", "elementType": "labels", "stylers": [ { "visibility": "off" } ] }, { "featureType": "road", "elementType": "all", "stylers": [ { "saturation": -100 }, { "lightness": 45 } ] }, { "featureType": "road.highway", "elementType": "all", "stylers": [ { "visibility": "simplified" } ] }, { "featureType": "road.arterial", "elementType": "labels.icon", "stylers": [ { "visibility": "off" } ] }, { "featureType": "transit", "elementType": "all", "stylers": [ { "visibility": "off" } ] }, { "featureType": "transit.station.bus", "elementType": "all", "stylers": [ { "visibility": "on" } ] }, { "featureType": "transit.station.bus", "elementType": "labels.text.fill", "stylers": [ { "saturation": "0" }, { "lightness": "0" }, { "gamma": "1.00" }, { "weight": "1" } ] }, { "featureType": "transit.station.bus", "elementType": "labels.icon", "stylers": [ { "saturation": "-100" }, { "weight": "1" }, { "lightness": "0" } ] }, { "featureType": "transit.station.rail", "elementType": "all", "stylers": [ { "visibility": "on" } ] }, { "featureType": "transit.station.rail", "elementType": "labels.text.fill", "stylers": [ { "gamma": "1" }, { "lightness": "40" } ] }, { "featureType": "transit.station.rail", "elementType": "labels.icon", "stylers": [ { "saturation": "-100" }, { "lightness": "30" } ] }, { "featureType": "water", "elementType": "all", "stylers": [ { "color": "#d2d2d2" }, { "visibility": "on" } ] } ],
setContainer: function(container) {
self.construct.container = container;
return self;
},
setPoints: function(points) {
self.construct.points = points;
return self;
},
setCurrent: function(current, key) {
self.construct.current[key] = current;
return self;
},
construct: {
container: null,
map: null,
points: {},
current: [],
display: null,
polyline: {},
markers: {},
marker: null,
bounds: new google.maps.LatLngBounds(),
places: google.maps.places.PlacesService,
autocompleteService: new google.maps.places.AutocompleteService(),
geocoder: new google.maps.Geocoder,
directionsService: new google.maps.DirectionsService,
infowindowContent: [],
infoWindow: new google.maps.InfoWindow()
},
getMap: function(map) {
return map ? map : self.construct.map;
},
setMap: function(element, center, zoom) {
if (!!navigator.geolocation) { // Geolocation is supported by browser
var mapContainer = element && element[0] ? element[0] : document.querySelector('#map');
if (mapContainer) {
// Set some values to a parent class
self.setContainer(mapContainer);
//self.construct.infoWindow = document.querySelector('#infowindow-content');
self.construct.map = new google.maps.Map(mapContainer, {
center: center ? center : {lat: 52.3702157, lng: 4.895167900000001}, // Amsterdam
zoom: zoom ? zoom : 4
});
// Enables CSS manipulation on marker icons
if (self.construct.map) {
var mapOverlay = new google.maps.OverlayView();
mapOverlay.draw = function () {
this.getPanes().markerLayer.id='markerLayer';
};
mapOverlay.setMap(self.construct.map);
}
return self.construct.map;
}
else {
return false;
}
}
else {
if (mapContainer) {
angular.element(mapContainer).html('<h3 class="error">Geolocation is not supported by the browser</h3>');
}
}
},
parseLocation: function(location) {
return {lat: parseFloat(location.geometry.location.lat()), lng: parseFloat(location.geometry.location.lng())};
},
setMarker: function(data, map, exists) { //key, label, icon, param, map) {
// Unset existing polylines
if (exists) {
self.clear(self.construct.markers);
}
var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var labelIndex = 0;
if (!map) {
map = self.construct.map();
}
return new google.maps.Marker({
position: data.position,
title: data.label ? data.label : data.key,
// label: labels[labelIndex++ % labels.length],
param: data.param ? data.param : null,
icon: data.icon ? data.icon : '/images/map-icons/icon-' + data.key +'.png',
map: map,
//animation: google.maps.Animation.DROP,
optimized: data.optimized ? data.optimized : false
});
},
getGeocode: function(latlng, key, label, types, multi, infowindow) {
self.construct.geocoder.geocode({'location': latlng}, function(results, status) {
if (status === 'OK') {
if (angular.isArray(results) && angular.isObject(results[0])) {
angular.forEach(results, function(result, index) {
if (angular.isArray(types)) {
if (result.types[0] === types[0] && result.types[1] === types[1]) {
self.construct.current.push(self.parseLocation(result));
self.construct.markers.push(self.setMarker(self.parseLocation(result), key, label));
self.polyline(key, multi, infowindow);
}
}
else {
self.construct.current.push(self.parseLocation(results[0]));
self.construct.markers.push(self.setMarker(self.parseLocation(results[0]), key, label));
self.polyline(key, multi, infowindow);
}
});
}
else {
self.construct.infoWindow.innerHTML = '<div class="error">No results found</div>';
}
}
else {
self.construct.infoWindow.innerHTML = '<div class="error">Geocoder failed due to: <em>' + status + '</em><br />Please try reloading the page</div>';
}
});
},
clear: function(objects, type) {
angular.forEach(objects, function(object, index) {
if (object.departure && object.destination) {
object.departure.setMap(null);
object.destination.setMap(null);
}
else {
object.setMap(null);
}
});
return type === 'array' ? [] : {};
},
polyline: function(path, map, param, exists, key) {
// Unset existing polylines
if (exists) {
self.clear(self.construct.polyline);
}
var options = {
param: param,
path: path ? path : self.construct.current,
geodesic: true,
strokeColor: '#FF0000', //'#DDDf00',
strokeOpacity: 1.0,
strokeWeight: 4
};
var polyline = new google.maps.Polyline(options);
if (!map) {
map = self.construct.map();
}
polyline.setMap(map);
return polyline;
},
getPredictions: function(value, map, key, id, label, multi) {
var deferred = $q.defer();
var request = {input: value}; //value};
var prediction;
self.construct.autocompleteService.getPlacePredictions(request, function(predictions, status) {
if (status === 'OK') {
self.construct.points[id] = {};
var placeRequest = {placeId: predictions[0].place_id};
var currentMap = map ? map : self.construct.map;
var placeService = new self.construct.places(currentMap);
placeService.getDetails(placeRequest, function(place, placeStatus) {
if (placeStatus === 'OK') {
self.construct.points[id][key] = place;
//return place;
deferred.resolve(place);
}
});
}
});
return deferred.promise;
},
// Parse and store country (entry) iso code and name
getLocationStrings: function(place) {
var parent = this;
var country = {country: null, city: null, iso: null};
for (var i = 0; i < place.address_components.length; i++) {
for (var t = 0; t < place.address_components[i].types.length; t++) {
if (place.address_components[i].types[0] === 'country') {
country.iso = place.address_components[i].short_name.toLowerCase();
country.country = place.address_components[i].long_name;
}
if (place.address_components[i].types[0] === 'locality') {
country.city = place.address_components[i].long_name;
}
}
}
return country;
},
directions: function() {
self.construct.directionsService.route({
origin: self.construct.points.departure.string,
destination: self.construct.points.destination.string,
travelMode: google.maps.TravelMode.WALKING
}, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
// Draw route on the map
self.construct.display = new google.maps.DirectionsRenderer({map: self.construct.map, directions: response});
// Show distance in km
self.construct.infoWindow.innerHTML = '< /div >h2 class="title">Distance: ' + response.routes[0].legs[0].distance.text + '< /div >/h2>'; // textContent
}
else {
self.construct.infoWindow.innerHTML = '< /div >div class="messages error">Directions request failed due to ' + status + '< /div >/div>';
}
});
}
};
return self;
}])
// Custom factory to build the logic for decorator method below
.factory('stepDecorators', ['$q', '$http', '$rootScope', '$timeout', '$location', 'debounce', '$http', '$state', '$stateParams', '$filter', 'FileOps', 'LabsRoutes', 'LabsBackend', 'Node', 'LabsGmaps', 'LabsScroll', 'stepConfig', 'mapCache', 'labsCache', function($q, $http, $rootScope, $timeout, $location, debounce, $http, $state, $stateParams, $filter, FileOps, LabsRoutes, LabsBackend, Node, LabsGmaps, LabsScroll, stepConfig, mapCache, labsCache) {
var decorators = {
// Parse and prepare some data given from back-end
parseValue: function(item, key, element, map) {
var deferred = $q.defer();
var point = {
element: element ? element : null
};
point.lat_lng = item[key].indexOf('|') > -1 ? item[key].split('|')[1] : null;
point.lat = item[key].indexOf('|') > -1 ? parseFloat(item[key].split('|')[1].split(',')[0]) : null;
point.lng = item[key].indexOf('|') > -1 ? parseFloat(item[key].split('|')[1].split(',')[1]) : null;
point.string = item[key].indexOf('|') > -1 ? item[key].split('|')[0] : null;
// Data, the whole parent object is stashed too
point.data = item;
// Check if user has profile pic and set it for the destination marker
if (item.authorimageMarker && key === 'destination') {
if (FileOps.isJson(item.authorimageMarker)) {
var authorImage = angular.fromJson(item.authorimageMarker);
point.icon = angular.isObject(authorImage) && authorImage.image ? authorImage.image : null;
}
else {
point.icon = item.authorimageMarker;
}
}
return point.lat_lng ? point : null;
},
getMap: function(map) {
return map ? map : LabsGmaps.getMap();
},
setMap: function(element, center, zoom) {
var pl = center ? center : {lat: 54.110943, lng: 18.149414}; //Lat,Lng - somewhere in Poland
var regensburg = center ? center : {lat: 49.0215595, lng: 12.04013}; // Regensburg in Germany seems to be pretty much central in our case
var z = zoom ? zoom : 4;
return LabsGmaps.setMap(element, regensburg, z);
},
getPredictions: function(value, map) {
return LabsGmaps.getPredictions(value, map);
},
provideMapInterface: function(data, config) {
if (config.map) {
LabsGmaps.construct.bounds = new google.maps.LatLngBounds();
LabsGmaps.construct.markers = LabsGmaps.clear(LabsGmaps.construct.markers);
LabsGmaps.construct.polyline = LabsGmaps.clear(LabsGmaps.construct.polyline);
}
var pl = {lat: 54.110943, lng: 18.149414}; //Lat,Lng - somewhere in Poland
var regensburg = {lat: 49.0215595, lng: 12.04013}; // Regensburg in Germany seems to be pretty much central in our case
// This will initially set the map (initial page view) or use existing map (search for example)
// Note: config.map is also used below in a boolean role for main class to clear previous objects (polylines, markers etc.) from the map
var map = config.map ? config.map : decorators.setMap(null); //LabsGmaps.setMap(null, regensburg, 4);
if (map) { // Essential with async
var emptyGeoData = 0;
var promise = $timeout();
var errors = [];
angular.forEach(data, function(item, index) {
if (item.brick.departure && item.brick.destination) {
promise = promise.then(function() {
var htmlElement = document.querySelector('#item-' + item.brick.nid);
if (config.map) { // Unset bounds, doing it here unlike for markers and polylines above because in case none of the items from data array has geo data bounds should stay the same as previous
LabsGmaps.construct.bounds = new google.maps.LatLngBounds();
}
// Parse data given from back-end
var departure = decorators.parseValue(item.brick, 'departure', null, map);
var destination = decorators.parseValue(item.brick, 'destination', null, map);
// ####
if (angular.isObject(departure) && angular.isObject(destination)) {
if (departure.lat && departure.lng && destination.lat && destination.lng) {
// Feed the main class with this properties, perhaps not needed actually
LabsGmaps.construct.points[item.brick.nid] = {
departure: departure,
destination: destination
};
// Set Departure marker
if (config.markers) {
var departureData = {
position: {lat: departure.lat, lng: departure.lng},
key: 'departure',
label: departure.string.indexOf(', ') > -1 ? departure.string.split(', ')[0] : departure.string,
icon: departure.icon,
param: item.brick.nid
};
var markerDeparture = LabsGmaps.setMarker(departureData, map);
markerDeparture.element = htmlElement;
LabsGmaps.construct.markers[item.brick.nid] = {departure: markerDeparture};
if (config.listeners) {
decorators.mapListeners('marker', LabsGmaps.construct.markers[item.brick.nid].departure);
}
}
// Extend bounds array
if (config.bounds) {
LabsGmaps.construct.bounds.extend({lat: departure.lat, lng: departure.lng});
}
// Set Polyline
if (config.polyline) {
var path = [new google.maps.LatLng(departure.lat, departure.lng), new google.maps.LatLng(destination.lat, destination.lng)];
LabsGmaps.construct.polyline[item.brick.nid] = LabsGmaps.polyline(path, map, item.brick.nid);
LabsGmaps.construct.polyline[item.brick.nid].element = htmlElement;
if (config.listeners) {
decorators.mapListeners('polyline', LabsGmaps.construct.polyline[item.brick.nid]);
}
}
// Set Destination marker
if (config.markers) {
if (LabsGmaps.construct.markers[item.brick.nid]) {
var destinationData = {
position: {lat: destination.lat, lng: destination.lng},
key: 'destination',
label: destination.string.indexOf(', ') > -1 ? destination.string.split(', ')[0] : destination.string,
icon: destination.icon,
param: item.brick.nid
};
var markerDestination = LabsGmaps.setMarker(destinationData, map);
markerDestination.element = htmlElement;
LabsGmaps.construct.markers[item.brick.nid].destination = markerDestination;
if (config.listeners) {
decorators.mapListeners('marker', markerDestination);
}
}
}
// Extend bounds array
if (config.bounds) {
LabsGmaps.construct.bounds.extend({lat: destination.lat, lng: destination.lng});
}
return $timeout(10);
}
}
else {
errors.push(item.brick.nid);
}
});
}
else {
// Count each item that does not have geo data; in case ALL of them do not have no bounds and zoom are set to the map
emptyGeoData++;
}
});
// Fit bounds
if (config.bounds) {
if (emptyGeoData < data.length) { // If at least one item has geodata, set bounds
if (config.map) {
//map.fitBounds(LabsGmaps.construct.bounds);
//map.setZoom(2);
}
}
}
// Set zoom (again), after bounds
if (config.zoom && !config.map) { // Init map construct only
var debounceZoom = debounce(100, function() {
map.setZoom(4);
});
debounceZoom();
}
}
},
getSearchPromise: function(value, type) {
var deferred = $q.defer();
var promise = $http.post(LabsRoutes.search.solr, {term: value, type: type}); // + '/1' --page, to be implemented
promise.then(function(search) {
var nodes = [];
if (angular.isObject(search.data)) {
if (search.data[0]) {
angular.forEach(search.data, function(item, index) {
nodes.push({brick: item});
});
}
}
deferred.resolve(nodes);
});
return deferred.promise;
},
mapListeners: function(type, object) {
if (type === 'marker' || type === 'polyline') {
object.addListener('click', function() {
if (object.param && object.element) {
var items = document.querySelectorAll('.card.grid');
angular.forEach(items, function(item, index) {
angular.element(item).removeClass('active');
});
angular.element(object.element).addClass('active');
var container = document.querySelector('#scrollMe');
var topPosition = object.element ? object.element.offsetTop : 2;
LabsScroll.scrollDiv(container, object.element, topPosition-1, 600, 10);
}
});
}
if (type === 'polyline') {
google.maps.event.addListener(object, 'mouseover', function() {
object.setOptions({strokeOpacity: 1});
});
google.maps.event.addListener(object, 'mouseout', function() {
object.setOptions({strokeOpacity: 0.6});
});
}
},
feedInfowindow: function(point) {
var content;
if (angular.isObject(point.departure) && angular.isObject(point.departure.data)) {
if (point.departure.data.imageUrl) {
content = '< /div >div class="gmap node node-step">< /div >div>< /div >img src="' + point.departure.data.imageUrl + '" width="60" height="45" />< /div >/div>< /div >div>' + point.departure.data.title + '< /div >/div>< /div >div>' + point.departure.data.authorname + '< /div >/div>< /div >div>From ' + point.departure.string + ' to ' + point.destination.string + '< /div >/div>< /div >/div>';
}
else {
content = '< /div >div class="gmap node node-step">< /div >div>' + point.departure.data.text + '< /div >/div>< /div >div>< /div >h3>' + point.departure.data.title + '< /div >/h3>< /div >/div>< /div >div>' + point.departure.data.authorname + '< /div >/div>< /div >div>From ' + point.departure.string + ' to ' + point.destination.string + '< /div >/div>< /div >/div>';
}
}
return content;
},
pushPageItems: function(stateParams, staticPath, integer, existing, request) {
var deferred = $q.defer();
if (existing) { // Request was done (i.e. search) or appropriate data is already available
if (mapCache.get('stepSearchInput')) {
var input = angular.element(document.querySelector('#stepSearch'));
input.val(mapCache.get('stepSearchInput'));
// Remove any previous search items from the cache
// mapCache.remove('stepSearch');
//mapCache.remove('stepSearchInput');
}
var nodes = [];
angular.forEach(existing, function(item, itemIndex) {
var collection = {
images: {
presets: ['imageList', 'imageProject', 'imageGrid']
},
index: itemIndex
};
nodes.push({brick: Node.renderNode(item.brick, collection)});
});
deferred.resolve(nodes);
return deferred.promise;
}
else { // Prepare data and do request to backend
if (!integer) {
integer = 0;
}
// Important - take care of the page param
if (integer != 0) {
if (!stateParams.page) {
if (stateParams.page == null) {
stateParams.page = integer;
}
else {
stateParams.page = stateParams.page == 0 ? 1 : parseInt(stateParams.page) + integer;
}
}
else {
stateParams.page = parseInt(stateParams.page) + integer;
}
}
var nodes = [];
var path = stateParams.page != null && stateParams.page > -1 ? staticPath + '?page=' + stateParams.page : staticPath;
var params = {
page: stateParams.page,
action: stateParams.action,
sort: stateParams.sort
};
var promise = $http({
'method': 'GET',
'url': path,
'params': {
'params': params
},
'cache': mapCache
});
// Well, return data
promise.then(function(result) {
// Essential
$stateParams.page = stateParams.page;
$stateParams.search = stateParams.search;
// Now return items for the project view and for a listing
angular.forEach(result.data.nodes, function(node, index) {
if (angular.isObject(node.node) && node.node.nid) {
var collection = {
images: {
presets: ['imageList', 'imageProject', 'imageGrid']
},
index: index
};
var urlArgs = $location.search();
if (request === 'request' || urlArgs.sb) {
nodes.push(Node.renderNode(node.node, collection));
}
else {
nodes.push({brick: Node.renderNode(node.node, collection)});
}
}
});
deferred.resolve(nodes);
});
return deferred.promise;
}
},
generateProject: function(data, stateParams, id) {
var nodes = [];
angular.forEach(data, function(object, index) {
// STEP projects exists already, it is listed on current page within promoted projects grid, we are loading it from there so no request to back-end
if (angular.isObject(object.brick) && object.brick.nid == stateParams.nid) {
var collection = {
images: {
presets: ['imageList', 'imageProject', 'imageGrid']
},
index: index,
action: null,
access: {},
relatedObjects: []
};
// Grant confirmation action (since node takes its data from parent array we need to assign this upon user login)
if (angular.isObject($rootScope.session) && angular.isObject($rootScope.session.account)) {
if (stateParams.action > 0) {
if ($rootScope.session.account.uid == object.brick.author || $rootScope.session.account.uid == 1) {
if (object.brick.action) {
collection.action = stateParams.action;
}
}
}
}
if (angular.isArray(object.brick.relatedObjects) && angular.isObject(object.brick.relatedObjects[0])) {
angular.forEach(object.brick.relatedObjects, function(relatedObject, relatedIndex) {
if (angular.isObject(relatedObject) && angular.isObject(relatedObject.related)) {
if (relatedObject.related.nid == stateParams.nid) {
collection.relatedObjects.push(relatedObject);
}
}
});
var newId = id === 'list' ? 'grid' : 'list';
collection.relatedObjects.push({id: newId, index: index, related: object.brick});
}
else {
var newId = id === 'list' ? 'grid' : 'list';
collection.relatedObjects[0] = {id: newId, index: index, related: object.brick};
}
nodes.push(Node.renderNode(object.brick, collection));
}
});
return nodes;
},
scroll: function(integer, element) {
integer = integer ? integer : 100;
var debounceScroll = debounce(integer, function() {
var element = element ? element : document.querySelector('#scrollMe > div:first-child');
var container = document.querySelector('#scrollMe');
var topPosition = element.offsetTop;
LabsScroll.scrollDiv(container, element, topPosition-100, 300, 50);
});
debounceScroll();
},
projectSubmitted: function(scope) {
scope.sbSubmitted = null;
if (angular.isObject($location.search())) {
var params = $location.search();
if (params.sb) {
scope.sbSubmitted = params.sb;
var debounceMessage = debounce(120000, function() {
scope.sbSubmitted = null;
});
debounceMessage();
}
}
},
projectsReferences: function(scope) {
scope.node.references = {};
if (!$stateParams.r) {
if ($stateParams.labpath === stepConfig.travelpath) {
if (angular.isObject($rootScope.session) && angular.isObject($rootScope.session.card) && angular.isObject($rootScope.session.card.step_beyond)) {
// Default value for select list -> empty
$rootScope.session.card.step_beyond[0] = {
nid: 'empty',
title: stepConfig.strings.sbProjectSelect
};
scope.node.references[$stateParams.labpath] = $rootScope.session.card.step_beyond[0].nid;
// Assign select project callback (checkup for other step updates if any)
scope.sbProjectSelect = decorators.selectProject;
}
else {
scope.node.references[$stateParams.labpath] = null;
}
}
}
else if ($stateParams.r) {
scope.node.references[$stateParams.labpath] = null;
}
},
selectProject: function(node) {
var nid = node.references[$stateParams.labpath];
var changed;
if (nid !== 'empty' && angular.isObject(LabsRoutes.plugins) && LabsRoutes.plugins.step) {
$http.post(LabsRoutes.plugins.step.updates, {nid: nid}).then(function(promise) {
if (angular.isObject(promise.data) && angular.isObject(promise.data.node)) {
var f = promise.data.node.hasFinalReport ? '1' : '';
changed = $state.go($state.current, {f: f}, {inherit: true, notify: false, reload:false});
if (changed) {
//$history.remove();
}
}
});
}
else {
changed = $state.go($state.current, {f: '1'}, {inherit: true, notify: false, reload:false});
if (changed) {
// $history.remove();
}
}
}
};
return decorators;
}]);