<commit_before>'use strict';

const util = require('./util');

const convertToJson = function(node, options, parentTagName, jpath) {

  const jObj = {};

  // when no child node or attr is present
  if (!options.alwaysCreateTextNode && (!node.child || util.isEmptyObject(node.child)) && (!node.attrsMap || util.isEmptyObject(node.attrsMap))) {
    return util.isExist(node.val) ? node.val : '';
  }

  // otherwise create a textnode if node has some text
  if (util.isExist(node.val) && !(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) {
    const tagName = options.textNodeName;
    const newJpath = jpath + "." + tagName;
    jObj[tagName] = options.isArray(tagName, newJpath, true) ? [node.val] : node.val;
  }

  copyAttributesifPresent(jObj, node.attrsMap, jpath, options);

  const keys = Object.keys(node.child);
  for (let index = 0; index < keys.length; index++) {
    const tagName = keys[index];
    let newJpath = "";
    if(jpath === undefined) newJpath = tagName;
    else newJpath = jpath + "." + tagName;

    if (node.child[tagName] && node.child[tagName].length > 1) {
      jObj[tagName] = [];
      for (let tag in node.child[tagName]) {
        if (node.child[tagName].hasOwnProperty(tag)) {
          jObj[tagName].push(
            convertToJson(node.child[tagName][tag], options, tagName, newJpath)
          );
        }
      }
    } else {

      const result = convertToJson(node.child[tagName][0], options, tagName, newJpath);
      jObj[tagName] = options.isArray(tagName, newJpath, isLeafNode(node.child, tagName)) ? [result] : result;
    }
  }

  //add value
  return jObj;
};
function copyAttributesifPresent(tagObj, attrMap, jpath, options){
  if (attrMap) {
    const keys = Object.keys(attrMap);
    const len = keys.length; //don't make it inline
    for (let i = 0; i < len; i++) {
      const atrrName = keys[i];
      if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
        tagObj[atrrName] = [ attrMap[atrrName] ];
      } else {
        tagObj[atrrName] = attrMap[atrrName];
      }
    }
  }
}

//a non-leaf tag would be an array
function prettify(node, options){
  return compress( [node], options);
}

/**
 *
 * @param {array} arr
 * @param {object} options
 * @param {string} jPath
 * @returns object
 */
function compress(arr, options, jPath){
  let text = "";
  const compressedObj = {};
  for (let i = 0; i < arr.length; i++) {
    const tagObj = arr[i];
    const property = propName(tagObj);
    let newJpath = "";
    if(jPath === undefined) newJpath = property;
    else newJpath = jPath + "." + property;

    if(property === options.textNodeName){
      text += tagObj[property];
    }else if(property === undefined){
      continue;
    }else if(tagObj[property]){

      let val = compress(tagObj[property], options, newJpath);
      const isLeaf = isLeafTag(val, options);

      if(tagObj.attributes){
        assignAttributes( val, tagObj.attributes, newJpath, options);
      }else if(Object.keys(val).length === 1 && val[options.textNodeName] && !options.alwaysCreateTextNode){
        val = val[options.textNodeName];
      }else if(Object.keys(val).length === 0){
        if(options.alwaysCreateTextNode) val[options.textNodeName] = "";
        else val = "";
      }

      if(compressedObj[property] !== undefined) {
        if(!Array.isArray(compressedObj[property])) {
          compressedObj[property] = [ compressedObj[property] ];
        }
        compressedObj[property].push(val);
      }else{
        //TODO: if a node is not an array, then check if it should be an array
        //also determine if it is a leaf node
        if (options.isArray(property, newJpath, isLeaf )) {
          compressedObj[property] = [val];
        }else{
          compressedObj[property] = val;
        }
      }
    }

  }
  if(text.length > 0) compressedObj[options.textNodeName] = text;
  return compressedObj;
}

function propName(obj){
  const keys = Object.keys(obj);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    if(key !== "attributes") return key;
  }
}

function assignAttributes(obj, attrMap, jpath, options){
  if (attrMap) {
    const keys = Object.keys(attrMap);
    const len = keys.length; //don't make it inline
    for (let i = 0; i < len; i++) {
      const atrrName = keys[i];
      if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
        obj[atrrName] = [ attrMap[atrrName] ];
      } else {
        obj[atrrName] = attrMap[atrrName];
      }
    }
  }
}

function isLeafTag(obj, options){
  const propCount = Object.keys(obj).length;
  if( propCount === 0 || (propCount === 1 && obj[options.textNodeName]) ) return true;
  return false;
}
exports.convertToJson = prettify;
<commit_msg>fix: text concatenation<commit_after>'use strict';

const util = require('./util');

const convertToJson = function(node, options, parentTagName, jpath) {

  const jObj = {};

  // when no child node or attr is present
  if (!options.alwaysCreateTextNode && (!node.child || util.isEmptyObject(node.child)) && (!node.attrsMap || util.isEmptyObject(node.attrsMap))) {
    return util.isExist(node.val) ? node.val : '';
  }

  // otherwise create a textnode if node has some text
  if (util.isExist(node.val) && !(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) {
    const tagName = options.textNodeName;
    const newJpath = jpath + "." + tagName;
    jObj[tagName] = options.isArray(tagName, newJpath, true) ? [node.val] : node.val;
  }

  copyAttributesifPresent(jObj, node.attrsMap, jpath, options);

  const keys = Object.keys(node.child);
  for (let index = 0; index < keys.length; index++) {
    const tagName = keys[index];
    let newJpath = "";
    if(jpath === undefined) newJpath = tagName;
    else newJpath = jpath + "." + tagName;

    if (node.child[tagName] && node.child[tagName].length > 1) {
      jObj[tagName] = [];
      for (let tag in node.child[tagName]) {
        if (node.child[tagName].hasOwnProperty(tag)) {
          jObj[tagName].push(
            convertToJson(node.child[tagName][tag], options, tagName, newJpath)
          );
        }
      }
    } else {

      const result = convertToJson(node.child[tagName][0], options, tagName, newJpath);
      jObj[tagName] = options.isArray(tagName, newJpath, isLeafNode(node.child, tagName)) ? [result] : result;
    }
  }

  //add value
  return jObj;
};
function copyAttributesifPresent(tagObj, attrMap, jpath, options){
  if (attrMap) {
    const keys = Object.keys(attrMap);
    const len = keys.length; //don't make it inline
    for (let i = 0; i < len; i++) {
      const atrrName = keys[i];
      if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
        tagObj[atrrName] = [ attrMap[atrrName] ];
      } else {
        tagObj[atrrName] = attrMap[atrrName];
      }
    }
  }
}

//a non-leaf tag would be an array
function prettify(node, options){
  return compress( [node], options);
}

/**
 *
 * @param {array} arr
 * @param {object} options
 * @param {string} jPath
 * @returns object
 */
function compress(arr, options, jPath){
  let text;
  const compressedObj = {};
  for (let i = 0; i < arr.length; i++) {
    const tagObj = arr[i];
    const property = propName(tagObj);
    let newJpath = "";
    if(jPath === undefined) newJpath = property;
    else newJpath = jPath + "." + property;

    if(property === options.textNodeName){
      if(text === undefined) text = tagObj[property];
      else text += "" + tagObj[property];
    }else if(property === undefined){
      continue;
    }else if(tagObj[property]){

      let val = compress(tagObj[property], options, newJpath);
      const isLeaf = isLeafTag(val, options);

      if(tagObj.attributes){
        assignAttributes( val, tagObj.attributes, newJpath, options);
      }else if(Object.keys(val).length === 1 && val[options.textNodeName] && !options.alwaysCreateTextNode){
        val = val[options.textNodeName];
      }else if(Object.keys(val).length === 0){
        if(options.alwaysCreateTextNode) val[options.textNodeName] = "";
        else val = "";
      }

      if(compressedObj[property] !== undefined) {
        if(!Array.isArray(compressedObj[property])) {
          compressedObj[property] = [ compressedObj[property] ];
        }
        compressedObj[property].push(val);
      }else{
        //TODO: if a node is not an array, then check if it should be an array
        //also determine if it is a leaf node
        if (options.isArray(property, newJpath, isLeaf )) {
          compressedObj[property] = [val];
        }else{
          compressedObj[property] = val;
        }
      }
    }

  }
  if(text && text.length > 0) compressedObj[options.textNodeName] = text;
  return compressedObj;
}

function propName(obj){
  const keys = Object.keys(obj);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    if(key !== "attributes") return key;
  }
}

function assignAttributes(obj, attrMap, jpath, options){
  if (attrMap) {
    const keys = Object.keys(attrMap);
    const len = keys.length; //don't make it inline
    for (let i = 0; i < len; i++) {
      const atrrName = keys[i];
      if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
        obj[atrrName] = [ attrMap[atrrName] ];
      } else {
        obj[atrrName] = attrMap[atrrName];
      }
    }
  }
}

function isLeafTag(obj, options){
  const propCount = Object.keys(obj).length;
  if( propCount === 0 || (propCount === 1 && obj[options.textNodeName]) ) return true;
  return false;
}
exports.convertToJson = prettify;
<|endoftext|>


<commit_before>'use strict';
/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    Instructor = mongoose.model('Instructor'),
    Applicant = mongoose.model('Applicant'),
    User = mongoose.model('User'),
    Bootcamp = mongoose.model('Bootcamp'),
    Skillset = mongoose.model('Skillset'),
    _ = require('lodash');
var users = require('../../app/controllers/users');
var uuid = require('node-uuid'),
    multiparty = require('multiparty');

var path = require('path'),
    fs = require('fs');

exports.returnJson = function(res, id) {
    Applicant.findById(id).where({_type: 'Applicant'}).exec(function(err, user) {
       res.jsonp(user);
    });
};

exports.jsonCamp = function(res, id) {
    Bootcamp.findById(id).exec(function(err, camp) {
       res.jsonp(camp);
    });
};

/**
Input assessment score for trainee
*/
exports.createAssmt = function(req, res){
	req.body.instructorId = req.user.id;
	req.body.applicantId = req.trainee._id;

	Applicant.update(
      {
        _id: req.trainee._id,
      },
      { $push: {
        "assessments": req.body }
      },
      function(err) {
      	  if (err) {
             return res.send(400, {message: err });
          } else {
             //res.jsonp(trainee);
             returnJson(res, req.trainee._id);
          }
      }
    );
};

/*
*Update assessment
*/
exports.updateAssmt = function(req, res) {
	var assessment = req.assessment,
	    trainee = req.trainee;

	assessment = _.extend(assessment , req.body);
	Applicant.update({_id: trainee._id, 'assessments._id': assessment._id},
	      {$set:
	      	  {  'assessments.$.assessment_name' : assessment.assessment_name,
	             'assessments.$.assessment_date' : assessment.assessment_date,
	             'assessments.$.score ': req.body.score
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, { message: err });
              } else {
                 //res.jsonp(trainee);
                 returnJson(res, trainee._id);
              }
          }
    );
};

