<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The "add a folder" dialog
 */
var FilterModal = Modal.extend({

    type: "filter",
    closeText: "Save",

    events: {
        //'submit form': 'save',
        'click .dialog_footer a' : 'call',
        'click .insert-member' : 'open_select_member_selector'
    },

    buttons: [
        { text: "OK", method: "save" },
        { text: "Cancel", method: "close" },
        { text: "Help", method: "help"}
    ],

    message: "",

    expression_text: function() {
        var c = "

";
        if (this.expressionType == "Order") {
            c += "
Sort Type <select class='form-control' id='fun'><option>ASC</option><option>BASC</option><option>DESC</option><option>BDESC</option> </select>
";
        }
        c += "
" + this.expressionType + " MDX Expression:
" +
             "

" +
             "
" +
            "Insert Member
";
        return c;
    },

    expression: " ",
    expressonType: "",

    initialize: function(args) {
        var self = this;
        this.id = _.uniqueId('filter-modal-');
        this.workspace = args.workspace;
        this.axis = args.axis;
        this.query = args.query;
        this.success = args.success;
        this.expression = args.expression;
        this.expressionType = args.expressionType;
        _.bindAll(this, "save", "expression_text");

        _.extend(this.options, {
            title: "Custom " + this.expressionType + " for " + this.axis
        });

        this.message = this.expression_text(this.expressionType);

        this.bind( 'open', function( ) {
            $(this.el).find('.sidebar').width(380);

            this.editor = ace.edit(this.id);
            this.editor.setValue(self.expression);
            this.editor.setShowPrintMargin(false);
            this.editor.setFontSize(11);
        });

        // fix event listening in IE < 9
        if(isIE && isIE < 9) {
            $(this.el).find('form').on('submit', this.save);
        }

    },

    save: function( event ) {
        event.preventDefault( );
        var self = this;
        this.expression = $(this.el).find('textarea').val();

        var alert_msg = "";
        if (typeof this.expression == "undefined" || !this.expression || this.expression === "") {
            alert_msg += "You have to enter a MDX expression for the " + this.expressionType + " function! ";
            alert(alert_msg);
        } else {
            if (self.expressionType == "Order") {
                var sortOrder = $('#fun').val();
                self.success(sortOrder, this.expression);
            } else {
                self.success(this.expression);
            }
            this.close();
        }

        return false;
    },

    error: function() {
        $(this.el).find('dialog_body')
            .html("Could not add new folder");
    },

    /**
     * Open the select member dialog
     * @param event
     */
    open_select_member_selector: function(event){
        event.preventDefault();
        var dimension = {
            val: this.$el.find('#cms-dimension option:selected').val(),
            txt: this.$el.find('#cms-dimension option:selected').text(),
            dataDimension: this.$el.find('#cms-dimension option:selected').data('dimension'),
            dataType: this.$el.find('#cms-dimension option:selected').data('type')
        };
        var editor = ace.edit(this.id);
        var that = this;

         (new ParentMemberSelectorModal({
                dialog: this,
                workspace: this.workspace,
                cube: this.workspace.selected_cube,
                dimensions: Saiku.session.sessionworkspace.cube[this.workspace.selected_cube].get('data').dimensions,
                selectDimension: dimension.val,
                dimension: dimension.dataDimension,
                hierarchy: dimension.txt,
                uniqueName: this.pmUniqueName,
                lastLevel: this.pmLevel,
                breadcrumbs: this.pmBreadcrumbs,
                select_type: "select_member",
                selected_member: this.selected_member,
                close_callback: function(args){
                    var e = editor;
                    that.close_select_modal(e, args);
                }
            })).render().open();

            this.$el.parents('.ui-dialog').find('.ui-dialog-title').text('Custom Filter');

    },

    /**
     * Callback to update the editor with the selected member.
     * @param editor
     * @param n
     */
    close_select_modal: function(editor, n){
        editor.insert(n);
    },

    help: function(){
        window.open("http://wiki.meteorite.bi/display/SAIK/Filtering");
    }

});
<commit_msg>fix filter modal<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The "add a folder" dialog
 */
var FilterModal = Modal.extend({

    type: "filter",
    closeText: "Save",

    events: {
        //'submit form': 'save',
        'click .dialog_footer a' : 'call',
        'click .insert-member' : 'open_select_member_selector'
    },

    buttons: [
        { text: "OK", method: "save" },
        { text: "Cancel", method: "close" },
        { text: "Help", method: "help"}
    ],

    message: "",

    expression_text: function() {
        var c = "

";
        if (this.expressionType == "Order") {
            c += "
Sort Type <select class='form-control' id='fun'><option>ASC</option><option>BASC</option><option>DESC</option><option>BDESC</option> </select>
";
        }
        c += "
" + this.expressionType + " MDX Expression:
" +
             "

" +
             "
" +
            "Insert Member
";
        return c;
    },

    expression: " ",
    expressonType: "",

    initialize: function(args) {
        var self = this;
        this.id = _.uniqueId('filter-modal-');
        this.workspace = args.workspace;
        this.axis = args.axis;
        this.query = args.query;
        this.success = args.success;
        this.expression = args.expression;
        this.expressionType = args.expressionType;
        _.bindAll(this, "save", "expression_text");

        _.extend(this.options, {
            title: "Custom " + this.expressionType + " for " + this.axis
        });

        this.message = this.expression_text(this.expressionType);

        this.bind( 'open', function( ) {
            $(this.el).find('.sidebar').width(380);

            this.editor = ace.edit(this.id);
            this.editor.setValue(self.expression);
            this.editor.setShowPrintMargin(false);
            this.editor.setFontSize(11);
        });

        // fix event listening in IE < 9
        if(isIE && isIE < 9) {
            $(this.el).find('form').on('submit', this.save);
        }

    },

    save: function( event ) {
        event.preventDefault( );
        var self = this;
        this.expression = this.editor.getValue();

        var alert_msg = "";
        if (typeof this.expression == "undefined" || !this.expression || this.expression === "") {
            alert_msg += "You have to enter a MDX expression for the " + this.expressionType + " function! ";
            alert(alert_msg);
        } else {
            if (self.expressionType == "Order") {
                var sortOrder = $('#fun').val();
                self.success(sortOrder, this.expression);
            } else {
                self.success(this.expression);
            }
            this.close();
        }

        return false;
    },

    error: function() {
        $(this.el).find('dialog_body')
            .html("Could not add new folder");
    },

    /**
     * Open the select member dialog
     * @param event
     */
    open_select_member_selector: function(event){
        event.preventDefault();
        var dimension = {
            val: this.$el.find('#cms-dimension option:selected').val(),
            txt: this.$el.find('#cms-dimension option:selected').text(),
            dataDimension: this.$el.find('#cms-dimension option:selected').data('dimension'),
            dataType: this.$el.find('#cms-dimension option:selected').data('type')
        };
        var editor = ace.edit(this.id);
        var that = this;

         (new ParentMemberSelectorModal({
                dialog: this,
                workspace: this.workspace,
                cube: this.workspace.selected_cube,
                dimensions: Saiku.session.sessionworkspace.cube[this.workspace.selected_cube].get('data').dimensions,
                selectDimension: dimension.val,
                dimension: dimension.dataDimension,
                hierarchy: dimension.txt,
                uniqueName: this.pmUniqueName,
                lastLevel: this.pmLevel,
                breadcrumbs: this.pmBreadcrumbs,
                select_type: "select_member",
                selected_member: this.selected_member,
                close_callback: function(args){
                    var e = editor;
                    that.close_select_modal(e, args);
                }
            })).render().open();

            this.$el.parents('.ui-dialog').find('.ui-dialog-title').text('Custom Filter');

    },

    /**
     * Callback to update the editor with the selected member.
     * @param editor
     * @param n
     */
    close_select_modal: function(editor, n){
        editor.insert(n);
    },

    help: function(){
        window.open("http://wiki.meteorite.bi/display/SAIK/Filtering");
    }

});
<|endoftext|>


<commit_before>import GeoJSON from 'geojson';
import { Record } from '../../models/record.model';
import Specie from '../../models/specie.model';

function constructQuery(req) {
  const bmClass = {
    $or: [
      {
        bmClass: {
          $in: req.query.bmClass
            ? req.query.bmClass.constructor === Array
              ? req.query.bmClass
              : [req.query.bmClass]
            : undefined
        }
      }
    ]
  };
  const endemic = { $or: [{ endemic: { $in: [true] } }] };
  const invasive = { $or: [{ invasive: { $in: [true] } }] };
  const endangered = {
    $or: [
      {
        iucn: {
          $in: ['EN', 'VU', 'CR']
        }
      }
    ]
  };
  const query = {};
  if (
    req.params.species ||
    req.query.bmClass ||
    req.query.endangered ||
    req.query.endemic ||
    req.query.invasive
  ) {
    query.$and = [];
    if (req.query.bmClass) query.$and.push(bmClass);
    if (req.query.endangered) query.$and.push(endangered);
    if (req.query.endemic) query.$and.push(endemic);
    if (req.query.invasive) query.$and.push(invasive);
  }
  return query;
}

/**
 * @swagger
 * /species/records/{taxID}:
 *   get:
 *     description: "All the records of an acceptedNameUsage (using internal taxID as key): query (use = true & visualizationPrivileges = 0)"
 *     operationId: REC1
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function read(req, res) {
  if (req.params.taxID) {
    try {
      const docs = await Record.aggregate([
        {
          $match: {
            taxID: +req.params.taxID,
            use: true,
            visualizationPrivileges: 0,
            spatialDuplicated: false
          }
        },
        {
          $project: {
            reported: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$reported' }, { $ne: ['$reported', []] }]
                },
                then: { $isArray: '$reported' },
                else: false
              }
            },
            updated: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$updated' }, { $ne: ['$updated', []] }]
                },
                then: { $isArray: '$updated' },
                else: false
              }
            },
            _id: 1,
            taxID: 1,
            acceptedNameUsage: 1,
            species: 1,
            speciesOriginal: 1,
            locality: 1,
            lat: 1,
            lon: 1,
            alt: 1,
            basisOfRecord: 1,
            catalogNumber: 1,
            collector: 1,
            institution: 1,
            url: 1,
            dd: 1,
            mm: 1,
            yyyy: 1,
            suggestedStateProvince: 1,
            suggestedCounty: 1,
            environmentalOutlier: 1,
            source: 1
          }
        }
      ]);
      res.send(GeoJSON.parse(docs, { Point: ['lat', 'lon'] }));
    } catch (err) {
      res.send(err);
    }
  }
}

export async function readValidForGroup(req, res) {
  if (req.params.taxID) {
    try {
      const docs = await Record.aggregate([
        {
          $match: {
            taxID: +req.params.taxID,
            use: true,
            visualizationPrivileges: 1,
            spatialDuplicated: false
          }
        },
        {
          $project: {
            reported: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$reported' }, { $ne: ['$reported', []] }]
                },
                then: { $isArray: '$reported' },
                else: false
              }
            },
            updated: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$updated' }, { $ne: ['$updated', []] }]
                },
                then: { $isArray: '$updated' },
                else: false
              }
            },
            _id: 1,
            taxID: 1,
            acceptedNameUsage: 1,
            species: 1,
            speciesOriginal: 1,
            locality: 1,
            lat: 1,
            lon: 1,
            alt: 1,
            basisOfRecord: 1,
            catalogNumber: 1,
            collector: 1,
            institution: 1,
            url: 1,
            dd: 1,
            mm: 1,
            yyyy: 1,
            suggestedStateProvince: 1,
            suggestedCounty: 1,
            environmentalOutlier: 1,
            source: 1
          }
        }
      ]);
      res.send(GeoJSON.parse(docs, { Point: ['lat', 'lon'] }));
    } catch (err) {
      res.send(err);
    }
  }
}

/**
 * @swagger
 * /species:
 *   get:
 *     description: "Get all the species in BioModelos"
 *     operationId: SPE1
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function getAllSpecies(req, res) {
  const query = constructQuery(req);
  if (req.query.modelStatus) {
    try {
      const docs = await Specie.aggregate([
        {
          $match: query
        },
        {
          $lookup: {
            localField: 'taxID',
            from: 'models',
            foreignField: 'taxID',
            as: 'models'
          }
        },
        {
          $match: { 'models.modelStatus': req.query.modelStatus }
        },
        {
          $project: {
            species: 1,
            taxID: 1,
            _id: 0
          }
        }
      ]).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  } else {
    try {
      const docs = await Specie.find(query, {
        species: 1,
        taxID: 1,
        _id: 0
      }).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  }
}

/**
 * @swagger
 * /species/{taxID}:
 *   get:
 *     description: "Get superior taxonomy and the total of records of a species"
 *     operationId: SPE2
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function getTaxonomyAndRecords(req, res) {
  if (req.params.taxID) {
    try {
      const doc = await Specie.find(
        { taxID: +req.params.taxID },
        {
          _id: 0,
          acceptedNameUsage: 1,
          species: 1,
          kingdom: 1,
          phylum: 1,
          class: 1,
          order: 1,
          family: 1
        }
      );
      try {
        const doc2 = await Record.aggregate([
          {
            $match: {
              taxID: +req.params.taxID,
              use: true,
              spatialDuplicated: false
            }
          },
          {
            $group: {
              _id: {
                characteristics: { taxID: '$taxID' }
              },
              totalRecords: { $sum: 1 }
            }
          },
          {
            $project: {
              _id: 0,
              taxID: '$_id.characteristics.taxID',
              totalRecords: '$totalRecords'
            }
          }
        ]);
        if (doc2[0] && doc2[0].totalRecords) {
          doc[0]['_doc']['totalRecords'] = doc2[0].totalRecords;
        } else {
          doc[0]['_doc']['totalRecords'] = 0;
        }
        res.json(doc);
      } catch (err) {
        res.json(err);
      }
    } catch (err) {
      res.json(err);
    }
  }
}

/**
 * @swagger
 * /species/search/{species}:
 *   get:
 *     description: "Get general query to obtain species with some specific characteristics"
 *     operationId: SPE3
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function searchSpecie(req, res) {
  const query = constructQuery(req);
  if (req.params.species) {
    const regEx = new RegExp(req.params.species, 'ig');
    query.$and.push({ species: { $regex: regEx } });
  }
  if (req.query.modelStatus) {
    try {
      const docs = await Specie.aggregate([
        {
          $match: query
        },
        {
          $lookup: {
            localField: 'taxID',
            from: 'models',
            foreignField: 'taxID',
            as: 'models'
          }
        },
        {
          $match: { 'models.modelStatus': req.query.modelStatus }
        },
        {
          $project: {
            _id: 0,
            species: 1,
            taxID: 1,
            taxonomicStatus: 1,
            iucn: 1,
            bmClass: 1,
            endemic: 1,
            invasive: 1
          }
        }
      ]).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  } else {
    try {
      const docs = await Specie.find(query, {
        _id: 0,
        species: 1,
        taxID: 1,
        taxonomicStatus: 1,
        iucn: 1,
        bmClass: 1,
        endemic: 1,
        invasive: 1
      }).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  }
}
<commit_msg>Query for visualization open and closed for an specie<commit_after>import GeoJSON from 'geojson';
import { Record } from '../../models/record.model';
import Specie from '../../models/specie.model';

function constructQuery(req) {
  const bmClass = {
    $or: [
      {
        bmClass: {
          $in: req.query.bmClass
            ? req.query.bmClass.constructor === Array
              ? req.query.bmClass
              : [req.query.bmClass]
            : undefined
        }
      }
    ]
  };
  const endemic = { $or: [{ endemic: { $in: [true] } }] };
  const invasive = { $or: [{ invasive: { $in: [true] } }] };
  const endangered = {
    $or: [
      {
        iucn: {
          $in: ['EN', 'VU', 'CR']
        }
      }
    ]
  };
  const query = {};
  if (
    req.params.species ||
    req.query.bmClass ||
    req.query.endangered ||
    req.query.endemic ||
    req.query.invasive
  ) {
    query.$and = [];
    if (req.query.bmClass) query.$and.push(bmClass);
    if (req.query.endangered) query.$and.push(endangered);
    if (req.query.endemic) query.$and.push(endemic);
    if (req.query.invasive) query.$and.push(invasive);
  }
  return query;
}

/**
 * @swagger
 * /species/records/{taxID}:
 *   get:
 *     description: "All the records of an acceptedNameUsage (using internal taxID as key): query (use = true & visualizationPrivileges = 0)"
 *     operationId: REC1
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function read(req, res) {
  if (req.params.taxID) {
    try {
      const docs = await Record.aggregate([
        {
          $match: {
            taxID: +req.params.taxID,
            use: true,
            visualizationPrivileges: 0,
            spatialDuplicated: false
          }
        },
        {
          $project: {
            reported: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$reported' }, { $ne: ['$reported', []] }]
                },
                then: { $isArray: '$reported' },
                else: false
              }
            },
            updated: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$updated' }, { $ne: ['$updated', []] }]
                },
                then: { $isArray: '$updated' },
                else: false
              }
            },
            _id: 1,
            taxID: 1,
            acceptedNameUsage: 1,
            species: 1,
            speciesOriginal: 1,
            locality: 1,
            lat: 1,
            lon: 1,
            alt: 1,
            basisOfRecord: 1,
            catalogNumber: 1,
            collector: 1,
            institution: 1,
            url: 1,
            dd: 1,
            mm: 1,
            yyyy: 1,
            suggestedStateProvince: 1,
            suggestedCounty: 1,
            environmentalOutlier: 1,
            source: 1
          }
        }
      ]);
      res.send(GeoJSON.parse(docs, { Point: ['lat', 'lon'] }));
    } catch (err) {
      res.send(err);
    }
  }
}

export async function readValidForGroup(req, res) {
  if (req.params.taxID) {
    try {
      const docs = await Record.aggregate([
        {
          $match: {
            taxID: +req.params.taxID,
            use: true,
            visualizationPrivileges: { $in: [1, 0] },
            spatialDuplicated: false
          }
        },
        {
          $project: {
            reported: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$reported' }, { $ne: ['$reported', []] }]
                },
                then: { $isArray: '$reported' },
                else: false
              }
            },
            updated: {
              $cond: {
                if: {
                  $and: [{ $isArray: '$updated' }, { $ne: ['$updated', []] }]
                },
                then: { $isArray: '$updated' },
                else: false
              }
            },
            _id: 1,
            taxID: 1,
            acceptedNameUsage: 1,
            species: 1,
            speciesOriginal: 1,
            locality: 1,
            lat: 1,
            lon: 1,
            alt: 1,
            basisOfRecord: 1,
            catalogNumber: 1,
            collector: 1,
            institution: 1,
            url: 1,
            dd: 1,
            mm: 1,
            yyyy: 1,
            suggestedStateProvince: 1,
            suggestedCounty: 1,
            environmentalOutlier: 1,
            source: 1
          }
        }
      ]);
      res.send(GeoJSON.parse(docs, { Point: ['lat', 'lon'] }));
    } catch (err) {
      res.send(err);
    }
  }
}

/**
 * @swagger
 * /species:
 *   get:
 *     description: "Get all the species in BioModelos"
 *     operationId: SPE1
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function getAllSpecies(req, res) {
  const query = constructQuery(req);
  if (req.query.modelStatus) {
    try {
      const docs = await Specie.aggregate([
        {
          $match: query
        },
        {
          $lookup: {
            localField: 'taxID',
            from: 'models',
            foreignField: 'taxID',
            as: 'models'
          }
        },
        {
          $match: { 'models.modelStatus': req.query.modelStatus }
        },
        {
          $project: {
            species: 1,
            taxID: 1,
            _id: 0
          }
        }
      ]).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  } else {
    try {
      const docs = await Specie.find(query, {
        species: 1,
        taxID: 1,
        _id: 0
      }).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  }
}

/**
 * @swagger
 * /species/{taxID}:
 *   get:
 *     description: "Get superior taxonomy and the total of records of a species"
 *     operationId: SPE2
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function getTaxonomyAndRecords(req, res) {
  if (req.params.taxID) {
    try {
      const doc = await Specie.find(
        { taxID: +req.params.taxID },
        {
          _id: 0,
          acceptedNameUsage: 1,
          species: 1,
          kingdom: 1,
          phylum: 1,
          class: 1,
          order: 1,
          family: 1
        }
      );
      try {
        const doc2 = await Record.aggregate([
          {
            $match: {
              taxID: +req.params.taxID,
              use: true,
              spatialDuplicated: false
            }
          },
          {
            $group: {
              _id: {
                characteristics: { taxID: '$taxID' }
              },
              totalRecords: { $sum: 1 }
            }
          },
          {
            $project: {
              _id: 0,
              taxID: '$_id.characteristics.taxID',
              totalRecords: '$totalRecords'
            }
          }
        ]);
        if (doc2[0] && doc2[0].totalRecords) {
          doc[0]['_doc']['totalRecords'] = doc2[0].totalRecords;
        } else {
          doc[0]['_doc']['totalRecords'] = 0;
        }
        res.json(doc);
      } catch (err) {
        res.json(err);
      }
    } catch (err) {
      res.json(err);
    }
  }
}

/**
 * @swagger
 * /species/search/{species}:
 *   get:
 *     description: "Get general query to obtain species with some specific characteristics"
 *     operationId: SPE3
 *     parameters:
 *       - name: taxID
 *         in: path
 *         description: The taxon ID of the specie
 *         required: true
 *         type: string
 *     responses:
 *       "200":
 *         description: Success
 *         schema:
 *           type: object
 *           required:
 *             - type
 *             - features
 *           properties:
 *             type:
 *               type: string
 *               default: "FeatureCollection"
 *             features:
 *               type: array
 *               items:
 *                 $ref: "#/definitions/FeatureSpecieRecord"
 *     default:
 *       description: Error
 *       schema:
 *         $ref: "#/definitions/ErrorResponse"
 */