/*
*delete an assessment
*/
exports.deleteAssmt = function(req, res) {
	var assessment = req.assessment,
	    trainee = req.trainee;

    Applicant.update(
	    { '_id': trainee._id },
	    { $pull: { 'assessments': { '_id': assessment._id } }
	    }, function (err) {
	    	if (err) {
				return res.send(400, {
					message: 'error occurred while trying to delete assessment'
				});
			} else {
				//res.jsonp(trainee);
				returnJson(res, trainee._id);
			}
	    }
    );
};

/*
*Select a fellow
*/
exports.selectFellow = function(req, res){
	  var trainee = req.trainee,
	      role = req.body.role;
      trainee = _.extend(trainee, req.body);

      if (role === 'applicant' || role === 'admin' || role === 'instructor') {
         return res.send(400, {
              message: 'Error: action couldn\'t be carried out'
         });
      } else {
            Applicant.update({_id: trainee._id},
		      {$set:
		      	  { 'role' : role }

		      },
	          function(err) {
	          	  if (err) {
	                 return res.send(400, {message: 'could not change applicant role' });
	              } else {
	                 //res.jsonp(trainee);
	                 returnJson(res, trainee._id);
	              }
	          }
            );
      }
};

/*
*Rate a fellow's skill
*/
exports.rateFellow = function(req, res) {
	var skillset = req.body,
        fellow = req.trainee;

    if (fellow.role !== 'fellow') {
    	return res.send(400, {
			   message: 'Error: You can only rate a fellow\'s skills'
	    });
    } else if (req.body.rating < 1 || req.body.rating > 10) {
		return res.send(400, {
			   message: 'Error: rating is a 10 point system'
	    });
	} else {
		Applicant.update(
		    { _id: fellow._id },
		    { $push: { 'skillSets':  skillset }
		    },
		    function (err) {
		    	if (err) {
					return res.send(400, {
						message: 'Error: Couldn\'t rate fellow'
					});
				} else {
					//res.jsonp(fellow);
					returnJson(res, fellow._id);
				}
	        }
        );
	}
};

/*
*Instructor adds his own skillset
*/
exports.addSkills = function(req, res) {
	var skill = req.body;
	User.findById(req.user._id).exec(function(err, user) {
        if (user._type === 'Instructor') {
        	 Instructor.update(
			    { _id: user._id },
			    { $push: { 'skillSets':  skill }
			    },
			    function (err) {
			    	if (err) {
						return res.send(400, {
							message: 'Error: Couldn\'t add skill'
						});
					} else {
						//res.jsonp(user);
						returnJson(res, req.user._id);
					}
		        }
	         );
        } else {
        	return res.send(400, {
			        message: 'Error: You are not authorized to carryout this operation'
			});
        }
	});
};

/*
*Edit a fellow's rating
*/
exports.editRating = function(req, res) {
	var skillset = req.skill,
        fellow = req.trainee;

    skillset = _.extend(skillset, req.body);
    if (req.profile) { //if instructor wants to edit his own skills
    	Instructor.update({'_id': req.profile._id, 'skillSets._id': skillset._id},
	      {$set:
	      	  { 'skillSets.$.skill' : skillset.skill,
	            'skillSets.$.rating' : skillset.rating
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, {message: 'could not edit rating' });
              } else {
                 //res.jsonp(user);
                 returnJson(res, req.profile._id);
              }
          }
        );
    } else { //for instructor to edit fellow's rating
	    Applicant.update({'_id': fellow._id, 'skillSets._id': skillset._id},
	      {$set:
	      	  { 'skillSets.$.skill' : skillset.skill,
	            'skillSets.$.rating' : skillset.rating
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, { message: 'could not edit rating' });
              } else {
                 //res.jsonp(fellow);
                 returnJson(res, fellow._id);
              }
          }
        );
	}
};

/*
*Delete a rating
*/
exports.deleteRating = function(req, res){
	var skillset = req.skill,
        fellow = req.trainee;

    if (req.profile) {
		Instructor.update(
		    { _id: req.profile._id },
		    { $pull: { 'skillSets': { '_id': skillset._id } }
		    }, function (err) {
		    	if (err) {
					return res.send(400, {
						message: 'could not delete rating'
					});
				} else {
					//res.jsonp(user);
					returnJson(res, req.profile._id);
				}
		    }
	    );
    } else {
        Applicant.update(
		    { '_id': fellow._id },
		    { $pull: { 'skillSets': { '_id': skillset._id } }
		    }, function (err, fellow) {
		    	if (err) {
					return res.send(400, {
						message: 'could not delete rating'
					});
				} else {
					//res.jsonp(fellow);
					returnJson(res, fellow._id);
				}
		    }
	    );
	}
};

/**
 * Show the current trainee/fellow
 */
exports.readTrainee = function(req, res) {
    res.jsonp(req.trainee);
};

/**
* Particular trainee middleware
*/
exports.traineeByID = function(req, res, next, id){
    Applicant.findById(id).where({_type: 'Applicant'}).populate('campId','camp_name').exec(function(err, trainee) {
		if (err) return next(err);
		if (!trainee) return next(new Error('Failed to load trainee ' + id));
		req.trainee = trainee;
		next();
	});
};

/**
* Particular skillset middleware
*/
exports.skillByID = function(req, res, next, id){
	if (req.profile && req.profile._type === 'Instructor') {
		req.skill = req.profile.skillSets.id(id);
	} else {
        req.skill = req.trainee.skillSets.id(id);
	}
    next();
};

/**
* Particular assessment middleware
*/
exports.assessmentByID = function(req, res, next, id){
    req.assessment = req.trainee.assessments.id(id);
    next();
};

/**
* Check if he is the creator of the assessment middleware
*/
exports.isCreator = function(req, res, next){
	if (req.assessment.instructorId.toString() !== req.user.id) {
	   return res.send(403, 'User is not authorized');
	}
	next();
};

/**
 * Instructor authorization middleware
 */
exports.checkRights = function(req, res, next) {
    if (req.user._type === 'Instructor' && req.user.role === 'instructor') {
        next();
    } else {
        return res.send(403, {
              message: 'User is not authorized'
        });
    }
};

/**
 * Upload image
 */
var uploadImage = function(req, res, contentType, tmpPath, destPath, person, experience) {

    // Server side file type checker.
    if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
        fs.unlink(tmpPath);
        return res.send(400, {
        	message: 'Unsupported file type. Only jpeg or png format allowed'
        });
    } else {
    	fs.readFile(tmpPath , function(err, data) {
	        fs.writeFile(destPath, data, function(err) {
	        	if (err) {
	        	   return res.send(400, { message: 'Destination path doesn\'t exist.' });
	        	}
	        	else {
	               fs.unlink(tmpPath, function(){
			        	var path =  person.photo;

				        if (fs.existsSync(path)) {
						    fs.unlink(path);
						}

				        Instructor.update(
				             {_id: person._id},
				             {$set: { photo: destPath, experience: experience } },
				              function (error, user) {
				                 if (error) {
				                    return res.send(400, { message: 'Error: save operation failed' });
				                 } else {
				                     res.jsonp(user);
				                 }
				              }
				        );
	               });
	        	}
	        });
	    });
    }
};

/**
 * Instructor updates experience and photo
 */
exports.updateInfo = function(req, res) {
    //Parse Form
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {

    	User.findById(req.user.id).exec(function(err, person) {
	        if (err) {
               return res.send(400, { message: err });
	        } else if (!person) {
	        	return res.send(400, { message: 'Failed to load User ' + person._id });
	        } else {
	        	var experience = '';
	        	if (fields.exp) {
		        	experience = fields.exp[0];
		        }

		        if (files.file) {
		            //if there is a file do upload
		            var file = files.file[0],     contentType = file.headers['content-type'],
		                tmpPath = file.path,      extIndex = tmpPath.lastIndexOf('.'),
		                extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);

		            // uuid is for generating unique filenames.
		            var fileName = uuid.v4() + extension;

		            var destPath =  'public/modules/core/img/server/Temp/' + fileName;

		            uploadImage(req, res, contentType, tmpPath, destPath, person, experience);
		        } else {
		        	Instructor.update(
			             {_id: person._id},
			             {$set: { experience: experience } },
			              function (error, user) {
			                 if (error) {
			                    return res.send(400, { message: 'Error: save operation failed' });
			                 } else {
			                     res.jsonp(user);
			                 }
			              }
				    );
		        }
		    }
	    });
    });
};

/**
 * Delete photo
 */
exports.deletePhoto = function(req, res) {
	var profile = req.profile;

	if (fs.existsSync(profile.photo)) {
	    fs.unlink(profile.photo);
	}

	Instructor.update(
	     {_id: profile._id},
	     {$set: { photo: '' } },
	      function (error) {
	         if (error) {
	            return res.send(400, { message: 'Error: save operation failed' });
	         } else {
	             //res.jsonp(user);
	             returnJson(res, profile._id);
	         }
	      }
	);
};<commit_msg>Project 100%<commit_after>'use strict';
/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    Instructor = mongoose.model('Instructor'),
    Applicant = mongoose.model('Applicant'),
    User = mongoose.model('User'),
    Bootcamp = mongoose.model('Bootcamp'),
    Skillset = mongoose.model('Skillset'),
    _ = require('lodash');
var users = require('../../app/controllers/users');
var uuid = require('node-uuid'),
    multiparty = require('multiparty');

var path = require('path'),
    fs = require('fs');

exports.returnJson = function(res, id) {
    Applicant.findById(id).where({_type: 'Applicant'}).exec(function(err, user) {
       res.jsonp(user);
    });
};

exports.jsonCamp = function(res, id) {
    Bootcamp.findById(id).exec(function(err, camp) {
       res.jsonp(camp);
    });
};

/**
Input assessment score for trainee
*/
exports.createAssmt = function(req, res){
	req.body.instructorId = req.user.id;
	req.body.applicantId = req.trainee._id;

	Applicant.update(
      {
        _id: req.trainee._id,
      },
      { $push: {
        "assessments": req.body }
      },
      function(err) {
      	  if (err) {
             return res.send(400, {message: err });
          } else {
             //res.jsonp(trainee);
             exports.returnJson(res, req.trainee._id);
          }
      }
    );
};

/*
*Update assessment
*/
exports.updateAssmt = function(req, res) {
	var assessment = req.assessment,
	    trainee = req.trainee;

	assessment = _.extend(assessment , req.body);
	Applicant.update({_id: trainee._id, 'assessments._id': assessment._id},
	      {$set:
	      	  {  'assessments.$.assessment_name' : assessment.assessment_name,
	             'assessments.$.assessment_date' : assessment.assessment_date,
	             'assessments.$.score ': req.body.score
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, { message: err });
              } else {
                 //res.jsonp(trainee);
                 exports.returnJson(res, trainee._id);
              }
          }
    );
};

/*
*delete an assessment
*/
exports.deleteAssmt = function(req, res) {
	var assessment = req.assessment,
	    trainee = req.trainee;

    Applicant.update(
	    { '_id': trainee._id },
	    { $pull: { 'assessments': { '_id': assessment._id } }
	    }, function (err) {
	    	if (err) {
				return res.send(400, {
					message: 'error occurred while trying to delete assessment'
				});
			} else {
				//res.jsonp(trainee);
				exports.returnJson(res, trainee._id);
			}
	    }
    );
};