export async function searchSpecie(req, res) {
  const query = constructQuery(req);
  if (req.params.species) {
    const regEx = new RegExp(req.params.species, 'ig');
    query.$and.push({ species: { $regex: regEx } });
  }
  if (req.query.modelStatus) {
    try {
      const docs = await Specie.aggregate([
        {
          $match: query
        },
        {
          $lookup: {
            localField: 'taxID',
            from: 'models',
            foreignField: 'taxID',
            as: 'models'
          }
        },
        {
          $match: { 'models.modelStatus': req.query.modelStatus }
        },
        {
          $project: {
            _id: 0,
            species: 1,
            taxID: 1,
            taxonomicStatus: 1,
            iucn: 1,
            bmClass: 1,
            endemic: 1,
            invasive: 1
          }
        }
      ]).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  } else {
    try {
      const docs = await Specie.find(query, {
        _id: 0,
        species: 1,
        taxID: 1,
        taxonomicStatus: 1,
        iucn: 1,
        bmClass: 1,
        endemic: 1,
        invasive: 1
      }).sort({ species: 1 });
      res.json(docs);
    } catch (err) {
      res.json(err);
    }
  }
}
<|endoftext|>


<commit_before>(function (env) {
    "use strict";

    env.ddg_spice_population = function(api_result){

        var result = api_result[1][0],
            population = result.value;

        if (api_result.error || !population) {
            return Spice.failed('population');
        }

        var countryName = result.country.value,
            lastUpdated = result.date;

        var script = $('[src*="/js/spice/population/"]')[0],
            source = $(script).attr("src"),
            query = source.match(/population\/([^\/]+)\/([^\/]+)/);

        var query_country = decodeURIComponent(query[2]);
        var URL_country = countryName.replace(/\,|\./g,"").replace(/ /g,"-").replace(/Dem/g,"democratic").replace(/Rep/g,"republic").toLowerCase();

        population = population.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

        Spice.add({
            id: "population",
            name: "Population",
            data: result,
            meta: {
                sourceName: "worldbank.org",
                sourceUrl: 'http://data.worldbank.org/country/' + URL_country,
            },
           normalize: function(item) {
                return {
                    title: population,
                    subtitle: query_country + " - Population as of " + lastUpdated
                };
            },
            templates: {
                group: 'text',
                options: {
                    moreAt: true
                }
            }
        });
    };
}(this));<commit_msg>Population Spice - Moving error checking to top.<commit_after>(function (env) {
    "use strict";

    env.ddg_spice_population = function(api_result){

        if (!api_result || !api_result[1][0].value) {
            return Spice.failed('population');
        }

        var result = api_result[1][0],
            population  = result.value,
            countryName = result.country.value,
            lastUpdated = result.date;

        var script = $('[src*="/js/spice/population/"]')[0],
            source = $(script).attr("src"),
            query = source.match(/population\/([^\/]+)\/([^\/]+)/);

        var query_country = decodeURIComponent(query[2]);
        var URL_country = countryName.replace(/\,|\./g,"").replace(/ /g,"-").replace(/Dem/g,"democratic").replace(/Rep/g,"republic").toLowerCase();

        population = population.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

        Spice.add({
            id: "population",
            name: "Population",
            data: result,
            meta: {
                sourceName: "worldbank.org",
                sourceUrl: 'http://data.worldbank.org/country/' + URL_country,
            },
           normalize: function(item) {
                return {
                    title: population,
                    subtitle: query_country + " - Population as of " + lastUpdated
                };
            },
            templates: {
                group: 'text',
                options: {
                    moreAt: true
                }
            }
        });
    };
}(this));<|endoftext|>


<commit_before>import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';

import LocationConflation from '@ideditor/location-conflation';
import whichPolygon from 'which-polygon';

import { t, languageName } from '../util/locale';
import { svgIcon } from '../svg/icon';
import { uiDisclosure } from '../ui/disclosure';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util/rebind';

let _oci = null;

export function uiSuccess(context) {
  const MAXEVENTS = 2;
  const detected = utilDetect();
  const dispatch = d3_dispatch('cancel');
  let _changeset;
  let _location;
  ensureOSMCommunityIndex();   // start fetching the data

  function ensureOSMCommunityIndex() {
    const data = context.data();
    return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
      .then(vals => {
        if (_oci) return _oci;

        const ociResources = vals[0];
        const loco = new LocationConflation(vals[1]);
        let ociFeatures = {};

        Object.values(ociResources).forEach(resource => {
          const feature = loco.resolveLocationSet(resource.locationSet);
          let ociFeature = ociFeatures[feature.id];
          if (!ociFeature) {
            ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
            ociFeature.properties.resourceIDs = new Set();
            ociFeatures[feature.id] = ociFeature;
          }
          ociFeature.properties.resourceIDs.add(resource.id);
        });

        return _oci = {
          features: ociFeatures,
          resources: ociResources,
          query: whichPolygon({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
        };
      });
  }

  // string-to-date parsing in JavaScript is weird
  function parseEventDate(when) {
    if (!when) return;

    let raw = when.trim();
    if (!raw) return;

    if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
      raw += 'Z';            // this forces date to be parsed as a UTC date
    }

    const parsed = new Date(raw);
    return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
  }

  function success(selection) {
    let header = selection
      .append('div')
      .attr('class', 'header fillL');

    header
      .append('button')
      .attr('class', 'fr')
      .on('click', () => dispatch.call('cancel'))
      .call(svgIcon('#iD-icon-close'));

    header
      .append('h3')
      .text(t('success.just_edited'));

    let body = selection
      .append('div')
      .attr('class', 'body save-success fillL');

    let summary = body
      .append('div')
      .attr('class', 'save-summary');

    summary
      .append('h3')
      .text(t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));

    summary
      .append('p')
      .text(t('success.help_html'))
      .append('a')
      .attr('class', 'link-out')
      .attr('target', '_blank')
      .attr('tabindex', -1)
      .attr('href', t('success.help_link_url'))
      .call(svgIcon('#iD-icon-out-link', 'inline'))
      .append('span')
      .text(t('success.help_link_text'));

    let osm = context.connection();
    if (!osm) return;

    let changesetURL = osm.changesetURL(_changeset.id);

    let table = summary
      .append('table')
      .attr('class', 'summary-table');

    let row = table
      .append('tr')
      .attr('class', 'summary-row');

    row
      .append('td')
      .attr('class', 'cell-icon summary-icon')
      .append('a')
      .attr('target', '_blank')
      .attr('href', changesetURL)
      .append('svg')
      .attr('class', 'logo-small')
      .append('use')
      .attr('xlink:href', '#iD-logo-osm');

    let summaryDetail = row
      .append('td')
      .attr('class', 'cell-detail summary-detail');

    summaryDetail
      .append('a')
      .attr('class', 'cell-detail summary-view-on-osm')
      .attr('target', '_blank')
      .attr('href', changesetURL)
      .text(t('success.view_on_osm'));

    summaryDetail
      .append('div')
      .html(t('success.changeset_id', {
        changeset_id: `<a href="${changesetURL}" target="_blank">${_changeset.id}</a>`
      }));

    // Get OSM community index features intersecting the map..
    ensureOSMCommunityIndex()
      .then(oci => {
        let communities = [];
        const properties = oci.query(context.map().center(), true) || [];

        // Gather the communities from the result
        properties.forEach(props => {
          const resourceIDs = Array.from(props.resourceIDs);
          resourceIDs.forEach(resourceID => {
            const resource = oci.resources[resourceID];
            communities.push({
              area: props.area || Infinity,
              order: resource.order || 0,
              resource: resource
            });
          });
        });

        // sort communities by feature area ascending, community order descending
        communities.sort((a, b) => a.area - b.area || b.order - a.order);

        body
          .call(showCommunityLinks, communities.map(c => c.resource));
      });
  }

  function showCommunityLinks(selection, resources) {
    let communityLinks = selection
      .append('div')
      .attr('class', 'save-communityLinks');

    communityLinks
      .append('h3')
      .text(t('success.like_osm'));

    let table = communityLinks
      .append('table')
      .attr('class', 'community-table');

    let row = table.selectAll('.community-row')
      .data(resources);

    let rowEnter = row.enter()
      .append('tr')
      .attr('class', 'community-row');

    rowEnter
      .append('td')
      .attr('class', 'cell-icon community-icon')
      .append('a')
      .attr('target', '_blank')
      .attr('href', d => d.url)
      .append('svg')
      .attr('class', 'logo-small')
      .append('use')
      .attr('xlink:href', d => `#community-${d.type}`);

    let communityDetail = rowEnter
      .append('td')
      .attr('class', 'cell-detail community-detail');

    communityDetail
      .each(showCommunityDetails);

    communityLinks
      .append('div')
      .attr('class', 'community-missing')
      .text(t('success.missing'))
      .append('a')
      .attr('class', 'link-out')
      .attr('target', '_blank')
      .attr('tabindex', -1)
      .call(svgIcon('#iD-icon-out-link', 'inline'))
      .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
      .append('span')
      .text(t('success.tell_us'));
  }

  function showCommunityDetails(d) {
    let selection = d3_select(this);
    let communityID = d.id;
    let replacements = {
      url: linkify(d.url),
      signupUrl: linkify(d.signupUrl || d.url)
    };

    selection
      .append('div')
      .attr('class', 'community-name')
      .append('a')
      .attr('target', '_blank')
      .attr('href', d.url)
      .text(t(`community.${d.id}.name`));

    let descriptionHTML = t(`community.${d.id}.description`, replacements);

    if (d.type === 'reddit') {   // linkify subreddits  #4997
      descriptionHTML = descriptionHTML
        .replace(/(\/r\/\w*\/*)/i, match => linkify(d.url, match));
    }

    selection
      .append('div')
      .attr('class', 'community-description')
      .html(descriptionHTML);

    if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
      selection
        .append('div')
        .call(uiDisclosure(context, `community-more-${d.id}`, false)
          .expanded(false)
          .updatePreference(false)
          .title(t('success.more'))
          .content(showMore)
        );
    }

    let nextEvents = (d.events || [])
      .map(event => {
        event.date = parseEventDate(event.when);
        return event;
      })
      .filter(event => {      // date is valid and future (or today)
        const t = event.date.getTime();
        const now = (new Date()).setHours(0,0,0,0);
        return !isNaN(t) && t >= now;
      })
      .sort((a, b) => {       // sort by date ascending
        return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
      })
      .slice(0, MAXEVENTS);   // limit number of events shown

    if (nextEvents.length) {
      selection
        .append('div')
        .call(uiDisclosure(context, `community-events-${d.id}`, false)
          .expanded(false)
          .updatePreference(false)
          .title(t('success.events'))
          .content(showNextEvents)
        )
        .select('.hide-toggle')
        .append('span')
        .attr('class', 'badge-text')
        .text(nextEvents.length);
    }

    function showMore(selection) {
      let more = selection.selectAll('.community-more')
        .data([0]);

      let moreEnter = more.enter()
        .append('div')
        .attr('class', 'community-more');

      if (d.extendedDescription) {
        moreEnter
          .append('div')
          .attr('class', 'community-extended-description')
          .html(t(`community.${d.id}.extendedDescription`, replacements));
      }

      if (d.languageCodes && d.languageCodes.length) {
        const languageList = d.languageCodes
          .map(code => languageName(context, code))
          .join(', ');

        moreEnter
          .append('div')
          .attr('class', 'community-languages')
          .text(t('success.languages', { languages: languageList }));
      }
    }

    function showNextEvents(selection) {
      let events = selection
        .append('div')
        .attr('class', 'community-events');

      let item = events.selectAll('.community-event')
        .data(nextEvents);

      let itemEnter = item.enter()
        .append('div')
        .attr('class', 'community-event');

      itemEnter
        .append('div')
        .attr('class', 'community-event-name')
        .append('a')
        .attr('target', '_blank')
        .attr('href', d => d.url)
        .text(d => {
          let name = d.name;
          if (d.i18n && d.id) {
            name = t(`community.${communityID}.events.${d.id}.name`, { default: name });
          }
          return name;
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-when')
        .text(d => {
          let options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
          if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
            options.hour = 'numeric';
            options.minute = 'numeric';
          }
          return d.date.toLocaleString(detected.locale, options);
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-where')
        .text(d => {
          let where = d.where;
          if (d.i18n && d.id) {
            where = t(`community.${communityID}.events.${d.id}.where`, { default: where });
          }
          return where;
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-description')
        .text(d => {
          let description = d.description;
          if (d.i18n && d.id) {
            description = t(`community.${communityID}.events.${d.id}.description`, { default: description });
          }
          return description;
        });
    }

    function linkify(url, text) {
      text = text || url;
      return `<a target="_blank" href="${url}">${text}</a>`;
    }
  }

  success.changeset = function(val) {
    if (!arguments.length) return _changeset;
    _changeset = val;
    return success;
  };

  success.location = function(val) {
    if (!arguments.length) return _location;
    _location = val;
    return success;
  };

  return utilRebind(success, dispatch, 'on');
}
<commit_msg>Fix JS error when loading post-save community resources<commit_after>import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';

import LocationConflation from '@ideditor/location-conflation';
import whichPolygon from 'which-polygon';

import { t, languageName } from '../util/locale';
import { svgIcon } from '../svg/icon';
import { uiDisclosure } from '../ui/disclosure';
import { utilDetect } from '../util/detect';
import { utilRebind } from '../util/rebind';

let _oci = null;

export function uiSuccess(context) {
  const MAXEVENTS = 2;
  const detected = utilDetect();
  const dispatch = d3_dispatch('cancel');
  let _changeset;
  let _location;
  ensureOSMCommunityIndex();   // start fetching the data

  function ensureOSMCommunityIndex() {
    const data = context.data();
    return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
      .then(vals => {
        if (_oci) return _oci;

        const ociResources = vals[0].resources;
        const loco = new LocationConflation(vals[1]);
        let ociFeatures = {};

        Object.values(ociResources).forEach(resource => {
          const feature = loco.resolveLocationSet(resource.locationSet);
          let ociFeature = ociFeatures[feature.id];
          if (!ociFeature) {
            ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
            ociFeature.properties.resourceIDs = new Set();
            ociFeatures[feature.id] = ociFeature;
          }
          ociFeature.properties.resourceIDs.add(resource.id);
        });

        return _oci = {
          features: ociFeatures,
          resources: ociResources,
          query: whichPolygon({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
        };
      });
  }

  // string-to-date parsing in JavaScript is weird
  function parseEventDate(when) {
    if (!when) return;

    let raw = when.trim();
    if (!raw) return;

    if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
      raw += 'Z';            // this forces date to be parsed as a UTC date
    }

    const parsed = new Date(raw);
    return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
  }

  function success(selection) {
    let header = selection
      .append('div')
      .attr('class', 'header fillL');

    header
      .append('button')
      .attr('class', 'fr')
      .on('click', () => dispatch.call('cancel'))
      .call(svgIcon('#iD-icon-close'));

    header
      .append('h3')
      .text(t('success.just_edited'));

    let body = selection
      .append('div')
      .attr('class', 'body save-success fillL');

    let summary = body
      .append('div')
      .attr('class', 'save-summary');

    summary
      .append('h3')
      .text(t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));

    summary
      .append('p')
      .text(t('success.help_html'))
      .append('a')
      .attr('class', 'link-out')
      .attr('target', '_blank')
      .attr('tabindex', -1)
      .attr('href', t('success.help_link_url'))
      .call(svgIcon('#iD-icon-out-link', 'inline'))
      .append('span')
      .text(t('success.help_link_text'));

    let osm = context.connection();
    if (!osm) return;

    let changesetURL = osm.changesetURL(_changeset.id);

    let table = summary
      .append('table')
      .attr('class', 'summary-table');

    let row = table
      .append('tr')
      .attr('class', 'summary-row');

    row
      .append('td')
      .attr('class', 'cell-icon summary-icon')
      .append('a')
      .attr('target', '_blank')
      .attr('href', changesetURL)
      .append('svg')
      .attr('class', 'logo-small')
      .append('use')
      .attr('xlink:href', '#iD-logo-osm');

    let summaryDetail = row
      .append('td')
      .attr('class', 'cell-detail summary-detail');

    summaryDetail
      .append('a')
      .attr('class', 'cell-detail summary-view-on-osm')
      .attr('target', '_blank')
      .attr('href', changesetURL)
      .text(t('success.view_on_osm'));

    summaryDetail
      .append('div')
      .html(t('success.changeset_id', {
        changeset_id: `<a href="${changesetURL}" target="_blank">${_changeset.id}</a>`
      }));

    // Get OSM community index features intersecting the map..
    ensureOSMCommunityIndex()
      .then(oci => {
        let communities = [];
        const properties = oci.query(context.map().center(), true) || [];

        // Gather the communities from the result
        properties.forEach(props => {
          const resourceIDs = Array.from(props.resourceIDs);
          resourceIDs.forEach(resourceID => {
            const resource = oci.resources[resourceID];
            communities.push({
              area: props.area || Infinity,
              order: resource.order || 0,
              resource: resource
            });
          });
        });

        // sort communities by feature area ascending, community order descending
        communities.sort((a, b) => a.area - b.area || b.order - a.order);

        body
          .call(showCommunityLinks, communities.map(c => c.resource));
      });
  }

  function showCommunityLinks(selection, resources) {
    let communityLinks = selection
      .append('div')
      .attr('class', 'save-communityLinks');

    communityLinks
      .append('h3')
      .text(t('success.like_osm'));

    let table = communityLinks
      .append('table')
      .attr('class', 'community-table');

    let row = table.selectAll('.community-row')
      .data(resources);

    let rowEnter = row.enter()
      .append('tr')
      .attr('class', 'community-row');

    rowEnter
      .append('td')
      .attr('class', 'cell-icon community-icon')
      .append('a')
      .attr('target', '_blank')
      .attr('href', d => d.url)
      .append('svg')
      .attr('class', 'logo-small')
      .append('use')
      .attr('xlink:href', d => `#community-${d.type}`);

    let communityDetail = rowEnter
      .append('td')
      .attr('class', 'cell-detail community-detail');

    communityDetail
      .each(showCommunityDetails);

    communityLinks
      .append('div')
      .attr('class', 'community-missing')
      .text(t('success.missing'))
      .append('a')
      .attr('class', 'link-out')
      .attr('target', '_blank')
      .attr('tabindex', -1)
      .call(svgIcon('#iD-icon-out-link', 'inline'))
      .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
      .append('span')
      .text(t('success.tell_us'));
  }

  function showCommunityDetails(d) {
    let selection = d3_select(this);
    let communityID = d.id;
    let replacements = {
      url: linkify(d.url),
      signupUrl: linkify(d.signupUrl || d.url)
    };

    selection
      .append('div')
      .attr('class', 'community-name')
      .append('a')
      .attr('target', '_blank')
      .attr('href', d.url)
      .text(t(`community.${d.id}.name`));

    let descriptionHTML = t(`community.${d.id}.description`, replacements);

    if (d.type === 'reddit') {   // linkify subreddits  #4997
      descriptionHTML = descriptionHTML
        .replace(/(\/r\/\w*\/*)/i, match => linkify(d.url, match));
    }

    selection
      .append('div')
      .attr('class', 'community-description')
      .html(descriptionHTML);

    if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
      selection
        .append('div')
        .call(uiDisclosure(context, `community-more-${d.id}`, false)
          .expanded(false)
          .updatePreference(false)
          .title(t('success.more'))
          .content(showMore)
        );
    }

    let nextEvents = (d.events || [])
      .map(event => {
        event.date = parseEventDate(event.when);
        return event;
      })
      .filter(event => {      // date is valid and future (or today)
        const t = event.date.getTime();
        const now = (new Date()).setHours(0,0,0,0);
        return !isNaN(t) && t >= now;
      })
      .sort((a, b) => {       // sort by date ascending
        return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
      })
      .slice(0, MAXEVENTS);   // limit number of events shown

    if (nextEvents.length) {
      selection
        .append('div')
        .call(uiDisclosure(context, `community-events-${d.id}`, false)
          .expanded(false)
          .updatePreference(false)
          .title(t('success.events'))
          .content(showNextEvents)
        )
        .select('.hide-toggle')
        .append('span')
        .attr('class', 'badge-text')
        .text(nextEvents.length);
    }

    function showMore(selection) {
      let more = selection.selectAll('.community-more')
        .data([0]);

      let moreEnter = more.enter()
        .append('div')
        .attr('class', 'community-more');

      if (d.extendedDescription) {
        moreEnter
          .append('div')
          .attr('class', 'community-extended-description')
          .html(t(`community.${d.id}.extendedDescription`, replacements));
      }

      if (d.languageCodes && d.languageCodes.length) {
        const languageList = d.languageCodes
          .map(code => languageName(context, code))
          .join(', ');

        moreEnter
          .append('div')
          .attr('class', 'community-languages')
          .text(t('success.languages', { languages: languageList }));
      }
    }

    function showNextEvents(selection) {
      let events = selection
        .append('div')
        .attr('class', 'community-events');

      let item = events.selectAll('.community-event')
        .data(nextEvents);

      let itemEnter = item.enter()
        .append('div')
        .attr('class', 'community-event');

      itemEnter
        .append('div')
        .attr('class', 'community-event-name')
        .append('a')
        .attr('target', '_blank')
        .attr('href', d => d.url)
        .text(d => {
          let name = d.name;
          if (d.i18n && d.id) {
            name = t(`community.${communityID}.events.${d.id}.name`, { default: name });
          }
          return name;
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-when')
        .text(d => {
          let options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
          if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
            options.hour = 'numeric';
            options.minute = 'numeric';
          }
          return d.date.toLocaleString(detected.locale, options);
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-where')
        .text(d => {
          let where = d.where;
          if (d.i18n && d.id) {
            where = t(`community.${communityID}.events.${d.id}.where`, { default: where });
          }
          return where;
        });

      itemEnter
        .append('div')
        .attr('class', 'community-event-description')
        .text(d => {
          let description = d.description;
          if (d.i18n && d.id) {
            description = t(`community.${communityID}.events.${d.id}.description`, { default: description });
          }
          return description;
        });
    }

    function linkify(url, text) {
      text = text || url;
      return `<a target="_blank" href="${url}">${text}</a>`;
    }
  }

  success.changeset = function(val) {
    if (!arguments.length) return _changeset;
    _changeset = val;
    return success;
  };

  success.location = function(val) {
    if (!arguments.length) return _location;
    _location = val;
    return success;
  };

  return utilRebind(success, dispatch, 'on');
}
<|endoftext|>


<commit_before>/* eslint-env mocha */
const LinkedList = require('../../src').DataStructures.LinkedList;
const assert = require('assert');

describe('LinkedList', () => {
  it('should be empty when initialized', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());
    assert.equal(inst.length, 0);
  });

  it('should have 1 node when pushed', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());

    inst.push(1);
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
  });

  it('should have 1 node when pushed twice and popped once', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    inst.pop();
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
  });

  it('should throw error on popping from empty list', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());
    assert.throws(() => inst.pop(), Error);
  });

  it('should pop using index', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    inst.popNodeByIndex(1);
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
    const ref = inst.getNodeByIndex(0);
    assert.equal(ref.value, 2);
  });

  it('should return list in string format', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    const listString = inst.toString();
    assert.equal(listString, '2 -> 1 -> null');
  });
});
<commit_msg>Update: 100% test coverage<commit_after>/* eslint-env mocha */
const LinkedList = require('../../src').DataStructures.LinkedList;
const assert = require('assert');

describe('LinkedList', () => {
  it('should be empty when initialized', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());
    assert.equal(inst.length, 0);
  });

  it('should have 1 node when pushed', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());

    inst.push(1);
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
  });

  it('should have 1 node when pushed twice and popped once', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    inst.pop();
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
  });

  it('should throw error on popping from empty list', () => {
    const inst = new LinkedList();
    assert(inst.isEmpty());
    assert.throws(() => inst.pop(), Error);
  });

  it('should pop using index', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    inst.popNodeByIndex(1);
    assert(!inst.isEmpty());
    assert.equal(inst.length, 1);
    const ref = inst.getNodeByIndex(0);
    assert.equal(ref.value, 2);
  });

  it('should return list in string format', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    const listString = inst.toString();
    assert.equal(listString, '2 -> 1 -> null');
  });

  it('should call function on all nodes', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);

    inst.forEach(value => 2 * value);
    const listString = inst.toString();
    assert.equal(listString, '4 -> 2 -> null');
  });

  it('should return list as array', () => {
    const inst = new LinkedList();

    inst.push(1);
    inst.push(2);
    const listArray = inst.getListAsArray();
    assert.equal(listArray[0], 2);
    assert.equal(listArray[1], 1);
  });

  it('should return first node that matches', () => {
    const inst = new LinkedList();

    inst.push({ t: 1 });
    inst.push({ x: 2 });
    const node = inst.getNodeByValue({ t: 1 });
    assert.deepStrictEqual(node.value, { t: 1 });
  });

  it('should return -1 if node not listed', () => {
    const inst = new LinkedList();

    inst.push({ t: 1 });
    inst.push({ x: 2 });
    const node = inst.getNodeByValue({ t: 2 });
    assert.deepStrictEqual(node, -1);
  });

  it('should return node at index', () => {
    const inst = new LinkedList();

    inst.push({ t: 1 });
    inst.push({ x: 2 });
    const node = inst.getNodeByIndex(1);
    assert.deepStrictEqual(node.value, { t: 1 });
  });

  it('should throw error if index out of bound while getting node', () => {
    const inst = new LinkedList();

    inst.push({ t: 1 });
    inst.push({ x: 2 });
    assert.throws(() => inst.getNodeByIndex(5), Error);
  });

  it('should throw error if index out of bound while popping', () => {
    const inst = new LinkedList();

    inst.push({ t: 1 });
    inst.push({ x: 2 });
    assert.throws(() => inst.popNodeByIndex(5), Error);
  });
});
<|endoftext|>