/*
*Select a fellow
*/
exports.selectFellow = function(req, res){
	  var trainee = req.trainee,
	      role = req.body.role;
      trainee = _.extend(trainee, req.body);

      if (role === 'applicant' || role === 'admin' || role === 'instructor') {
         return res.send(400, {
              message: 'Error: action couldn\'t be carried out'
         });
      } else {
            Applicant.update({_id: trainee._id},
		      {$set:
		      	  { 'role' : role }

		      },
	          function(err) {
	          	  if (err) {
	                 return res.send(400, {message: 'could not change applicant role' });
	              } else {
	                 //res.jsonp(trainee);
	                 exports.returnJson(res, trainee._id);
	              }
	          }
            );
      }
};

/*
*Rate a fellow's skill
*/
exports.rateFellow = function(req, res) {
	var skillset = req.body,
        fellow = req.trainee;

    if (fellow.role !== 'fellow') {
    	return res.send(400, {
			   message: 'Error: You can only rate a fellow\'s skills'
	    });
    } else if (req.body.rating < 1 || req.body.rating > 10) {
		return res.send(400, {
			   message: 'Error: rating is a 10 point system'
	    });
	} else {
		Applicant.update(
		    { _id: fellow._id },
		    { $push: { 'skillSets':  skillset }
		    },
		    function (err) {
		    	if (err) {
					return res.send(400, {
						message: 'Error: Couldn\'t rate fellow'
					});
				} else {
					//res.jsonp(fellow);
					exports.returnJson(res, fellow._id);
				}
	        }
        );
	}
};

/*
*Instructor adds his own skillset
*/
exports.addSkills = function(req, res) {
	var skill = req.body;
	User.findById(req.user._id).exec(function(err, user) {
        if (user._type === 'Instructor') {
        	 Instructor.update(
			    { _id: user._id },
			    { $push: { 'skillSets':  skill }
			    },
			    function (err) {
			    	if (err) {
						return res.send(400, {
							message: 'Error: Couldn\'t add skill'
						});
					} else {
						//res.jsonp(user);
						exports.returnJson(res, req.user._id);
					}
		        }
	         );
        } else {
        	return res.send(400, {
			        message: 'Error: You are not authorized to carryout this operation'
			});
        }
	});
};

/*
*Edit a fellow's rating
*/
exports.editRating = function(req, res) {
	var skillset = req.skill,
        fellow = req.trainee;

    skillset = _.extend(skillset, req.body);
    if (req.profile) { //if instructor wants to edit his own skills
    	Instructor.update({'_id': req.profile._id, 'skillSets._id': skillset._id},
	      {$set:
	      	  { 'skillSets.$.skill' : skillset.skill,
	            'skillSets.$.rating' : skillset.rating
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, {message: 'could not edit rating' });
              } else {
                 //res.jsonp(user);
                 exports.returnJson(res, req.profile._id);
              }
          }
        );
    } else { //for instructor to edit fellow's rating
	    Applicant.update({'_id': fellow._id, 'skillSets._id': skillset._id},
	      {$set:
	      	  { 'skillSets.$.skill' : skillset.skill,
	            'skillSets.$.rating' : skillset.rating
	      	  }

	      },
          function(err) {
          	  if (err) {
                 return res.send(400, { message: 'could not edit rating' });
              } else {
                 //res.jsonp(fellow);
                 exports.returnJson(res, fellow._id);
              }
          }
        );
	}
};

/*
*Delete a rating
*/
exports.deleteRating = function(req, res){
	var skillset = req.skill,
        fellow = req.trainee;

    if (req.profile) {
		Instructor.update(
		    { _id: req.profile._id },
		    { $pull: { 'skillSets': { '_id': skillset._id } }
		    }, function (err) {
		    	if (err) {
					return res.send(400, {
						message: 'could not delete rating'
					});
				} else {
					//res.jsonp(user);
					exports.returnJson(res, req.profile._id);
				}
		    }
	    );
    } else {
        Applicant.update(
		    { '_id': fellow._id },
		    { $pull: { 'skillSets': { '_id': skillset._id } }
		    }, function (err, fellow) {
		    	if (err) {
					return res.send(400, {
						message: 'could not delete rating'
					});
				} else {
					//res.jsonp(fellow);
					exports.returnJson(res, fellow._id);
				}
		    }
	    );
	}
};

/**
 * Show the current trainee/fellow
 */
exports.readTrainee = function(req, res) {
    res.jsonp(req.trainee);
};

/**
* Particular trainee middleware
*/
exports.traineeByID = function(req, res, next, id){
    Applicant.findById(id).where({_type: 'Applicant'}).populate('campId','camp_name').exec(function(err, trainee) {
		if (err) return next(err);
		if (!trainee) return next(new Error('Failed to load trainee ' + id));
		req.trainee = trainee;
		next();
	});
};

/**
* Particular skillset middleware
*/
exports.skillByID = function(req, res, next, id){
	if (req.profile && req.profile._type === 'Instructor') {
		req.skill = req.profile.skillSets.id(id);
	} else {
        req.skill = req.trainee.skillSets.id(id);
	}
    next();
};

/**
* Particular assessment middleware
*/
exports.assessmentByID = function(req, res, next, id){
    req.assessment = req.trainee.assessments.id(id);
    next();
};

/**
* Check if he is the creator of the assessment middleware
*/
exports.isCreator = function(req, res, next){
	if (req.assessment.instructorId.toString() !== req.user.id) {
	   return res.send(403, 'User is not authorized');
	}
	next();
};

/**
 * Instructor authorization middleware
 */
exports.checkRights = function(req, res, next) {
    if (req.user._type === 'Instructor' && req.user.role === 'instructor') {
        next();
    } else {
        return res.send(403, {
              message: 'User is not authorized'
        });
    }
};

/**
 * Upload image
 */
var uploadImage = function(req, res, contentType, tmpPath, destPath, person, experience) {

    // Server side file type checker.
    if (contentType !== 'image/png' && contentType !== 'image/jpeg') {
        fs.unlink(tmpPath);
        return res.send(400, {
        	message: 'Unsupported file type. Only jpeg or png format allowed'
        });
    } else {
    	fs.readFile(tmpPath , function(err, data) {
	        fs.writeFile(destPath, data, function(err) {
	        	if (err) {
	        	   return res.send(400, { message: 'Destination path doesn\'t exist.' });
	        	}
	        	else {
	               fs.unlink(tmpPath, function(){
			        	var path =  person.photo;

				        if (fs.existsSync(path)) {
						    fs.unlink(path);
						}

				        Instructor.update(
				             {_id: person._id},
				             {$set: { photo: destPath, experience: experience } },
				              function (error, user) {
				                 if (error) {
				                    return res.send(400, { message: 'Error: save operation failed' });
				                 } else {
				                     //res.jsonp(user);
				                     exports.returnJson(res, person._id);
				                 }
				              }
				        );
	               });
	        	}
	        });
	    });
    }
};

/**
 * Instructor updates experience and photo
 */
exports.updateInfo = function(req, res) {
    //Parse Form
    var form = new multiparty.Form();
    form.parse(req, function(err, fields, files) {

    	User.findById(req.user.id).exec(function(err, person) {
	        if (err) {
               return res.send(400, { message: err });
	        } else if (!person) {
	        	return res.send(400, { message: 'Failed to load User ' + person._id });
	        } else {
	        	var experience = '';
	        	if (fields.exp) {
		        	experience = fields.exp[0];
		        }

		        if (files.file) {
		            //if there is a file do upload
		            var file = files.file[0],     contentType = file.headers['content-type'],
		                tmpPath = file.path,      extIndex = tmpPath.lastIndexOf('.'),
		                extension = (extIndex < 0) ? '' : tmpPath.substr(extIndex);

		            // uuid is for generating unique filenames.
		            var fileName = uuid.v4() + extension;

		            var destPath =  'public/modules/core/img/server/Temp/' + fileName;

		            uploadImage(req, res, contentType, tmpPath, destPath, person, experience);
		        } else {
		        	Instructor.update(
			             {_id: person._id},
			             {$set: { experience: experience } },
			              function (error, user) {
			                 if (error) {
			                    return res.send(400, { message: 'Error: save operation failed' });
			                 } else {
			                     //res.jsonp(user);
			                     exports.returnJson(res, person._id);
			                 }
			              }
				    );
		        }
		    }
	    });
    });
};

/**
 * Delete photo
 */
exports.deletePhoto = function(req, res) {
	var profile = req.profile;

	if (fs.existsSync(profile.photo)) {
	    fs.unlink(profile.photo);
	}

	Instructor.update(
	     {_id: profile._id},
	     {$set: { photo: '' } },
	      function (error) {
	         if (error) {
	            return res.send(400, { message: 'Error: save operation failed' });
	         } else {
	             //res.jsonp(user);
	             exports.returnJson(res, profile._id);
	         }
	      }
	);
};<|endoftext|>


<commit_before>goog.provide('og.Globus');

goog.require('og.webgl.Handler');
goog.require('og.Renderer');
goog.require('og.scene.Planet');
goog.require('og.scene.PlanetAtmosphere');
goog.require('og.ellipsoid.wgs84');
goog.require('og.terrainProvider.EmptyTerrainProvider');

/**
 * Creates a WebGL context with globe.
 * @class
 *
 * @example
Basic initialization

 * globus = new og.Globus({
 *     'atmosphere': false,
 *     'target': 'globus',
 *     'name': 'Earth',
 *     'controls': [
 *          new og.control.MouseNavigation({ autoActivate: true }),
 *          new og.control.KeyboardNavigation({ autoActivate: true }),
 *          new og.control.EarthCoordinates({ autoActivate: true, center: false }),
 *          new og.control.LayerSwitcher({ autoActivate: true }),
 *          new og.control.ZoomControl({ autoActivate: true }),
 *          new og.control.TouchNavigation({ autoActivate: true }),
 *          new og.control.Sun({ autoActivate: true })
 *      ],
 *     'skybox': skybox,
 *     'terrain': terrain,
 *     'layers': [
 *          new og.layer.XYZ("OpenStreetMap", { isBaseLayer: true, url: "http://b.tile.openstreetmap.org/{z}/{x}/{y}.png", visibility: true, attribution: 'Data @ OpenStreetMap contributors, ODbL' })
 *      ],
 *     'autoActivated': true
 * });
 *
 * @param {object} options - Options:
 * @param {string} options.target - HTML element id where planet canvas have to be created.
 * @param {boolean} [options.skybox] - Render skybox. null - default.
 * @param {boolean} [options.atmosphere] - Render planet with atmosphere. False - default.
 * @param {string} [options.name] - Planet name. Default is unic identifier.
 * @param {og.terrainProvider.TerrainProvider} [options.terrain] - Terrain provider. Default no terrain - og.terrainProvider.EmptyTerrainProvider.
 * @param {Array.<og.control.BaseControl>} [options.controls] - Renderer controls array.
 * @param {Array.<og.layer.Layer>} [options.layers] - Planet layers.
 * @param {og.Extent} [options.viewExtent] - Viewable starting extent.
 * @param {boolean} [options.autoActivate] - Globus rendering auto activation flag. True is default.
 */