<commit_before>import * as THREE from 'three';
import * as TWEEN from 'tween.js';

import Config from '../config';

export function isCountry(country) {
  // return country.type === 'Sovereign country';

  if(country.name === 'Taiwan') {
    return false;
  }

  const isCountry = country.type === 'Sovereign country' ||
    (country.type === 'Country' && (country.sovereignt === country.name || country.sovereignt === country.nameSort)) ||
    country.name === 'Hong Kong' ||
    country.name === 'Macao'
    ;

  // if(country.name.includes('Hong')) {
  //   console.log(country, isCountry);
  // }

  return isCountry;

};

export function matchDestinationToCountryName(destination, country) {
  if(destination === country) return true;

  if(destination === 'Brunei') {
    destination = 'Brunei Darussalam';
  } else if(destination === 'People\'s Republic of China') {
    destination = 'China';
  } else if(destination === 'Czech Republic') {
    destination = 'Czechia';
  } else if(destination === 'Republic of the Congo') {
    destination = 'Republic of Congo';
  } else if(destination === 'Ivory Coast') {
    destination = 'Côte d\'Ivoire';
  } else if(destination === 'Gambia') {
    destination = 'The Gambia';
  } else if(destination === 'North Korea') {
    destination = 'Dem. Rep. Korea';
  } else if(destination === 'South Korea') {
    destination = 'Republic of Korea';
  } else if(destination === 'Laos') {
    destination = 'Lao PDR';
  } else if(destination === 'Macau') {
    destination = 'Macao';
  } else if(destination === 'Burma') {
    destination = 'Myanmar';
  } else if(destination === 'Russia') {
    destination = 'Russian Federation';
  } else if(destination === 'São Tomé and Príncipe') {
    destination = 'São Tomé and Principe';
  } else if(destination === 'Vatican City') {
    destination = 'Vatican';
  } else if(destination === 'United States of America') {
    destination = 'United States';
  } else if(destination === 'Republic of Serbia') {
    destination = 'Serbia';
  } else if(destination === 'eSwatini') {
    destination = 'Swaziland';
  }

  // Swaziland

  return country === destination;
};

export function getCountryNameWithArticle(country) {
  var name = country.name;
  var nameFormatted = '' + name + '';
  if(name === 'Republic of the Congo') {
    return 'the ' + nameFormatted;
  } else if(name === 'Russia Federation') {
    return 'the ' + nameFormatted;
  } else if(name === 'Vatican') {
    return 'the ' + nameFormatted;
  } else if(name === 'United States') {
    return 'the ' + nameFormatted;
  } else if(name === 'British Indian Ocean Territory') {
    return 'the ' + nameFormatted;
  } else if(name === 'British Virgin Islands') {
    return 'the ' + nameFormatted;
  }
  return nameFormatted;
};

export function getCountryByName(countries, name) {
  for(var c = 0; c < countries.length; c++) {
    if(matchDestinationToCountryName(countries[c].name, name) || matchDestinationToCountryName(name, countries[c].name)) {
      return countries[c];
    }
  }
  return null;
};

export function getAllCountriesWithSameSovereignty(countries, sov) {
  var countriesMatched = [];
  for(var c = 0; c < countries.length; c++) {
    if(countries[c].sovereignt === sov) {
      countriesMatched.push(countries[c]);
    }
  }
  return countriesMatched;
};

export function correctCenter(country) {
  if(country.name === 'France') {
    country.center2D.x = -55;
    country.center2D.y = 89;
  } else if(country.name === 'Netherlands') {
    country.center2D.x = -47;
    country.center2D.y = 104;
  } else if(country.name === 'Norway') {
    country.center2D.x = -35;
    country.center2D.y = 140;
  } else if(country.name === 'United States') {
    country.center2D.x = -300;
    country.center2D.y = 65;
  } else if(country.name === 'Canada') {
    country.center2D.x = -290;
    country.center2D.y = 130;
  } else if(country.name === 'Denmark') {
    country.center2D.x = -38;
    country.center2D.y = 114;
  } else if(country.name === 'India') {
    country.center2D.x = 145;
    country.center2D.y = 20;
  } else if(country.name === 'Russia') {
    country.center2D.x = 135;
    country.center2D.y = 132;
  } else if(country.name === 'Brazil') {
    country.center2D.x = -190;
    country.center2D.y = -78;
  } else if(country.name === 'United Kingdom') {
    country.center2D.x = -64;
    country.center2D.y = 107;
  } else if(country.name === 'Spain') {
    country.center2D.x = -67;
    country.center2D.y = 70;
  } else if(country.name === 'Portugal') {
    country.center2D.x = -79;
    country.center2D.y = 67;
  }
};

export function getCountryColorByVisaStatus(country) {
  var c;
  if(country.visa_required === 'no') {
    c = Config.colorVisaNotRequired;

  } else if(country.visa_required === 'on-arrival') {
    c = Config.colorVisaOnArrival;

  } else if(country.visa_required === 'eta') {
    c = Config.colorVisaETA;

  } else if(country.visa_required === 'free-eu') {
    c = Config.colorVisaFreeEU;

  } else if(country.visa_required === 'yes') {
    c = Config.colorVisaRequired;

  } else if(country.visa_required === 'admission-refused') {
    c = Config.colorVisaAdmissionRefused;

  } else if(country.visa_required === '') {
    c = Config.colorVisaDataNotAvailable;

  } else { // special
    c = Config.colorVisaSpecial;

  }
  return c;
};

export function getLineMaterial(country) {
  var material = Config.materialLineDefault;

  if(country.visa_required === 'no') {
    material = Config.materialLineVisaNotRequired;

  } else if(country.visa_required === 'on-arrival') {
    material = Config.materialLineVisaOnArrival;

  } else if(country.visa_required === 'eta') {
    material = Config.materialLineVisaETA;

  } else if(country.visa_required === 'free-eu') {
    material = Config.materialLineVisaFreeEU;

  } else if(country.visa_required === 'yes') {
    material = Config.materialLineVisaRequired;

  } else if(country.visa_required === 'admission-refused') {
    material = Config.materialLineVisaAdmissionRefused;

  } else if(country.visa_required === '') {
    material = Config.materialLineVisaDataNotAvailable;

  } else { // special
    material = Config.materialLineVisaSpecial;
  }
  return material;
};