og.Globus = function (options) {

    //Canvas creation.
    var _canvasId = og.Globus.CANVAS_ID_PREFIX + og.Globus.__id;
    var _canvas = document.createElement("canvas");
    _canvas.id = _canvasId;
    _canvas.style.width = "100%";
    _canvas.style.height = "100%";
    _canvas.style.display = "block";
    _canvas.style.opacity = "1.0";

    /**
     * Dom element where WebGL canvas creates
     * @public
     * @type {Element}
     */
    this.div = document.getElementById(options.target);
    this.div.appendChild(_canvas);
    this.div.classList.add("ogViewport");
    function _disableWheel() { return false; };
    function _enableWheel() { return true; };
    this.div.onmouseenter = function () { document.onmousewheel = _disableWheel };
    this.div.onmouseleave = function () { document.onmousewheel = _enableWheel };

    //WegGL handler creation
    var _handler = new og.webgl.Handler(_canvasId, { 'alpha': false, 'antialias': false });
    _handler.initialize();

    /**
     * Interface for the renderer context(events, input states, renderer nodes etc.)
     * @public
     * @type {og.Renderer}
     */
    this.renderer = new og.Renderer(_handler);
    this.renderer.init();
    this.renderer.div = this.div;
    this.renderer.div.attributions = document.createElement("div");
    this.renderer.div.attributions.classList.add("ogAttribution");
    this.div.appendChild(this.renderer.div.attributions);

    //Skybox
    if (options.skybox) {
        this.renderer.addRenderNode(options.skybox);
    }

    /**
     * Planet node name. Access with this.renderer.<name>
     * @private
     * @type {String}
     */
    this._planetName = options.name ? options.name : og.Globus.PLANET_NAME_PREFIX + og.Globus.__id;

    if (options.atmosphere) {
        /**
         * Render node renders a planet.
         * @public
         * @type {og.scene.Planet|og.scene.PlanetAtmosphere}
         */
        this.planet = new og.scene.PlanetAtmosphere(this._planetName, options.ellipsoid ? options.ellipsoid : og.ellipsoid.wgs84);
    } else {
        this.planet = new og.scene.Planet(this._planetName, options.ellipsoid ? options.ellipsoid : og.ellipsoid.wgs84);
    }

    //Attach terrain provider
    if (options.terrain) {
        this.planet.setTerrainProvider(options.terrain);
    } else {
        this.planet.setTerrainProvider(new og.terrainProvider.EmptyTerrainProvider());
    }

    this.renderer.addRenderNode(this.planet);

    //Add controls
    if (options.controls) {
        this.planet.addControls(options.controls)
    }

    if (options.layers) {
        this.planet.addLayers(options.layers);
    }

    og.Globus.__id++;

    //TODO: view center, altitude, extent
    if (options.viewExtent) {
        this.planet.viewToExtent(options.viewExtent);
    }

    var opacityCounter = 0;
    var fadeHandler = null;
    var stopHandler = null;

    /**
     * Starts screen brightness fading in effect by the duration time.
     * @public
     * @param {number} - fadein duration time.
     */
    this.fadeIn = function (duration) {
        clearInterval(stopHandler);
        clearInterval(fadeHandler);
        _canvas.style.opacity = 0.0;
        opacityCounter = 0.0;
        var delta = 10.0;
        var d = 1.0 / (duration / delta);

        fadeHandler = setInterval(function () {
            opacityCounter += d;
            if (opacityCounter >= 1) {
                opacityCounter = 1.0;
                clearInterval(fadeHandler);
            }
            _canvas.style.opacity = opacityCounter;
        }, delta);
    };

    /**
     * Starts screen brightness fading out effect by the duration time.
     * @public
     * @param {number} - Fadeout duration time.
     */
    this.fadeOut = function (duration) {
        clearInterval(stopHandler);
        clearInterval(fadeHandler);
        _canvas.style.opacity = 1.0;
        opacityCounter = 1.0;
        var delta = 10.0;
        var d = 1 / (duration / delta);

        fadeHandler = setInterval(function () {
            opacityCounter -= d;
            if (opacityCounter <= 0.0) {
                opacityCounter = 0.0;
                clearInterval(fadeHandler);
            }
            _canvas.style.opacity = opacityCounter;
        }, delta);

    };

    //Run!
    if (isUndefined(options.autoActivate) || options.autoActivate) {
        this.fadeIn(500);
        this.renderer.start();
    }
};

og.Globus.__id = 1;

/** @const {string} */
og.Globus.CANVAS_ID_PREFIX = "globus_viewport_";
/** @const {string} */
og.Globus.PLANET_NAME_PREFIX = "globus_planet_";

/**
 * Returns true if the object pointer is undefined.
 * @function
 * @param {Object} obj - Object pointer.
 * @returns {boolean}
 */
function isUndefined(obj) {
    return obj === void 0;
};<commit_msg>Default controls.<commit_after>goog.provide('og.Globus');

goog.require('og.webgl.Handler');
goog.require('og.Renderer');
goog.require('og.scene.Planet');
goog.require('og.scene.PlanetAtmosphere');
goog.require('og.ellipsoid.wgs84');
goog.require('og.terrainProvider.EmptyTerrainProvider');
goog.require('og.control.MouseNavigation');
goog.require('og.control.TouchNavigation');
goog.require('og.control.ZoomControl');
goog.require('og.control.EarthCoordinates');
goog.require('og.control.Sun');

/**
 * Creates a WebGL context with globe.
 * @class
 *
 * @example
Basic initialization

 * globus = new og.Globus({
 *     'atmosphere': false,
 *     'target': 'globus',
 *     'name': 'Earth',
 *     'controls': [
 *          new og.control.MouseNavigation({ autoActivate: true }),
 *          new og.control.KeyboardNavigation({ autoActivate: true }),
 *          new og.control.EarthCoordinates({ autoActivate: true, center: false }),
 *          new og.control.LayerSwitcher({ autoActivate: true }),
 *          new og.control.ZoomControl({ autoActivate: true }),
 *          new og.control.TouchNavigation({ autoActivate: true }),
 *          new og.control.Sun({ autoActivate: true })
 *      ],
 *     'skybox': skybox,
 *     'terrain': terrain,
 *     'layers': [
 *          new og.layer.XYZ("OpenStreetMap", { isBaseLayer: true, url: "http://b.tile.openstreetmap.org/{z}/{x}/{y}.png", visibility: true, attribution: 'Data @ OpenStreetMap contributors, ODbL' })
 *      ],
 *     'autoActivated': true
 * });
 *
 * @param {object} options - Options:
 * @param {string} options.target - HTML element id where planet canvas have to be created.
 * @param {boolean} [options.skybox] - Render skybox. null - default.
 * @param {boolean} [options.atmosphere] - Render planet with atmosphere. False - default.
 * @param {string} [options.name] - Planet name. Default is unic identifier.
 * @param {og.terrainProvider.TerrainProvider} [options.terrain] - Terrain provider. Default no terrain - og.terrainProvider.EmptyTerrainProvider.
 * @param {Array.<og.control.BaseControl>} [options.controls] - Renderer controls array.
 * @param {Array.<og.layer.Layer>} [options.layers] - Planet layers.
 * @param {og.Extent} [options.viewExtent] - Viewable starting extent.
 * @param {boolean} [options.autoActivate] - Globus rendering auto activation flag. True is default.
 */
og.Globus = function (options) {

    //Canvas creation.
    var _canvasId = og.Globus.CANVAS_ID_PREFIX + og.Globus.__id;
    var _canvas = document.createElement("canvas");
    _canvas.id = _canvasId;
    _canvas.style.width = "100%";
    _canvas.style.height = "100%";
    _canvas.style.display = "block";
    _canvas.style.opacity = "1.0";

    /**
     * Dom element where WebGL canvas creates
     * @public
     * @type {Element}
     */
    this.div = document.getElementById(options.target);
    this.div.appendChild(_canvas);
    this.div.classList.add("ogViewport");
    function _disableWheel() { return false; };
    function _enableWheel() { return true; };
    this.div.onmouseenter = function () { document.onmousewheel = _disableWheel };
    this.div.onmouseleave = function () { document.onmousewheel = _enableWheel };

    //WegGL handler creation
    var _handler = new og.webgl.Handler(_canvasId, { 'alpha': false, 'antialias': false });
    _handler.initialize();

    /**
     * Interface for the renderer context(events, input states, renderer nodes etc.)
     * @public
     * @type {og.Renderer}
     */
    this.renderer = new og.Renderer(_handler);
    this.renderer.init();
    this.renderer.div = this.div;
    this.renderer.div.attributions = document.createElement("div");
    this.renderer.div.attributions.classList.add("ogAttribution");
    this.div.appendChild(this.renderer.div.attributions);

    //Skybox
    if (options.skybox) {
        this.renderer.addRenderNode(options.skybox);
    }

    /**
     * Planet node name. Access with this.renderer.<name>
     * @private
     * @type {String}
     */
    this._planetName = options.name ? options.name : og.Globus.PLANET_NAME_PREFIX + og.Globus.__id;

    if (options.atmosphere) {
        /**
         * Render node renders a planet.
         * @public
         * @type {og.scene.Planet|og.scene.PlanetAtmosphere}
         */
        this.planet = new og.scene.PlanetAtmosphere(this._planetName, options.ellipsoid ? options.ellipsoid : og.ellipsoid.wgs84);
    } else {
        this.planet = new og.scene.Planet(this._planetName, options.ellipsoid ? options.ellipsoid : og.ellipsoid.wgs84);
    }

    //Attach terrain provider
    if (options.terrain) {
        this.planet.setTerrainProvider(options.terrain);
    } else {
        this.planet.setTerrainProvider(new og.terrainProvider.EmptyTerrainProvider());
    }

    this.renderer.addRenderNode(this.planet);

    //Add controls
    if (options.controls) {
        this.planet.addControls(options.controls)
    } else {
        this.planet.addControls([
            new og.control.MouseNavigation(),
            new og.control.TouchNavigation(),
            new og.control.ZoomControl(),
            new og.control.EarthCoordinates(),
            new og.control.Sun()
        ]);
    }

    if (options.layers) {
        this.planet.addLayers(options.layers);
    }

    og.Globus.__id++;

    //TODO: view center, altitude, extent
    if (options.viewExtent) {
        this.planet.viewToExtent(options.viewExtent);
    }

    var opacityCounter = 0;
    var fadeHandler = null;
    var stopHandler = null;

    /**
     * Starts screen brightness fading in effect by the duration time.
     * @public
     * @param {number} - fadein duration time.
     */
    this.fadeIn = function (duration) {
        clearInterval(stopHandler);
        clearInterval(fadeHandler);
        _canvas.style.opacity = 0.0;
        opacityCounter = 0.0;
        var delta = 10.0;
        var d = 1.0 / (duration / delta);

        fadeHandler = setInterval(function () {
            opacityCounter += d;
            if (opacityCounter >= 1) {
                opacityCounter = 1.0;
                clearInterval(fadeHandler);
            }
            _canvas.style.opacity = opacityCounter;
        }, delta);
    };

    /**
     * Starts screen brightness fading out effect by the duration time.
     * @public
     * @param {number} - Fadeout duration time.
     */
    this.fadeOut = function (duration) {
        clearInterval(stopHandler);
        clearInterval(fadeHandler);
        _canvas.style.opacity = 1.0;
        opacityCounter = 1.0;
        var delta = 10.0;
        var d = 1 / (duration / delta);

        fadeHandler = setInterval(function () {
            opacityCounter -= d;
            if (opacityCounter <= 0.0) {
                opacityCounter = 0.0;
                clearInterval(fadeHandler);
            }
            _canvas.style.opacity = opacityCounter;
        }, delta);

    };

    //Run!
    if (isUndefined(options.autoActivate) || options.autoActivate) {
        this.fadeIn(500);
        this.renderer.start();
    }
};

og.Globus.__id = 1;

/** @const {string} */
og.Globus.CANVAS_ID_PREFIX = "globus_viewport_";
/** @const {string} */
og.Globus.PLANET_NAME_PREFIX = "globus_planet_";

/**
 * Returns true if the object pointer is undefined.
 * @function
 * @param {Object} obj - Object pointer.
 * @returns {boolean}
 */
function isUndefined(obj) {
    return obj === void 0;
};<|endoftext|>


<commit_before>import { createSelector, createStructuredSelector } from 'reselect';
import compact from 'lodash/compact';
import isEmpty from 'lodash/isEmpty';
import groupBy from 'lodash/groupBy';
import flatMap from 'lodash/flatMap';

import { getAllLayers, getActiveDatasets } from 'components/map/selectors';
import { getActiveArea } from 'providers/areas-provider/selectors';
import { getDataLocation } from 'utils/location';
import { locationLevelToStr } from 'utils/format';
import { getWidgets } from 'components/widgets/selectors';

import { initialState } from './reducers';

const selectAnalysisUrlState = state =>
  state.location && state.location.query && state.location.query.analysis;
const selectAnalysisLoading = state => state.analysis && state.analysis.loading;
const selectDatasetsLoading = state => state.datasets && state.datasets.loading;
const selectGeostoreLoading = state => state.geostore && state.geostore.loading;
const selectGeodecriberLoading = state =>
  state.geodescriber && state.geodescriber.loading;
const selectSearch = state => state.location && state.location.search;
const selectAnalysisLocation = state =>
  state.analysis && state.analysis.location;
const selectEmbed = state =>
  state.location &&
  state.location.pathname &&
  state.location.pathname.includes('/embed');
const selectError = state => state.analysis && state.analysis.error;
const selectDatasets = state => state.datasets && state.datasets.data;
const selectGeostoreSize = state =>
  state.geostore && state.geostore.data && state.geostore.data.areaHa;

export const getLoading = createSelector(
  [
    selectAnalysisLoading,
    selectDatasetsLoading,
    selectGeostoreLoading,
    selectGeodecriberLoading
  ],
  (analysisLoading, datasetsLoading, geostoreLoading, geodescriberLoading) =>
    analysisLoading || datasetsLoading || geostoreLoading || geodescriberLoading
);

export const getAnalysisSettings = createSelector(
  [selectAnalysisUrlState],
  urlState => ({
    ...initialState.settings,
    ...urlState
  })
);

export const getShowDraw = createSelector(
  getAnalysisSettings,
  settings => settings.showDraw
);

export const getBoundaryDatasets = createSelector(
  [selectDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.isBoundary).map(d => ({
      name: d.name,
      dataset: d.id,
      layers: d.layers.map(l => l.id),
      id: d.id,
      label: d.name,
      value: d.layer
    }));
  }
);

export const getAllBoundaries = createSelector(
  [getBoundaryDatasets],
  boundaries =>
    [{ label: 'No boundaries', value: 'no-boundaries' }].concat(boundaries)
);

export const getActiveBoundaryDatasets = createSelector(
  [getBoundaryDatasets, getActiveDatasets],
  (datasets, activeDatasets) => {
    if (isEmpty(datasets) || isEmpty(activeDatasets)) return null;
    const datasetIds = activeDatasets.map(d => d.dataset);
    return datasets.find(d => datasetIds.includes(d.dataset));
  }
);

export const getWidgetLayers = createSelector(
  [getWidgets],
  widgets =>
    widgets &&
    flatMap(
      widgets.map(w =>
        flatMap(
          w.datasets &&
            w.datasets.map(
              d =>
                (Array.isArray(d.layers) ? d.layers : Object.values(d.layers))
            )
        )
      )
    )
);

export const getLayerEndpoints = createSelector(
  [getAllLayers, getDataLocation, getWidgetLayers],
  (layers, location, widgetLayers) => {
    if (!layers || !layers.length) return null;

    const { type, adm2 } = location;
    const routeType = type === 'country' ? 'admin' : type;
    const lossLayer = layers.find(l => l.metadata === 'tree_cover_loss');
    const hasWidgetLayers = widgetLayers && !!widgetLayers.length;

    const admLevel = locationLevelToStr(location);
    const endpoints = compact(
      layers
        .filter(
          l =>
            l.analysisConfig &&
            !l.analysisDisabled &&
            (!hasWidgetLayers || !widgetLayers.includes(l.id)) &&
            (!l.admLevels || l.admLevels.includes(admLevel))
        )
        .map(l => {
          const analysisConfig =
            l.analysisConfig.find(
              a =>
                a.type === routeType ||
                ((routeType === 'use' || routeType === 'wdpa') &&
                  a.type === 'geostore')
            ) || {};
          const { params, decodeParams } = l;
          return {
            name: l.name,
            version: analysisConfig.version || 'v1',
            slug: analysisConfig.service,
            params: {
              ...(analysisConfig.service === 'umd-loss-gain' &&
                lossLayer && {
                ...lossLayer.decodeParams
              }),
              ...decodeParams,
              ...params,
              query: analysisConfig.query
            }
          };
        })
    );

    const groupedEndpoints = groupBy(endpoints, 'slug');
    const parsedEndpoints = Object.keys(groupedEndpoints)
      .filter(slug => slug !== 'undefined')
      .map(slug => {
        let params = {};
        groupedEndpoints[slug].forEach(e => {
          params = {
            ...params,
            ...e.params
          };
        });

        return {
          slug,
          params,
          version: groupedEndpoints[slug][0].version,
          name: groupedEndpoints[slug][0].name
        };
      });

    return adm2
      ? parsedEndpoints.filter(e => !e.slug.includes('forma'))
      : parsedEndpoints;
  }
);

export const checkGeostoreSize = createSelector(
  [selectGeostoreSize, getDataLocation],
  (areaHa, location) => {
    if (!['aoi', 'geostore'].includes(location.type)) {
      return false;
    }

    return areaHa > 100000000;
  }
);

export const getAnalysisProps = createStructuredSelector({
  loading: getLoading,
  error: selectError,
  embed: selectEmbed,
  endpoints: getLayerEndpoints,
  location: getDataLocation,
  boundaries: getAllBoundaries,
  activeBoundary: getActiveBoundaryDatasets,
  widgetLayers: getWidgetLayers,
  analysisLocation: selectAnalysisLocation,
  activeArea: getActiveArea,
  search: selectSearch,
  areaTooLarge: checkGeostoreSize
});
<commit_msg>change to truthy evaluation of area size<commit_after>import { createSelector, createStructuredSelector } from 'reselect';
import compact from 'lodash/compact';
import isEmpty from 'lodash/isEmpty';
import groupBy from 'lodash/groupBy';
import flatMap from 'lodash/flatMap';

import { getAllLayers, getActiveDatasets } from 'components/map/selectors';
import { getActiveArea } from 'providers/areas-provider/selectors';
import { getDataLocation } from 'utils/location';
import { locationLevelToStr } from 'utils/format';
import { getWidgets } from 'components/widgets/selectors';

import { initialState } from './reducers';

const selectAnalysisUrlState = state =>
  state.location && state.location.query && state.location.query.analysis;
const selectAnalysisLoading = state => state.analysis && state.analysis.loading;
const selectDatasetsLoading = state => state.datasets && state.datasets.loading;
const selectGeostoreLoading = state => state.geostore && state.geostore.loading;
const selectGeodecriberLoading = state =>
  state.geodescriber && state.geodescriber.loading;
const selectSearch = state => state.location && state.location.search;
const selectAnalysisLocation = state =>
  state.analysis && state.analysis.location;
const selectEmbed = state =>
  state.location &&
  state.location.pathname &&
  state.location.pathname.includes('/embed');
const selectError = state => state.analysis && state.analysis.error;
const selectDatasets = state => state.datasets && state.datasets.data;
const selectGeostoreSize = state =>
  state.geostore && state.geostore.data && state.geostore.data.areaHa;

export const getLoading = createSelector(
  [
    selectAnalysisLoading,
    selectDatasetsLoading,
    selectGeostoreLoading,
    selectGeodecriberLoading
  ],
  (analysisLoading, datasetsLoading, geostoreLoading, geodescriberLoading) =>
    analysisLoading || datasetsLoading || geostoreLoading || geodescriberLoading
);

export const getAnalysisSettings = createSelector(
  [selectAnalysisUrlState],
  urlState => ({
    ...initialState.settings,
    ...urlState
  })
);

export const getShowDraw = createSelector(
  getAnalysisSettings,
  settings => settings.showDraw
);

export const getBoundaryDatasets = createSelector(
  [selectDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.isBoundary).map(d => ({
      name: d.name,
      dataset: d.id,
      layers: d.layers.map(l => l.id),
      id: d.id,
      label: d.name,
      value: d.layer
    }));
  }
);

export const getAllBoundaries = createSelector(
  [getBoundaryDatasets],
  boundaries =>
    [{ label: 'No boundaries', value: 'no-boundaries' }].concat(boundaries)
);

export const getActiveBoundaryDatasets = createSelector(
  [getBoundaryDatasets, getActiveDatasets],
  (datasets, activeDatasets) => {
    if (isEmpty(datasets) || isEmpty(activeDatasets)) return null;
    const datasetIds = activeDatasets.map(d => d.dataset);
    return datasets.find(d => datasetIds.includes(d.dataset));
  }
);

export const getWidgetLayers = createSelector(
  [getWidgets],
  widgets =>
    widgets &&
    flatMap(
      widgets.map(w =>
        flatMap(
          w.datasets &&
            w.datasets.map(
              d =>
                (Array.isArray(d.layers) ? d.layers : Object.values(d.layers))
            )
        )
      )
    )
);