export function getCountryColorByFreeDestinations(numDestinations, maxNumDestinationsFreeOrOnArrival) {
  var m = numDestinations / maxNumDestinationsFreeOrOnArrival;
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByFreeSources(numSources, maxNumSourcesFreeOrOnArrival) {
  var m = numSources / maxNumSourcesFreeOrOnArrival;
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByGDP(country, maxGDP) {
  var m = country.gdp / maxGDP;
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByGDPPerCapita(country, maxGDPPerCapita) {
  // var m = (country.gdp / country.population * 1000000) / maxGDPPerCapita;
  var m = country.gdpPerCapita / maxGDPPerCapita;
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getPopulationRatio(country, maxPopulation) {
  return parseFloat(country.population) / maxPopulation;    // 1 166 079 220.0;
};

export function getCountryColorByPopulation(country, maxPopulation) {
  var m = getPopulationRatio(country, maxPopulation);
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  // color.copyLinearToGamma(color);
  return color;
};

export function getCountryVisaTitle(country) {
  if(country.visa_title === '') {
    return 'Special regulations';
  } else {
    return country.visa_title;
  }
};

export function getCountryDetailsByVisaStatus(country) {
  var details = '';

  if(country.visa_required === 'no') {
    details = 'Visa not required';

  } else if(country.visa_required === 'on-arrival') {
    details = 'Visa on arrival';

  } else if(country.visa_required === 'free-eu') {
    details = 'Visa not required (EU)';

  } else if(country.visa_required === 'yes') {
    details = 'Visa required';

  } else if(country.visa_required === 'admission-refused') {
    details = 'Admission refused';

  } else if(country.visa_required === 'special') {
    details = 'Special regulations';

  } else if(country.visa_required === '') { // data not available
    details = 'Data not available';

  } else { // special
    details = country.visa_required;

  }
  return details;
};
<commit_msg>Match São Tomé and Principe and Sao Tome and Principe<commit_after>import * as THREE from 'three';
import * as TWEEN from 'tween.js';

import Config from '../config';

export function isCountry(country) {
  // return country.type === 'Sovereign country';

  if(country.name === 'Taiwan') {
    return false;
  }

  const isCountry = country.type === 'Sovereign country' ||
    (country.type === 'Country' && (country.sovereignt === country.name || country.sovereignt === country.nameSort)) ||
    country.name === 'Hong Kong' ||
    country.name === 'Macao'
    ;

  // if(country.name.includes('Hong')) {
  //   console.log(country, isCountry);
  // }

  return isCountry;

};

export function matchDestinationToCountryName(destination, country) {
  if(destination === country) return true;

  if(destination === 'Brunei') {
    destination = 'Brunei Darussalam';
  } else if(destination === 'People\'s Republic of China') {
    destination = 'China';
  } else if(destination === 'Czech Republic') {
    destination = 'Czechia';
  } else if(destination === 'Republic of the Congo') {
    destination = 'Republic of Congo';
  } else if(destination === 'Ivory Coast') {
    destination = 'Côte d\'Ivoire';
  } else if(destination === 'Gambia') {
    destination = 'The Gambia';
  } else if(destination === 'North Korea') {
    destination = 'Dem. Rep. Korea';
  } else if(destination === 'South Korea') {
    destination = 'Republic of Korea';
  } else if(destination === 'Laos') {
    destination = 'Lao PDR';
  } else if(destination === 'Macau') {
    destination = 'Macao';
  } else if(destination === 'Burma') {
    destination = 'Myanmar';
  } else if(destination === 'Russia') {
    destination = 'Russian Federation';
  } else if(destination === 'São Tomé and Príncipe') {
    destination = 'São Tomé and Principe';
  } else if(destination === 'Vatican City') {
    destination = 'Vatican';
  } else if(destination === 'United States of America') {
    destination = 'United States';
  } else if(destination === 'Republic of Serbia') {
    destination = 'Serbia';
  } else if(destination === 'eSwatini') {
    destination = 'Swaziland';
  } else if(destination === 'São Tomé and Principe') {
    destination = 'Sao Tome and Principe';
  }

  return country === destination;
};

export function getCountryNameWithArticle(country) {
  var name = country.name;
  var nameFormatted = '' + name + '';
  if(name === 'Republic of the Congo') {
    return 'the ' + nameFormatted;
  } else if(name === 'Russia Federation') {
    return 'the ' + nameFormatted;
  } else if(name === 'Vatican') {
    return 'the ' + nameFormatted;
  } else if(name === 'United States') {
    return 'the ' + nameFormatted;
  } else if(name === 'British Indian Ocean Territory') {
    return 'the ' + nameFormatted;
  } else if(name === 'British Virgin Islands') {
    return 'the ' + nameFormatted;
  }
  return nameFormatted;
};

export function getCountryByName(countries, name) {
  for(var c = 0; c < countries.length; c++) {
    if(matchDestinationToCountryName(countries[c].name, name) || matchDestinationToCountryName(name, countries[c].name)) {
      return countries[c];
    }
  }
  return null;
};

export function getAllCountriesWithSameSovereignty(countries, sov) {
  var countriesMatched = [];
  for(var c = 0; c < countries.length; c++) {
    if(countries[c].sovereignt === sov) {
      countriesMatched.push(countries[c]);
    }
  }
  return countriesMatched;
};

export function correctCenter(country) {
  if(country.name === 'France') {
    country.center2D.x = -55;
    country.center2D.y = 89;
  } else if(country.name === 'Netherlands') {
    country.center2D.x = -47;
    country.center2D.y = 104;
  } else if(country.name === 'Norway') {
    country.center2D.x = -35;
    country.center2D.y = 140;
  } else if(country.name === 'United States') {
    country.center2D.x = -300;
    country.center2D.y = 65;
  } else if(country.name === 'Canada') {
    country.center2D.x = -290;
    country.center2D.y = 130;
  } else if(country.name === 'Denmark') {
    country.center2D.x = -38;
    country.center2D.y = 114;
  } else if(country.name === 'India') {
    country.center2D.x = 145;
    country.center2D.y = 20;
  } else if(country.name === 'Russia') {
    country.center2D.x = 135;
    country.center2D.y = 132;
  } else if(country.name === 'Brazil') {
    country.center2D.x = -190;
    country.center2D.y = -78;
  } else if(country.name === 'United Kingdom') {
    country.center2D.x = -64;
    country.center2D.y = 107;
  } else if(country.name === 'Spain') {
    country.center2D.x = -67;
    country.center2D.y = 70;
  } else if(country.name === 'Portugal') {
    country.center2D.x = -79;
    country.center2D.y = 67;
  }
};

export function getCountryColorByVisaStatus(country) {
  var c;
  if(country.visa_required === 'no') {
    c = Config.colorVisaNotRequired;

  } else if(country.visa_required === 'on-arrival') {
    c = Config.colorVisaOnArrival;

  } else if(country.visa_required === 'eta') {
    c = Config.colorVisaETA;

  } else if(country.visa_required === 'free-eu') {
    c = Config.colorVisaFreeEU;

  } else if(country.visa_required === 'yes') {
    c = Config.colorVisaRequired;

  } else if(country.visa_required === 'admission-refused') {
    c = Config.colorVisaAdmissionRefused;

  } else if(country.visa_required === '') {
    c = Config.colorVisaDataNotAvailable;

  } else { // special
    c = Config.colorVisaSpecial;

  }
  return c;
};

export function getLineMaterial(country) {
  var material = Config.materialLineDefault;

  if(country.visa_required === 'no') {
    material = Config.materialLineVisaNotRequired;

  } else if(country.visa_required === 'on-arrival') {
    material = Config.materialLineVisaOnArrival;

  } else if(country.visa_required === 'eta') {
    material = Config.materialLineVisaETA;

  } else if(country.visa_required === 'free-eu') {
    material = Config.materialLineVisaFreeEU;

  } else if(country.visa_required === 'yes') {
    material = Config.materialLineVisaRequired;

  } else if(country.visa_required === 'admission-refused') {
    material = Config.materialLineVisaAdmissionRefused;

  } else if(country.visa_required === '') {
    material = Config.materialLineVisaDataNotAvailable;

  } else { // special
    material = Config.materialLineVisaSpecial;
  }
  return material;
};

export function getCountryColorByFreeDestinations(numDestinations, maxNumDestinationsFreeOrOnArrival) {
  var m = numDestinations / maxNumDestinationsFreeOrOnArrival;
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByFreeSources(numSources, maxNumSourcesFreeOrOnArrival) {
  var m = numSources / maxNumSourcesFreeOrOnArrival;
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByGDP(country, maxGDP) {
  var m = country.gdp / maxGDP;
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getCountryColorByGDPPerCapita(country, maxGDPPerCapita) {
  // var m = (country.gdp / country.population * 1000000) / maxGDPPerCapita;
  var m = country.gdpPerCapita / maxGDPPerCapita;
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  return color;
};

export function getPopulationRatio(country, maxPopulation) {
  return parseFloat(country.population) / maxPopulation;    // 1 166 079 220.0;
};

export function getCountryColorByPopulation(country, maxPopulation) {
  var m = getPopulationRatio(country, maxPopulation);
  m = TWEEN.Easing.Exponential.Out(m);
  var color = new THREE.Color(Config.colorZeroDestinations);
  color.lerp(Config.colorMaxDestinations, m);
  // color.copyLinearToGamma(color);
  return color;
};

export function getCountryVisaTitle(country) {
  if(country.visa_title === '') {
    return 'Special regulations';
  } else {
    return country.visa_title;
  }
};

export function getCountryDetailsByVisaStatus(country) {
  var details = '';

  if(country.visa_required === 'no') {
    details = 'Visa not required';

  } else if(country.visa_required === 'on-arrival') {
    details = 'Visa on arrival';

  } else if(country.visa_required === 'free-eu') {
    details = 'Visa not required (EU)';

  } else if(country.visa_required === 'yes') {
    details = 'Visa required';

  } else if(country.visa_required === 'admission-refused') {
    details = 'Admission refused';

  } else if(country.visa_required === 'special') {
    details = 'Special regulations';

  } else if(country.visa_required === '') { // data not available
    details = 'Data not available';

  } else { // special
    details = country.visa_required;

  }
  return details;
};
<|endoftext|>


<commit_before>Array.prototype.diff = function( ar ) {
  "use strict";

  if ( !Array.isArray( ar ) ) throw new TypeError;

  return this.filter( function( el ) {
    return ar.indexOf( el ) == -1;
  } );
};

Array.prototype.merge = function( ar ) {
  "use strict";

  if ( !Array.isArray( ar ) ) throw new TypeError;

  var new_ar = this.slice( 0 );
  this.push.apply( new_ar, ar.diff( this ) );
  return new_ar.unique();
};

Array.prototype.unique = function () {
  "use strict";

  return this.filter( function( el, i ) {
    return this.indexOf( el ) == i;
  }, this );
};

Array.prototype.for_each = function ( fun/*, thisp*/ ) {
  "use strict";

  if (this === void 0 || this === null)
    throw new TypeError();

  var t   = Object(this);
  var len = t.length >>> 0;
  if (typeof fun !== "function") throw new TypeError();

  var thisp = arguments[1];
  var args  = this.slice.call( arguments, 2 );
  args.unshift( null );

  for (var i = 0; i < len; i++)
  {
    if ( i in t ){
      args[0] = t[i];
      if ( fun.apply( thisp || t[i], args ) === false ) break;
    }
  }
};

Object.merge = function( obj1, obj2 ) {
  obj1 = obj1 || {};
  obj2 = obj2 || {};

  if ( !Object.isObject( obj1 ) || !Object.isObject( obj2 ) ) throw new TypeError;

  var res = Object.not_deep_clone( obj1 );

  for ( var prop in obj2 ) {
    res[ prop ] = obj2[ prop ];
  }

  return res;
};

Object.values = function( obj ) {
  return Object.keys( obj ).map( function( v ) {
    return obj[v];
  } );
}

Object.recursive_merge = function( obj1, obj2 ) {
  if ( !Object.isObject( obj1 ) || !Object.isObject( obj2 ) ) throw new TypeError;

  var res = Object.clone( obj1 );

  for ( var prop in obj2 ) {
    if ( res[ prop ] && res[ prop ] instanceof Object && obj2[ prop ] && obj2[ prop ] instanceof Object )
      res[ prop ] = this.recursive_merge( res[ prop ], obj2[ prop ] );
    else res[ prop ] = obj2[ prop ];
  }

  return res;
};

Object.isEmpty = function( v ) {
  if ( !v ) return true;
  if ( v instanceof Array && !v.length ) return true;
  return v instanceof this && !this.keys( v ).length;
};

Object.clone = function( obj ) {
  if ( Array.isArray( obj ) ) return obj.map( function( item ) {
    return Object.clone( item );
  } );

  if ( !Object.isObject( obj ) ) return obj;

  var result = {};
  for ( var prop in obj ) {
    result[ prop ] = this.clone( obj[ prop ] );
  }

  return result;
};

Object.isObject = function( v ) {
  return v && v instanceof this && !Array.isArray( v ) && typeof v != 'function';
}

Object.not_deep_clone = function( obj ) {

  if ( Array.isArray( obj ) ) return obj.slice( 0 );

  if ( !Object.isObject( obj ) ) return obj;

  var result = {};
  for ( var prop in obj )
    result[ prop ] = obj[ prop ];

  return result;
}

Object.reset = function( obj ) {
  if ( this.isEmpty( obj ) ) return null;
  if ( Array.isArray( obj ) ) return obj[0];
  if ( this.isObject( obj ) ) return obj[ this.keys( obj )[0] ];
  return null;
}

var InheritingProxyHandler = require( './proxy_handlers/inheriting_proxy_handler' );

Function.prototype.inherits = function( super_class ) {
  require('util').inherits( this, super_class );

  Object.defineProperty( this.prototype, 'super_', {
    get : function() {

      var handler = new InheritingProxyHandler( {
        target  : super_class.prototype,
        context : this
      } );

      return handler.get_proxy();
    }
  } );

  return this;
}

Function.prototype.is_instantiate = function ( obj ) {
  return obj instanceof this;
};

String.prototype.format = function() {
  var obj = arguments[0];
  if ( Object.isObject( obj ) ) {
    var res = this;
    for ( var str in obj ) res = res.replace( str, obj[ str ] );
    return res;
  }

  var i = 0;
  var args = arguments;
  return this.replace( /%s|%d/g, function() {
    return args[ i++ ];
  } );
};

String.unique = function(){
  var idx = [], itoh = '0123456789ABCDEF'.split('');

  // Array of digits in UUID (32 digits + 4 dashes)
  for (var i = 0; i < 36; i++) { idx[i] = 0xf & Math.random() * 0x10; }
  // Conform to RFC 4122, section 4.4
  idx[14] = 4; // version
  idx[19] = (idx[19] & 0x3) | 0x8; // high bits of clock sequence

  // Convert to hex chars
  for (var i = 0; i < 36; i++) { idx[i] = itoh[idx[i]]; }

  // Insert dashes
  idx[8] = idx[13] = idx[18] = idx[23] = '-';

  return idx.join('');
}

Number.frequency_to_period = function( frequency ) {
  var result = {
    count   : 1,
    period  : 0
  }

  if ( typeof frequency != 'string' ) return result;

  var ar = frequency.split( 'per' );
  if ( ar.length != 2 ) return result;

  var count   = parseInt( ar[0], 10 );
  var period  = parseInt( ar[1], 10 );

  if ( isNaN( count ) || isNaN( period ) ) return result;

  var factors = {
    'sec' : 1000,
    'min' : 60000,
    'hou' : 360000,
    'day' : 8.64e6,
    'wee' : 5.901e7,
    'mon' : 2.529e8
  };

  var factor = ar[1].match( /\W(\w{3})/ );
  factor = factors[ factor && factor[1] ];

  if ( isNaN( factor ) ) factor = 1;

  result.count  = count;
  result.period = period * factor;

  return result;
}

Date.prototype.format = function( format ) {
  var self = this;
  return format.replace( /[\w]/g, function( match ) {
    switch ( match ) {
      case 'Y': return self.getFullYear();
      case 'M': return two_pos( self.getMonth() + 1 );
      case 'D': return two_pos( self.getDate() );
      case 'h': return two_pos( self.getHours() );
      case 'm': return two_pos( self.getMinutes() );
      case 's': return two_pos( self.getSeconds() );
      case 'x':
        var x = self.getMilliseconds();
        return x < 100 ? '0' + two_pos( x ) : x;
    }
  } );
}

Date.prototype.getUTCFormat = function( format ) {
  var self = this;
  return format.replace( /[\w]/g, function( match ) {
    switch ( match ) {
      case 'Y': return self.getUTCFullYear();
      case 'M': return two_pos( self.getUTCMonth() + 1 );
      case 'D': return two_pos( self.getUTCDate() );
      case 'h': return two_pos( self.getUTCHours() );
      case 'm': return two_pos( self.getUTCMinutes() );
      case 's': return two_pos( self.getUTCSeconds() );
      case 'x':
        var x = self.getUTCMilliseconds();
        return x < 100 ? '0' + two_pos( x ) : x;
    }
  } );
}

process.EventEmitter.prototype.re_emit = function() {
  var emitter = arguments[ arguments.length - 1 ];
  for ( var i = 0, i_ln = arguments.length - 1; i < i_ln; i++ ) {
    var action = arguments[i];
    this.on( action, emitter.emit.bind( emitter, action ) );
  }

  return this;
}

var dust = require('dust');
dust.filters.n = function( value ){
  return isFinite( value ) ? Number( value ) : 0;
}

dust.filters.b = function( value ){
  return value ? 'true' : 'false';
}

function two_pos( i ) {
  return i < 10 ? '0' + i : i;
}

exports.next_tick = function( result, error, emitter, action ){
  emitter = emitter || new process.EventEmitter;

  process.nextTick( function() {
    emitter.emit( action || ( error ? 'error' : 'success' ), error || result );
  } );

  return emitter;
};

var Listener = require('./listener');
exports.create_async_listener = function( count, callback, params ) {
  return new Listener( count, callback, params );
};

exports.get_parallel_listener = function ( count, callback, context, params ) {
  var fired = 0;
  var self  = this;

  if ( !count ) return process.nextTick( function() {
    callback.call( context || null, params );
  } );

  return function() {
    var argument_names = Array.prototype.slice.call( arguments, 0 );
    var has_errors     = false;

    return function() {
      if ( has_errors ) return false;

      var can_receive_error = argument_names[0] == 'error';

      if ( can_receive_error && arguments[0] instanceof Error ) {
        has_errors = true;
        return callback.call( context || null, arguments[0] );
      }

      for ( var i = 0, i_ln = argument_names.length; i < i_ln; i++ ) {
        params = params || {};
        params[ argument_names[i] ] = arguments[i];
      }

      if ( ++fired == count ) callback.call(
        context || null,
        can_receive_error ? null : params,
        can_receive_error ? params : undefined
      );
    }
  }
};<commit_msg>app.tools.dust<commit_after>Array.prototype.diff = function( ar ) {
  "use strict";

  if ( !Array.isArray( ar ) ) throw new TypeError;

  return this.filter( function( el ) {
    return ar.indexOf( el ) == -1;
  } );
};

Array.prototype.merge = function( ar ) {
  "use strict";

  if ( !Array.isArray( ar ) ) throw new TypeError;

  var new_ar = this.slice( 0 );
  this.push.apply( new_ar, ar.diff( this ) );
  return new_ar.unique();
};

Array.prototype.unique = function () {
  "use strict";

  return this.filter( function( el, i ) {
    return this.indexOf( el ) == i;
  }, this );
};

Array.prototype.for_each = function ( fun/*, thisp*/ ) {
  "use strict";

  if (this === void 0 || this === null)
    throw new TypeError();

  var t   = Object(this);
  var len = t.length >>> 0;
  if (typeof fun !== "function") throw new TypeError();

  var thisp = arguments[1];
  var args  = this.slice.call( arguments, 2 );
  args.unshift( null );

  for (var i = 0; i < len; i++)
  {
    if ( i in t ){
      args[0] = t[i];
      if ( fun.apply( thisp || t[i], args ) === false ) break;
    }
  }
};

Object.merge = function( obj1, obj2 ) {
  obj1 = obj1 || {};
  obj2 = obj2 || {};

  if ( !Object.isObject( obj1 ) || !Object.isObject( obj2 ) ) throw new TypeError;

  var res = Object.not_deep_clone( obj1 );

  for ( var prop in obj2 ) {
    res[ prop ] = obj2[ prop ];
  }

  return res;
};

Object.values = function( obj ) {
  return Object.keys( obj ).map( function( v ) {
    return obj[v];
  } );
}

Object.recursive_merge = function( obj1, obj2 ) {
  if ( !Object.isObject( obj1 ) || !Object.isObject( obj2 ) ) throw new TypeError;

  var res = Object.clone( obj1 );

  for ( var prop in obj2 ) {
    if ( res[ prop ] && res[ prop ] instanceof Object && obj2[ prop ] && obj2[ prop ] instanceof Object )
      res[ prop ] = this.recursive_merge( res[ prop ], obj2[ prop ] );
    else res[ prop ] = obj2[ prop ];
  }

  return res;
};

Object.isEmpty = function( v ) {
  if ( !v ) return true;
  if ( v instanceof Array && !v.length ) return true;
  return v instanceof this && !this.keys( v ).length;
};

Object.clone = function( obj ) {
  if ( Array.isArray( obj ) ) return obj.map( function( item ) {
    return Object.clone( item );
  } );

  if ( !Object.isObject( obj ) ) return obj;

  var result = {};
  for ( var prop in obj ) {
    result[ prop ] = this.clone( obj[ prop ] );
  }

  return result;
};

Object.isObject = function( v ) {
  return v && v instanceof this && !Array.isArray( v ) && typeof v != 'function';
}

Object.not_deep_clone = function( obj ) {

  if ( Array.isArray( obj ) ) return obj.slice( 0 );

  if ( !Object.isObject( obj ) ) return obj;

  var result = {};
  for ( var prop in obj )
    result[ prop ] = obj[ prop ];

  return result;
}

Object.reset = function( obj ) {
  if ( this.isEmpty( obj ) ) return null;
  if ( Array.isArray( obj ) ) return obj[0];
  if ( this.isObject( obj ) ) return obj[ this.keys( obj )[0] ];
  return null;
}

var InheritingProxyHandler = require( './proxy_handlers/inheriting_proxy_handler' );

Function.prototype.inherits = function( super_class ) {
  require('util').inherits( this, super_class );

  Object.defineProperty( this.prototype, 'super_', {
    get : function() {

      var handler = new InheritingProxyHandler( {
        target  : super_class.prototype,
        context : this
      } );

      return handler.get_proxy();
    }
  } );

  return this;
}

Function.prototype.is_instantiate = function ( obj ) {
  return obj instanceof this;
};

String.prototype.format = function() {
  var obj = arguments[0];
  if ( Object.isObject( obj ) ) {
    var res = this;
    for ( var str in obj ) res = res.replace( str, obj[ str ] );
    return res;
  }

  var i = 0;
  var args = arguments;
  return this.replace( /%s|%d/g, function() {
    return args[ i++ ];
  } );
};

String.unique = function(){
  var idx = [], itoh = '0123456789ABCDEF'.split('');

  // Array of digits in UUID (32 digits + 4 dashes)
  for (var i = 0; i < 36; i++) { idx[i] = 0xf & Math.random() * 0x10; }
  // Conform to RFC 4122, section 4.4
  idx[14] = 4; // version
  idx[19] = (idx[19] & 0x3) | 0x8; // high bits of clock sequence

  // Convert to hex chars
  for (var i = 0; i < 36; i++) { idx[i] = itoh[idx[i]]; }

  // Insert dashes
  idx[8] = idx[13] = idx[18] = idx[23] = '-';

  return idx.join('');
}

Number.frequency_to_period = function( frequency ) {
  var result = {
    count   : 1,
    period  : 0
  }

  if ( typeof frequency != 'string' ) return result;

  var ar = frequency.split( 'per' );
  if ( ar.length != 2 ) return result;

  var count   = parseInt( ar[0], 10 );
  var period  = parseInt( ar[1], 10 );

  if ( isNaN( count ) || isNaN( period ) ) return result;

  var factors = {
    'sec' : 1000,
    'min' : 60000,
    'hou' : 360000,
    'day' : 8.64e6,
    'wee' : 5.901e7,
    'mon' : 2.529e8
  };

  var factor = ar[1].match( /\W(\w{3})/ );
  factor = factors[ factor && factor[1] ];

  if ( isNaN( factor ) ) factor = 1;

  result.count  = count;
  result.period = period * factor;

  return result;
}

Date.prototype.format = function( format ) {
  var self = this;
  return format.replace( /[\w]/g, function( match ) {
    switch ( match ) {
      case 'Y': return self.getFullYear();
      case 'M': return two_pos( self.getMonth() + 1 );
      case 'D': return two_pos( self.getDate() );
      case 'h': return two_pos( self.getHours() );
      case 'm': return two_pos( self.getMinutes() );
      case 's': return two_pos( self.getSeconds() );
      case 'x':
        var x = self.getMilliseconds();
        return x < 100 ? '0' + two_pos( x ) : x;
    }
  } );
}

Date.prototype.getUTCFormat = function( format ) {
  var self = this;
  return format.replace( /[\w]/g, function( match ) {
    switch ( match ) {
      case 'Y': return self.getUTCFullYear();
      case 'M': return two_pos( self.getUTCMonth() + 1 );
      case 'D': return two_pos( self.getUTCDate() );
      case 'h': return two_pos( self.getUTCHours() );
      case 'm': return two_pos( self.getUTCMinutes() );
      case 's': return two_pos( self.getUTCSeconds() );
      case 'x':
        var x = self.getUTCMilliseconds();
        return x < 100 ? '0' + two_pos( x ) : x;
    }
  } );
}

process.EventEmitter.prototype.re_emit = function() {
  var emitter = arguments[ arguments.length - 1 ];
  for ( var i = 0, i_ln = arguments.length - 1; i < i_ln; i++ ) {
    var action = arguments[i];
    this.on( action, emitter.emit.bind( emitter, action ) );
  }

  return this;
}

var dust = exports.dust = require('dust');
dust.filters.n = function( value ){
  return isFinite( value ) ? Number( value ) : 0;
}

dust.filters.b = function( value ){
  return value ? 'true' : 'false';
}

function two_pos( i ) {
  return i < 10 ? '0' + i : i;
}

exports.next_tick = function( result, error, emitter, action ){
  emitter = emitter || new process.EventEmitter;

  process.nextTick( function() {
    emitter.emit( action || ( error ? 'error' : 'success' ), error || result );
  } );

  return emitter;
};

var Listener = require('./listener');
exports.create_async_listener = function( count, callback, params ) {
  return new Listener( count, callback, params );
};

exports.get_parallel_listener = function ( count, callback, context, params ) {
  var fired = 0;
  var self  = this;

  if ( !count ) return process.nextTick( function() {
    callback.call( context || null, params );
  } );

  return function() {
    var argument_names = Array.prototype.slice.call( arguments, 0 );
    var has_errors     = false;

    return function() {
      if ( has_errors ) return false;

      var can_receive_error = argument_names[0] == 'error';

      if ( can_receive_error && arguments[0] instanceof Error ) {
        has_errors = true;
        return callback.call( context || null, arguments[0] );
      }

      for ( var i = 0, i_ln = argument_names.length; i < i_ln; i++ ) {
        params = params || {};
        params[ argument_names[i] ] = arguments[i];
      }

      if ( ++fired == count ) callback.call(
        context || null,
        can_receive_error ? null : params,
        can_receive_error ? params : undefined
      );
    }
  }
};<|endoftext|>