export const getLayerEndpoints = createSelector(
  [getAllLayers, getDataLocation, getWidgetLayers],
  (layers, location, widgetLayers) => {
    if (!layers || !layers.length) return null;

    const { type, adm2 } = location;
    const routeType = type === 'country' ? 'admin' : type;
    const lossLayer = layers.find(l => l.metadata === 'tree_cover_loss');
    const hasWidgetLayers = widgetLayers && !!widgetLayers.length;

    const admLevel = locationLevelToStr(location);
    const endpoints = compact(
      layers
        .filter(
          l =>
            l.analysisConfig &&
            !l.analysisDisabled &&
            (!hasWidgetLayers || !widgetLayers.includes(l.id)) &&
            (!l.admLevels || l.admLevels.includes(admLevel))
        )
        .map(l => {
          const analysisConfig =
            l.analysisConfig.find(
              a =>
                a.type === routeType ||
                ((routeType === 'use' || routeType === 'wdpa') &&
                  a.type === 'geostore')
            ) || {};
          const { params, decodeParams } = l;
          return {
            name: l.name,
            version: analysisConfig.version || 'v1',
            slug: analysisConfig.service,
            params: {
              ...(analysisConfig.service === 'umd-loss-gain' &&
                lossLayer && {
                ...lossLayer.decodeParams
              }),
              ...decodeParams,
              ...params,
              query: analysisConfig.query
            }
          };
        })
    );

    const groupedEndpoints = groupBy(endpoints, 'slug');
    const parsedEndpoints = Object.keys(groupedEndpoints)
      .filter(slug => slug !== 'undefined')
      .map(slug => {
        let params = {};
        groupedEndpoints[slug].forEach(e => {
          params = {
            ...params,
            ...e.params
          };
        });

        return {
          slug,
          params,
          version: groupedEndpoints[slug][0].version,
          name: groupedEndpoints[slug][0].name
        };
      });

    return adm2
      ? parsedEndpoints.filter(e => !e.slug.includes('forma'))
      : parsedEndpoints;
  }
);

export const checkGeostoreSize = createSelector(
  [selectGeostoreSize, getDataLocation],
  (areaHa, location) => {
    if (['aoi', 'geostore'].includes(location.type)) {
      return areaHa > 100000000;
    }

    return false;
  }
);

export const getAnalysisProps = createStructuredSelector({
  loading: getLoading,
  error: selectError,
  embed: selectEmbed,
  endpoints: getLayerEndpoints,
  location: getDataLocation,
  boundaries: getAllBoundaries,
  activeBoundary: getActiveBoundaryDatasets,
  widgetLayers: getWidgetLayers,
  analysisLocation: selectAnalysisLocation,
  activeArea: getActiveArea,
  search: selectSearch,
  areaTooLarge: checkGeostoreSize
});
<|endoftext|>


<commit_before>import { createSelector, createStructuredSelector } from 'reselect';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import moment from 'moment';
import intersection from 'lodash/intersection';
import flatMap from 'lodash/flatMap';

import { getDayRange } from 'utils/dates';

import { parseWidgetsWithOptions } from 'components/widgets/selectors';
import { initialState } from './reducers';
import basemaps, { labels } from './basemaps-schema';

// map state
const selectMapUrlState = state =>
  state.location && state.location.query && state.location.query.map;
const selectMapLoading = state => state.map && state.map.loading;
const selectGeostoreLoading = state => state.geostore && state.geostore.loading;
const selectLatestLoading = state => state.latest && state.latest.loading;
const selectRecentImageryLoading = state =>
  state.recentImagery && state.recentImagery.loading;
const selectDatasetsLoading = state => state.datasets && state.datasets.loading;
const selectDrawLoading = state => state.draw && state.draw.loading;

// datasets
const selectDatasets = state => state.datasets && state.datasets.data;
const selectLatest = state => state.latest && state.latest.data;

// location
const selectGeostore = state => state.geostore && state.geostore.data;

// interactions
const selectSelectedInteractionId = state =>
  state.popup && state.popup.selected;
const selectInteractions = state => state.popup && state.popup.interactions;

// CONSTS
export const getBasemaps = () => basemaps;
export const getLabels = () => labels;

// SELECTORS
export const getMapSettings = createSelector([selectMapUrlState], urlState => ({
  ...initialState.settings,
  ...urlState
}));

export const getBasemapFromState = createSelector(
  getMapSettings,
  settings => settings.basemap
);

export const getBasemap = createSelector(
  [getBasemapFromState],
  basemapState => ({
    ...basemaps[basemapState.value],
    ...basemapState
  })
);

export const getLabelKey = createSelector(
  getMapSettings,
  settings => settings.label
);

export const getLabel = createSelector(
  [getLabelKey],
  labelsKey => labels[labelsKey] || labels[labelsKey.id]
);

export const getMapZoom = createSelector(
  getMapSettings,
  settings => settings.zoom
);

export const getMapLat = createSelector(
  getMapSettings,
  settings => settings.center.lat
);

export const getMapLng = createSelector(
  getMapSettings,
  settings => settings.center.lng
);

export const getDraw = createSelector(
  [getMapSettings],
  settings => settings.draw
);

export const getBbox = createSelector(
  getMapSettings,
  settings => settings.bbox
);

export const getCanBound = createSelector(
  getMapSettings,
  settings => settings.canBound
);

export const getMapOptions = createSelector([getMapSettings], settings => {
  if (!settings) return null;
  const { minZoom, maxZoom, attributionControl } = settings;
  return {
    minZoom,
    maxZoom,
    attributionControl
  };
});

export const getMapLoading = createSelector(
  [
    selectMapLoading,
    selectGeostoreLoading,
    selectLatestLoading,
    selectDatasetsLoading,
    selectDrawLoading,
    selectRecentImageryLoading
  ],
  (
    mapLoading,
    geostoreLoading,
    latestLoading,
    datasetsLoading,
    drawLoading,
    recentImageryLoading
  ) =>
    mapLoading ||
    geostoreLoading ||
    latestLoading ||
    datasetsLoading ||
    drawLoading ||
    recentImageryLoading
);

export const getLoadingMessage = createSelector(
  [selectRecentImageryLoading, selectLatestLoading],
  (recentLoading, latestLoading) => {
    if (recentLoading) return 'Fetching the most recent satellite image...';
    if (latestLoading) return 'Fetching latest data...';
    return '';
  }
);

// select datasets and dataset state
export const getActiveDatasetsFromState = createSelector(
  getMapSettings,
  settings => settings.datasets
);

export const getActiveDatasetIds = createSelector(
  [getActiveDatasetsFromState],
  activeDatasetsState => {
    if (!activeDatasetsState || !activeDatasetsState.length) return null;
    return activeDatasetsState.map(l => l.dataset);
  }
);

export const getActiveDatasets = createSelector(
  [selectDatasets, getActiveDatasetIds],
  (datasets, datasetIds) => {
    if (isEmpty(datasets) || isEmpty(datasetIds)) return null;
    return datasets.filter(d => datasetIds.includes(d.id));
  }
);

export const getBoundaryDatasets = createSelector(
  [selectDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.isBoundary).map(d => ({
      name: d.name,
      dataset: d.id,
      layers: d.layers.map(l => l.id),
      id: d.id,
      label: d.name,
      value: d.layer
    }));
  }
);

export const getAllBoundaries = createSelector(
  [getBoundaryDatasets],
  boundaries =>
    [{ label: 'No boundaries', value: 'no-boundaries' }].concat(boundaries)
);

export const getActiveBoundaryDatasets = createSelector(
  [getBoundaryDatasets, getActiveDatasets],
  (datasets, activeDatasets) => {
    if (isEmpty(datasets) || isEmpty(activeDatasets)) return null;
    const datasetIds = activeDatasets.map(d => d.dataset);
    return datasets.find(d => datasetIds.includes(d.dataset));
  }
);

// parse active datasets to add config from url
export const getDatasetsWithConfig = createSelector(
  [getActiveDatasets, getActiveDatasetsFromState, selectLatest],
  (datasets, activeDatasetsState, latestDates) => {
    if (isEmpty(datasets) || isEmpty(activeDatasetsState)) return null;

    return datasets.map(d => {
      const layerConfig =
        activeDatasetsState.find(l => l.dataset === d.id) || {};
      const {
        params,
        sqlParams,
        decodeParams,
        timelineParams,
        layers,
        visibility,
        opacity,
        bbox
      } =
        layerConfig || {};

      return {
        ...d,
        ...layerConfig,
        ...(d.selectorLayerConfig && {
          selectorLayerConfig: {
            ...d.selectorLayerConfig,
            selected: d.selectorLayerConfig.options.find(
              l => l.value === layers[0]
            )
          }
        }),
        layers: d.layers.map(l => {
          const {
            hasParamsTimeline,
            hasDecodeTimeline,
            timelineConfig,
            id
          } = l;
          const maxDate = latestDates[id];
          const { latestFormat } = l.params || {};
          const maxDateFormatted = latestFormat
            ? moment(maxDate).format(latestFormat)
            : maxDate;

          return {
            ...l,
            visibility,
            opacity,
            bbox,
            color: d.color,
            active: layers && layers.includes(l.id),
            ...(!isEmpty(l.params) && {
              params: {
                ...l.params,
                ...(maxDate && {
                  endDate: maxDate
                }),
                ...params,
                ...(maxDateFormatted && {
                  date: maxDateFormatted
                }),
                ...(hasParamsTimeline && {
                  ...timelineParams
                }),
                ...(maxDate && {
                  maxDate
                })
              }
            }),
            ...(!isEmpty(l.sqlParams) && {
              sqlParams: {
                ...l.sqlParams,
                ...sqlParams
              }
            }),
            ...(l.decodeFunction && {
              decodeParams: {
                ...l.decodeParams,
                ...(layers && {
                  confirmedOnly: layers.includes('confirmedOnly') ? 1 : 0
                }),
                ...(maxDate && {
                  endDate: maxDate
                }),
                ...decodeParams,
                ...(hasDecodeTimeline && {
                  ...timelineParams
                }),
                ...(maxDate && {
                  maxDate
                })
              }
            }),
            ...((l.hasParamsTimeline || l.hasDecodeTimeline) && {
              timelineConfig: {
                ...timelineConfig,
                ...(l.hasParamsTimeline && {
                  ...l.params
                }),
                ...(l.hasDecodeTimeline && {
                  ...l.decodeParams
                }),
                ...(maxDate && {
                  endDate: maxDate,
                  maxDate,
                  trimEndDate: maxDate
                }),
                ...timelineParams
              }
            })
          };
        })
      };
    });
  }
);

// map active datasets into correct order based on url state (drag and drop)
export const getLayerGroups = createSelector(
  [getDatasetsWithConfig, getActiveDatasetsFromState],
  (datasets, activeDatasetsState) => {
    if (isEmpty(datasets) || isEmpty(activeDatasetsState)) return null;

    return activeDatasetsState.map(layer => {
      const dataset = datasets.find(d => d.id === layer.dataset);
      const { metadata } =
        (dataset && dataset.layers.find(l => l.active)) || {};
      return {
        ...dataset,
        metadata: metadata || dataset.metadata
      };
    });
  }
);

// flatten datasets into layers for the layer manager
export const getAllLayers = createSelector(getLayerGroups, layerGroups => {
  if (isEmpty(layerGroups)) return null;

  return flatten(layerGroups.map(d => d.layers))
    .filter(l => l.active && (!l.isRecentImagery || l.params.url))
    .map((l, i) => {
      let zIndex =
        l.interactionConfig && l.interactionConfig.article
          ? 1100 + i
          : 1000 - i;
      if (l.isRecentImagery) zIndex = 500;
      if (l.isBoundary) zIndex = 1050 - i;
      return {
        ...l,
        zIndex
      };
    });
});

// all layers for importing by other components
export const getActiveLayers = createSelector(getAllLayers, layers => {
  if (isEmpty(layers)) return [];
  return layers.filter(l => !l.confirmedOnly);
});

export const getActiveLayersWithDates = createSelector(
  getActiveLayers,
  layers => {
    if (isEmpty(layers)) return [];
    return layers.map(l => {
      const { decodeFunction, decodeParams } = l;
      const { startDate, endDate } = decodeParams || {};

      return {
        ...l,
        ...(decodeFunction &&
          decodeParams && {
            decodeParams: {
              ...decodeParams,
              ...(startDate && {
                startYear: moment(startDate).year(),
                startMonth: moment(startDate).month(),
                startDay: moment(startDate).month()
              }),
              ...(endDate && {
                endYear: moment(endDate).year(),
                endMonth: moment(endDate).month(),
                endDay: moment(endDate).month()
              }),
              ...getDayRange(decodeParams)
            }
          })
      };
    });
  }
);

export const getInteractiveLayers = createSelector(getActiveLayers, layers => {
  if (isEmpty(layers)) return [];
  const interactiveLayers = layers.filter(
    l =>
      !isEmpty(l.interactionConfig) &&
      (l.layerConfig.layers || l.layerConfig.body.vectorLayers)
  );

  return flatMap(
    interactiveLayers.reduce((arr, layer) => {
      const clickableLayers =
        (layer.layerConfig.layers && layer.layerConfig.layers) ||
        layer.layerConfig.body.vectorLayers;

      return [
        ...arr,
        clickableLayers.map((l, i) => `${layer.id}-${l.type}-${i}`)
      ];
    }, [])
  );
});

// get widgets related to map layers and use them to build the layers
export const getWidgetsWithLayerParams = createSelector(
  [parseWidgetsWithOptions, getActiveLayersWithDates],
  (widgets, layers) => {
    if (!widgets || !widgets.length || !layers || !layers.length) return null;
    const layerIds = layers && layers.map(l => l.id);
    const filteredWidgets = widgets.filter(w => {
      const layerIntersection = intersection(w.config.layers, layerIds);
      return w.config.analysis && layerIntersection && layerIntersection.length;
    });
    return filteredWidgets.map(w => {
      const widgetLayer =
        layers && layers.find(l => w.config && w.config.layers.includes(l.id));
      const { params, decodeParams } = widgetLayer || {};
      const startDate =
        (params && params.startDate) ||
        (decodeParams && decodeParams.startDate);
      const startYear =
        startDate && parseInt(moment(startDate).format('YYYY'), 10);
      const endDate =
        (params && params.endDate) || (decodeParams && decodeParams.endDate);
      const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

      return {
        ...w,
        settings: {
          ...w.settings,
          ...params,
          ...decodeParams,
          ...(startYear && {
            startYear
          }),
          ...(endYear && {
            endYear
          })
        }
      };
    });
  }
);

// flatten datasets into layers for the layer manager
export const getActiveLayersWithWidgetSettings = createSelector(
  [getActiveLayersWithDates, getWidgetsWithLayerParams],
  (layers, widgets) => {
    if (isEmpty(layers)) return [];
    if (isEmpty(widgets)) return layers;
    return layers.map(l => {
      const layerWidgetState =
        widgets &&
        Object.values(widgets).find(w => w.layers && w.layers.includes(l.id));
      const { updateLayer } = layerWidgetState || {};
      return {
        ...l,
        ...(l.decodeParams &&
          updateLayer && {
            decodeParams: {
              ...l.decodeParams,
              ...layerWidgetState
            }
          }),
        ...(l.params &&
          updateLayer && {
            params: {
              ...l.params,
              ...layerWidgetState
            }
          })
      };
    });
  }
);

export const getLayerBbox = createSelector([getActiveLayers], layers => {
  const layerWithBbox =
    layers && layers.find(l => l.bbox || (l.layerConfig && l.layerConfig.bbox));
  const layerBbox =
    layerWithBbox &&
    (layerWithBbox.bbox ||
      (layerWithBbox.layerConfig && layerWithBbox.layerConfig.bbox));
  return layerBbox;
});

export const getGeostoreBbox = createSelector(
  [selectGeostore],
  geostore => geostore && geostore.bbox
);

export const filterInteractions = createSelector(
  [selectInteractions, getActiveLayers],
  (interactions, activeLayers) => {
    if (isEmpty(interactions)) return null;
    return Object.keys(interactions).map(i => {
      const layer = activeLayers.find(l => l.id === i);
      return {
        data: interactions[i].data,
        geometry: interactions[i].geometry,
        layer,
        label: layer && layer.name,
        value: layer && layer.id,
        article:
          layer && layer.interactionConfig && layer.interactionConfig.article
      };
    });
  }
);

export const getSelectedInteraction = createSelector(
  [filterInteractions, selectSelectedInteractionId, getActiveLayers],
  (options, selected, layers) => {
    if (isEmpty(options)) return null;
    const layersWithoutBoundaries = layers.filter(
      l => !l.isBoundary && !isEmpty(l.interactionConfig)
    );
    // if there is an article (icon layer) then choose that
    let selectedData = options.find(o => o.data.cluster);
    selectedData = options.find(o => o.article);
    // if there is nothing selected get the top layer
    if (!selected && !!layersWithoutBoundaries.length) {
      selectedData = options.find(
        o => o.layer && o.layer.id === layersWithoutBoundaries[0].id
      );
    }
    // if only one layer then get that
    if (!selectedData && options.length === 1) selectedData = options[0];
    // otherwise get based on selected
    if (!selectedData) {
      selectedData = options.find(o => o.layer && o.layer.id === selected);
    }

    return selectedData;
  }
);

export const getMapProps = createStructuredSelector({
  loading: getMapLoading,
  loadingMessage: getLoadingMessage,
  mapOptions: getMapOptions,
  basemap: getBasemap,
  label: getLabel,
  layerBbox: getLayerBbox,
  geostoreBbox: getGeostoreBbox,
  bbox: getBbox,
  canBound: getCanBound,
  draw: getDraw,
  lat: getMapLat,
  lng: getMapLng,
  zoom: getMapZoom,
  selectedInteraction: getSelectedInteraction,
  interactiveLayers: getInteractiveLayers
});
<commit_msg>sort layers before adding to map<commit_after>import { createSelector, createStructuredSelector } from 'reselect';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import moment from 'moment';
import intersection from 'lodash/intersection';
import flatMap from 'lodash/flatMap';
import sortBy from 'lodash/sortBy';

import { getDayRange } from 'utils/dates';

import { parseWidgetsWithOptions } from 'components/widgets/selectors';
import { initialState } from './reducers';
import basemaps, { labels } from './basemaps-schema';

// map state
const selectMapUrlState = state =>
  state.location && state.location.query && state.location.query.map;
const selectMapLoading = state => state.map && state.map.loading;
const selectGeostoreLoading = state => state.geostore && state.geostore.loading;
const selectLatestLoading = state => state.latest && state.latest.loading;
const selectRecentImageryLoading = state =>
  state.recentImagery && state.recentImagery.loading;
const selectDatasetsLoading = state => state.datasets && state.datasets.loading;
const selectDrawLoading = state => state.draw && state.draw.loading;

// datasets
const selectDatasets = state => state.datasets && state.datasets.data;
const selectLatest = state => state.latest && state.latest.data;

// location
const selectGeostore = state => state.geostore && state.geostore.data;

// interactions
const selectSelectedInteractionId = state =>
  state.popup && state.popup.selected;
const selectInteractions = state => state.popup && state.popup.interactions;

// CONSTS
export const getBasemaps = () => basemaps;
export const getLabels = () => labels;

// SELECTORS
export const getMapSettings = createSelector([selectMapUrlState], urlState => ({
  ...initialState.settings,
  ...urlState
}));

export const getBasemapFromState = createSelector(
  getMapSettings,
  settings => settings.basemap
);

export const getBasemap = createSelector(
  [getBasemapFromState],
  basemapState => ({
    ...basemaps[basemapState.value],
    ...basemapState
  })
);

export const getLabelKey = createSelector(
  getMapSettings,
  settings => settings.label
);

export const getLabel = createSelector(
  [getLabelKey],
  labelsKey => labels[labelsKey] || labels[labelsKey.id]
);

export const getMapZoom = createSelector(
  getMapSettings,
  settings => settings.zoom
);

export const getMapLat = createSelector(
  getMapSettings,
  settings => settings.center.lat
);

export const getMapLng = createSelector(
  getMapSettings,
  settings => settings.center.lng
);

export const getDraw = createSelector(
  [getMapSettings],
  settings => settings.draw
);

export const getBbox = createSelector(
  getMapSettings,
  settings => settings.bbox
);

export const getCanBound = createSelector(
  getMapSettings,
  settings => settings.canBound
);

export const getMapOptions = createSelector([getMapSettings], settings => {
  if (!settings) return null;
  const { minZoom, maxZoom, attributionControl } = settings;
  return {
    minZoom,
    maxZoom,
    attributionControl
  };
});

export const getMapLoading = createSelector(
  [
    selectMapLoading,
    selectGeostoreLoading,
    selectLatestLoading,
    selectDatasetsLoading,
    selectDrawLoading,
    selectRecentImageryLoading
  ],
  (
    mapLoading,
    geostoreLoading,
    latestLoading,
    datasetsLoading,
    drawLoading,
    recentImageryLoading
  ) =>
    mapLoading ||
    geostoreLoading ||
    latestLoading ||
    datasetsLoading ||
    drawLoading ||
    recentImageryLoading
);

export const getLoadingMessage = createSelector(
  [selectRecentImageryLoading, selectLatestLoading],
  (recentLoading, latestLoading) => {
    if (recentLoading) return 'Fetching the most recent satellite image...';
    if (latestLoading) return 'Fetching latest data...';
    return '';
  }
);

// select datasets and dataset state
export const getActiveDatasetsFromState = createSelector(
  getMapSettings,
  settings => settings.datasets
);

export const getActiveDatasetIds = createSelector(
  [getActiveDatasetsFromState],
  activeDatasetsState => {
    if (!activeDatasetsState || !activeDatasetsState.length) return null;
    return activeDatasetsState.map(l => l.dataset);
  }
);

export const getActiveDatasets = createSelector(
  [selectDatasets, getActiveDatasetIds],
  (datasets, datasetIds) => {
    if (isEmpty(datasets) || isEmpty(datasetIds)) return null;
    return datasets.filter(d => datasetIds.includes(d.id));
  }
);

export const getBoundaryDatasets = createSelector(
  [selectDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.isBoundary).map(d => ({
      name: d.name,
      dataset: d.id,
      layers: d.layers.map(l => l.id),
      id: d.id,
      label: d.name,
      value: d.layer
    }));
  }
);

export const getAllBoundaries = createSelector(
  [getBoundaryDatasets],
  boundaries =>
    [{ label: 'No boundaries', value: 'no-boundaries' }].concat(boundaries)
);

export const getActiveBoundaryDatasets = createSelector(
  [getBoundaryDatasets, getActiveDatasets],
  (datasets, activeDatasets) => {
    if (isEmpty(datasets) || isEmpty(activeDatasets)) return null;
    const datasetIds = activeDatasets.map(d => d.dataset);
    return datasets.find(d => datasetIds.includes(d.dataset));
  }
);

// parse active datasets to add config from url
export const getDatasetsWithConfig = createSelector(
  [getActiveDatasets, getActiveDatasetsFromState, selectLatest],
  (datasets, activeDatasetsState, latestDates) => {
    if (isEmpty(datasets) || isEmpty(activeDatasetsState)) return null;

    return datasets.map(d => {
      const layerConfig =
        activeDatasetsState.find(l => l.dataset === d.id) || {};
      const {
        params,
        sqlParams,
        decodeParams,
        timelineParams,
        layers,
        visibility,
        opacity,
        bbox
      } =
        layerConfig || {};

      return {
        ...d,
        ...layerConfig,
        ...(d.selectorLayerConfig && {
          selectorLayerConfig: {
            ...d.selectorLayerConfig,
            selected: d.selectorLayerConfig.options.find(
              l => l.value === layers[0]
            )
          }
        }),
        layers: d.layers.map(l => {
          const {
            hasParamsTimeline,
            hasDecodeTimeline,
            timelineConfig,
            id
          } = l;
          const maxDate = latestDates[id];
          const { latestFormat } = l.params || {};
          const maxDateFormatted = latestFormat
            ? moment(maxDate).format(latestFormat)
            : maxDate;

          return {
            ...l,
            visibility,
            opacity,
            bbox,
            color: d.color,
            active: layers && layers.includes(l.id),
            ...(!isEmpty(l.params) && {
              params: {
                ...l.params,
                ...(maxDate && {
                  endDate: maxDate
                }),
                ...params,
                ...(maxDateFormatted && {
                  date: maxDateFormatted
                }),
                ...(hasParamsTimeline && {
                  ...timelineParams
                }),
                ...(maxDate && {
                  maxDate
                })
              }
            }),
            ...(!isEmpty(l.sqlParams) && {
              sqlParams: {
                ...l.sqlParams,
                ...sqlParams
              }
            }),
            ...(l.decodeFunction && {
              decodeParams: {
                ...l.decodeParams,
                ...(layers && {
                  confirmedOnly: layers.includes('confirmedOnly') ? 1 : 0
                }),
                ...(maxDate && {
                  endDate: maxDate
                }),
                ...decodeParams,
                ...(hasDecodeTimeline && {
                  ...timelineParams
                }),
                ...(maxDate && {
                  maxDate
                })
              }
            }),
            ...((l.hasParamsTimeline || l.hasDecodeTimeline) && {
              timelineConfig: {
                ...timelineConfig,
                ...(l.hasParamsTimeline && {
                  ...l.params
                }),
                ...(l.hasDecodeTimeline && {
                  ...l.decodeParams
                }),
                ...(maxDate && {
                  endDate: maxDate,
                  maxDate,
                  trimEndDate: maxDate
                }),
                ...timelineParams
              }
            })
          };
        })
      };
    });
  }
);

// map active datasets into correct order based on url state (drag and drop)
export const getLayerGroups = createSelector(
  [getDatasetsWithConfig, getActiveDatasetsFromState],
  (datasets, activeDatasetsState) => {
    if (isEmpty(datasets) || isEmpty(activeDatasetsState)) return null;

    return activeDatasetsState.map(layer => {
      const dataset = datasets.find(d => d.id === layer.dataset);
      const { metadata } =
        (dataset && dataset.layers.find(l => l.active)) || {};
      return {
        ...dataset,
        metadata: metadata || dataset.metadata
      };
    });
  }
);

// flatten datasets into layers for the layer manager
export const getAllLayers = createSelector(getLayerGroups, layerGroups => {
  if (isEmpty(layerGroups)) return null;

  return sortBy(
    flatten(layerGroups.map(d => d.layers))
      .filter(l => l.active && (!l.isRecentImagery || l.params.url))
      .map((l, i) => {
        let zIndex =
          l.interactionConfig && l.interactionConfig.article
            ? 1100 + i
            : 1000 - i;
        if (l.isRecentImagery) zIndex = 500;
        if (l.isBoundary) zIndex = 1050 - i;
        return {
          ...l,
          zIndex
        };
      }),
    'zIndex'
  );
});

// all layers for importing by other components
export const getActiveLayers = createSelector(getAllLayers, layers => {
  if (isEmpty(layers)) return [];
  return layers.filter(l => !l.confirmedOnly);
});

export const getActiveLayersWithDates = createSelector(
  getActiveLayers,
  layers => {
    if (isEmpty(layers)) return [];
    return layers.map(l => {
      const { decodeFunction, decodeParams } = l;
      const { startDate, endDate } = decodeParams || {};

      return {
        ...l,
        ...(decodeFunction &&
          decodeParams && {
            decodeParams: {
              ...decodeParams,
              ...(startDate && {
                startYear: moment(startDate).year(),
                startMonth: moment(startDate).month(),
                startDay: moment(startDate).month()
              }),
              ...(endDate && {
                endYear: moment(endDate).year(),
                endMonth: moment(endDate).month(),
                endDay: moment(endDate).month()
              }),
              ...getDayRange(decodeParams)
            }
          })
      };
    });
  }
);

export const getInteractiveLayers = createSelector(getActiveLayers, layers => {
  if (isEmpty(layers)) return [];
  const interactiveLayers = layers.filter(
    l =>
      !isEmpty(l.interactionConfig) &&
      (l.layerConfig.layers || l.layerConfig.body.vectorLayers)
  );

  return flatMap(
    interactiveLayers.reduce((arr, layer) => {
      const clickableLayers =
        (layer.layerConfig.layers && layer.layerConfig.layers) ||
        layer.layerConfig.body.vectorLayers;

      return [
        ...arr,
        clickableLayers.map((l, i) => `${layer.id}-${l.type}-${i}`)
      ];
    }, [])
  );
});

// get widgets related to map layers and use them to build the layers
export const getWidgetsWithLayerParams = createSelector(
  [parseWidgetsWithOptions, getActiveLayersWithDates],
  (widgets, layers) => {
    if (!widgets || !widgets.length || !layers || !layers.length) return null;
    const layerIds = layers && layers.map(l => l.id);
    const filteredWidgets = widgets.filter(w => {
      const layerIntersection = intersection(w.config.layers, layerIds);
      return w.config.analysis && layerIntersection && layerIntersection.length;
    });
    return filteredWidgets.map(w => {
      const widgetLayer =
        layers && layers.find(l => w.config && w.config.layers.includes(l.id));
      const { params, decodeParams } = widgetLayer || {};
      const startDate =
        (params && params.startDate) ||
        (decodeParams && decodeParams.startDate);
      const startYear =
        startDate && parseInt(moment(startDate).format('YYYY'), 10);
      const endDate =
        (params && params.endDate) || (decodeParams && decodeParams.endDate);
      const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10);

      return {
        ...w,
        settings: {
          ...w.settings,
          ...params,
          ...decodeParams,
          ...(startYear && {
            startYear
          }),
          ...(endYear && {
            endYear
          })
        }
      };
    });
  }
);

// flatten datasets into layers for the layer manager
export const getActiveLayersWithWidgetSettings = createSelector(
  [getActiveLayersWithDates, getWidgetsWithLayerParams],
  (layers, widgets) => {
    if (isEmpty(layers)) return [];
    if (isEmpty(widgets)) return layers;
    return layers.map(l => {
      const layerWidgetState =
        widgets &&
        Object.values(widgets).find(w => w.layers && w.layers.includes(l.id));
      const { updateLayer } = layerWidgetState || {};
      return {
        ...l,
        ...(l.decodeParams &&
          updateLayer && {
            decodeParams: {
              ...l.decodeParams,
              ...layerWidgetState
            }
          }),
        ...(l.params &&
          updateLayer && {
            params: {
              ...l.params,
              ...layerWidgetState
            }
          })
      };
    });
  }
);

export const getLayerBbox = createSelector([getActiveLayers], layers => {
  const layerWithBbox =
    layers && layers.find(l => l.bbox || (l.layerConfig && l.layerConfig.bbox));
  const layerBbox =
    layerWithBbox &&
    (layerWithBbox.bbox ||
      (layerWithBbox.layerConfig && layerWithBbox.layerConfig.bbox));
  return layerBbox;
});

export const getGeostoreBbox = createSelector(
  [selectGeostore],
  geostore => geostore && geostore.bbox
);

export const filterInteractions = createSelector(
  [selectInteractions, getActiveLayers],
  (interactions, activeLayers) => {
    if (isEmpty(interactions)) return null;
    return Object.keys(interactions).map(i => {
      const layer = activeLayers.find(l => l.id === i);
      return {
        data: interactions[i].data,
        geometry: interactions[i].geometry,
        layer,
        label: layer && layer.name,
        value: layer && layer.id,
        article:
          layer && layer.interactionConfig && layer.interactionConfig.article
      };
    });
  }
);

export const getSelectedInteraction = createSelector(
  [filterInteractions, selectSelectedInteractionId, getActiveLayers],
  (options, selected, layers) => {
    if (isEmpty(options)) return null;
    const layersWithoutBoundaries = layers.filter(
      l => !l.isBoundary && !isEmpty(l.interactionConfig)
    );
    // if there is an article (icon layer) then choose that
    let selectedData = options.find(o => o.data.cluster);
    selectedData = options.find(o => o.article);
    // if there is nothing selected get the top layer
    if (!selected && !!layersWithoutBoundaries.length) {
      selectedData = options.find(
        o => o.layer && o.layer.id === layersWithoutBoundaries[0].id
      );
    }
    // if only one layer then get that
    if (!selectedData && options.length === 1) selectedData = options[0];
    // otherwise get based on selected
    if (!selectedData) {
      selectedData = options.find(o => o.layer && o.layer.id === selected);
    }

    return selectedData;
  }
);

export const getMapProps = createStructuredSelector({
  loading: getMapLoading,
  loadingMessage: getLoadingMessage,
  mapOptions: getMapOptions,
  basemap: getBasemap,
  label: getLabel,
  layerBbox: getLayerBbox,
  geostoreBbox: getGeostoreBbox,
  bbox: getBbox,
  canBound: getCanBound,
  draw: getDraw,
  lat: getMapLat,
  lng: getMapLng,
  zoom: getMapZoom,
  selectedInteraction: getSelectedInteraction,
  interactiveLayers: getInteractiveLayers
});
<|endoftext|>