<commit_before>const _ = require('lodash')

module.exports = function (server) {
  const Candidate = server.plugins['hapi-shelf'].model('Candidate')
  const SurveyAnswer = server.plugins['hapi-shelf'].model('SurveyAnswer')

  function getCandidateMatch(surveyResponseId, callback)
  {
    SurveyAnswer
    .query(answer_builder => {
      answer_builder.column('survey_response.id as response_id')
      answer_builder.column('survey_response.geography_id')
      answer_builder.column('candidate_answer.candidate_id')
      answer_builder.column('candidate.candidate_name')
      answer_builder.column('candidate_type.id as type_id')
      answer_builder.column('candidate_type.type_name ')
      answer_builder.column('category.id as category_id')
      answer_builder.column('category.category_name')
      answer_builder.column('survey_answer.question_id')
      answer_builder.column('question.question_text')
      answer_builder.column('survey_answer.answer_id')
      answer_builder.column('a1.answer_value as answer_text')
      answer_builder.column('candidate_answer.answer_id as candidate_answer_id')
      answer_builder.column('a2.answer_value as candidate_answer_text')
      answer_builder.column(server.plugins['hapi-shelf'].knex.raw("case \
        when survey_answer.answer_id = candidate_answer.answer_id \
        then (cast(survey_answer.intensity as numeric(3,2)) + cast(candidate_answer.intensity as numeric(3,2))) / 10 \
        else 0.00 \
        end as score"))
      answer_builder.innerJoin('question', 'survey_answer.question_id', 'question.id')
      answer_builder.innerJoin('category', 'question.category_id', 'category.id')
      answer_builder.innerJoin('answer as a1', 'survey_answer.answer_id', 'a1.id')
      answer_builder.innerJoin('survey_response', 'survey_answer.survey_response_id', 'survey_response.id')
      answer_builder.innerJoin('candidate_answer', 'survey_answer.question_id', 'candidate_answer.question_id')
      answer_builder.innerJoin('answer as a2', 'candidate_answer.answer_id', 'a2.id')
      answer_builder.innerJoin('candidate_geography', function() {
        this.on('candidate_answer.candidate_id', '=', 'candidate_geography.candidate_id')
        .andOn('candidate_geography.geography_id', '=', 'survey_response.geography_id')
      })
      answer_builder.innerJoin('candidate', 'candidate_answer.candidate_id', 'candidate.id')
      answer_builder.innerJoin('candidate_type', 'candidate.candidate_type_id', 'candidate_type.id')
      answer_builder.where('survey_response.id', surveyResponseId)
    })
    .fetchAll()
    .then(answers => {
      formatCandidateMatch(answers, callback)
    })
  }

  function formatCandidateMatch(matchArray, callback)
  {
    var responses = _.uniq(matchArray.pluck('responseId'))
    var geographies = _.uniq(matchArray.pluck('geographyId'))
    var candidates = _.uniq(matchArray.pluck('candidateId'))
    var candidateTypes = _.uniq(matchArray.pluck('typeId'))
    var categories = _.uniq(matchArray.pluck('categoryId'))

    if (responses.length === 1)
    {
      var output = {}
      output.id = responses[0]
      output.geographyId = geographies[0]

      output.survey = candidateTypes.map((typeId) => {
        var candidateType = {}

        candidateType.candidateTypeId = typeId
        candidateType.candidateTypeName = matchArray.findWhere({'typeId': typeId}).get('typeName')

        candidateType.candidates = candidates.map((candidateId) => {
          var candidateList = matchArray.where({'typeId': typeId, 'candidateId': candidateId})

          if (candidateList)
          {
            var candidate = {}

            candidate.candidateId = candidateId
            candidate.candidateName = matchArray.findWhere({'candidateId': candidateId}).get('candidateName')
            candidate.compositeMatchScore = Math.round((candidateList.reduce((p, n) => {
              return p + parseFloat(n.get('score'))
            }, 0.0)*100) / (candidateList.length))

            candidate.categoryMatchScores = categories.map((categoryId) => {
              var categoryList = matchArray.where({'typeId': typeId,
                                                   'categoryId': categoryId,
                                                   'candidateId': candidateId})

             if (categoryList)
             {
               var category = {}

               category.categoryId = categoryId
               category.categoryName = matchArray.findWhere({'categoryId': categoryId}).get('categoryName')
               category.categoryMatch = Math.round((categoryList.reduce((p, n) => {
                 return p + parseFloat(n.get('score'))
               }, 0.0)*100) / categoryList.length)

               category.questions = categoryList.map((item) => {
                 return {
                   questionId: item.get('questionId'),
                   questionText: item.get('questionText'),
                   candidateAnswerId: item.get('candidateAnswerId'),
                   candidateAnswerLabel: item.get('candidateAnswerText'),
                   voterAnswerId: item.get('answerId'),
                   voterAnswerText: item.get('answerText')
                 }
               })

               return category
             }
            })

            return candidate
          }
        })

        return candidateType
      })
    }

    callback(output)
  }

  return [{
    method: 'GET',
    path: '/api/candidate',
    handler: (request, reply) => {
      Candidate
        .fetchAll()
        .then(candidates => {
          reply(candidates)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/candidate/{id}',
    handler: (request, reply) => {
      Candidate
        .where({id: request.params.id})
        .fetch()
        .then(candidate => {
          reply(candidate)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/candidate_match/{survey_response_id}',
    handler: (request, reply) => {
      getCandidateMatch(request.params.survey_response_id, (matches) => {
        reply(matches)
      })
    }
  }]
}
<commit_msg>fixed null scores and duplicate listings in match<commit_after>const _ = require('lodash')

module.exports = function (server) {
  const Candidate = server.plugins['hapi-shelf'].model('Candidate')
  const SurveyAnswer = server.plugins['hapi-shelf'].model('SurveyAnswer')

  function getCandidateMatch(surveyResponseId, callback)
  {
    SurveyAnswer
    .query(answer_builder => {
      answer_builder.column('survey_response.id as response_id')
      answer_builder.column('survey_response.geography_id')
      answer_builder.column('candidate_answer.candidate_id')
      answer_builder.column('candidate.candidate_name')
      answer_builder.column('candidate_type.id as type_id')
      answer_builder.column('candidate_type.type_name ')
      answer_builder.column('category.id as category_id')
      answer_builder.column('category.category_name')
      answer_builder.column('survey_answer.question_id')
      answer_builder.column('question.question_text')
      answer_builder.column('survey_answer.answer_id')
      answer_builder.column('a1.answer_value as answer_text')
      answer_builder.column('candidate_answer.answer_id as candidate_answer_id')
      answer_builder.column('a2.answer_value as candidate_answer_text')
      answer_builder.column(server.plugins['hapi-shelf'].knex.raw("case \
        when survey_answer.answer_id = candidate_answer.answer_id \
        then (cast(survey_answer.intensity as numeric(3,2)) + cast(candidate_answer.intensity as numeric(3,2))) / 10 \
        else 0.00 \
        end as score"))
      answer_builder.innerJoin('question', 'survey_answer.question_id', 'question.id')
      answer_builder.innerJoin('category', 'question.category_id', 'category.id')
      answer_builder.innerJoin('answer as a1', 'survey_answer.answer_id', 'a1.id')
      answer_builder.innerJoin('survey_response', 'survey_answer.survey_response_id', 'survey_response.id')
      answer_builder.innerJoin('candidate_answer', 'survey_answer.question_id', 'candidate_answer.question_id')
      answer_builder.innerJoin('answer as a2', 'candidate_answer.answer_id', 'a2.id')
      answer_builder.innerJoin('candidate_geography', function() {
        this.on('candidate_answer.candidate_id', '=', 'candidate_geography.candidate_id')
        .andOn('candidate_geography.geography_id', '=', 'survey_response.geography_id')
      })
      answer_builder.innerJoin('candidate', 'candidate_answer.candidate_id', 'candidate.id')
      answer_builder.innerJoin('candidate_type', 'candidate.candidate_type_id', 'candidate_type.id')
      answer_builder.where('survey_response.id', surveyResponseId)
    })
    .fetchAll()
    .then(answers => {
      formatCandidateMatch(answers, callback)
    })
  }

  function formatCandidateMatch(matchArray, callback)
  {
    var responses = _.uniq(matchArray.pluck('responseId'))
    var geographies = _.uniq(matchArray.pluck('geographyId'))
    var candidates = _.uniq(matchArray.pluck('candidateId'))
    var candidateTypes = _.uniq(matchArray.pluck('typeId'))
    var categories = _.uniq(matchArray.pluck('categoryId'))

    if (responses.length === 1)
    {
      var output = {}
      output.id = responses[0]
      output.geographyId = geographies[0]

      output.survey = candidateTypes.map((typeId) => {
        var candidateType = {}

        candidateType.candidateTypeId = typeId
        candidateType.candidateTypeName = matchArray.findWhere({'typeId': typeId}).get('typeName')

        candidateType.candidates = candidates.filter((candidateId) => {
          if (matchArray.findWhere({'typeId': typeId, 'candidateId': candidateId}))
          {
            return true
          }
          return false
        }).map((candidateId) => {
          var candidateList = matchArray.where({'typeId': typeId, 'candidateId': candidateId})

          var candidate = {}

          candidate.candidateId = candidateId
          candidate.candidateName = matchArray.findWhere({'candidateId': candidateId}).get('candidateName')
          candidate.compositeMatchScore = Math.round((candidateList.reduce((p, n) => {
            return p + parseFloat(n.get('score'))
          }, 0.0)*100) / (candidateList.length))

          candidate.categoryMatchScores = categories.filter((categoryId) => {
            if (matchArray.findWhere({'typeId': typeId,
                                      'categoryId': categoryId,
                                      'candidateId': candidateId}))
            {
              return true
            }
            return false
          })
          .map((categoryId) => {
            var categoryList = matchArray.where({'typeId': typeId,
                                                 'categoryId': categoryId,
                                                 'candidateId': candidateId})

           if (categoryList.length > 0)
           {
             var category = {}

             category.categoryId = categoryId
             category.categoryName = matchArray.findWhere({'categoryId': categoryId}).get('categoryName')
             category.categoryMatch = Math.round((categoryList.reduce((p, n) => {
               return p + parseFloat(n.get('score'))
             }, 0.0)*100) / categoryList.length)

             category.questions = categoryList.map((item) => {
               return {
                 questionId: item.get('questionId'),
                 questionText: item.get('questionText'),
                 candidateAnswerId: item.get('candidateAnswerId'),
                 candidateAnswerLabel: item.get('candidateAnswerText'),
                 voterAnswerId: item.get('answerId'),
                 voterAnswerText: item.get('answerText')
               }
             })

             return category
           }
          })

          return candidate
        })

        return candidateType
      })
    }

    callback(output)
  }

  return [{
    method: 'GET',
    path: '/api/candidate',
    handler: (request, reply) => {
      Candidate
        .fetchAll()
        .then(candidates => {
          reply(candidates)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/candidate/{id}',
    handler: (request, reply) => {
      Candidate
        .where({id: request.params.id})
        .fetch()
        .then(candidate => {
          reply(candidate)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/candidate_match/{survey_response_id}',
    handler: (request, reply) => {
      getCandidateMatch(request.params.survey_response_id, (matches) => {
        reply(matches)
      })
    }
  }]
}
<|endoftext|>


<commit_before>import difference from 'lodash/difference';
import UPDATE from './constants';
import {
    m2mFromFieldName,
    m2mToFieldName,
    normalizeEntity,
    includes,
} from './utils';

// Forwards side a Foreign Key: returns one object.
// Also works as forwardsOneToOneDescriptor.
function forwardManyToOneDescriptor(fieldName, declaredToModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredToModel = currentSession[declaredToModelName];
            const toId = this._fields[fieldName];
            if (typeof toId !== 'undefined' && toId !== null) {
                return declaredToModel.withId(toId);
            }
            return undefined;
        },
        set(value) {
            const currentSession = this.getClass().session;
            const declaredToModel = currentSession[declaredToModelName];
            const thisId = this.getId();
            let toId;
            if (value instanceof declaredToModel) {
                toId = value.getId();
            } else {
                toId = value;
            }

            this.getClass().addUpdate({
                type: UPDATE,
                payload: {
                    idArr: [thisId],
                    updater: {
                        [fieldName]: toId,
                    },
                },
            });
        },
    };
}

const forwardOneToOneDescriptor = forwardManyToOneDescriptor;

function backwardOneToOneDescriptor(declaredFieldName, declaredFromModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const thisId = this.getId();
            let found;
            try {
                found = declaredFromModel.get({ [declaredFieldName]: thisId });
            } catch (e) {
                return null;
            }
            return found;
        },
        set() {
            throw new Error('Can\'t mutate a reverse one-to-one relation.');
        },
    };
}

// Reverse side of a Foreign Key: returns many objects.
function backwardManyToOneDescriptor(declaredFieldName, declaredFromModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const thisId = this.getId();
            return declaredFromModel.filter({ [declaredFieldName]: thisId });
        },
        set() {
            throw new Error('Can\'t mutate a reverse many-to-one relation.');
        },
    };
}

// Both sides of Many to Many, use the reverse flag.
function manyToManyDescriptor(
    declaredFromModelName,
    declaredToModelName,
    throughModelName,
    throughFields,
    reverse) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const declaredToModel = currentSession[declaredToModelName];
            const throughModel = currentSession[throughModelName];
            const thisId = this.getId();

            const fromFieldName = throughFields.from;
            const toFieldName = throughFields.to;

            const lookupObj = {};
            if (!reverse) {
                lookupObj[fromFieldName] = thisId;
            } else {
                lookupObj[toFieldName] = thisId;
            }

            const throughQs = throughModel.filter(lookupObj);
            const toIds = throughQs.withRefs.map(obj => obj[reverse ? fromFieldName : toFieldName]);

            const qsFromModel = reverse ? declaredFromModel : declaredToModel;
            const qs = qsFromModel.getQuerySetFromIds(toIds);

            qs.add = function add(...args) {
                const idsToAdd = args.map(normalizeEntity);

                const filterWithAttr = reverse ? fromFieldName : toFieldName;

                const existingQs = throughQs.withRefs
                    .filter(through => includes(idsToAdd, through[filterWithAttr]));

                if (existingQs.exists()) {
                    const existingIds = existingQs.withRefs.map(through => through[filterWithAttr]);

                    const toAddModel = reverse
                        ? declaredFromModel.modelName
                        : declaredToModel.modelName;

                    const addFromModel = reverse
                        ? declaredToModel.modelName
                        : declaredFromModel.modelName;

                    throw new Error(`Tried to add already existing ${toAddModel} id(s) ${existingIds} to the ${addFromModel} instance with id ${thisId}`);
                }

                idsToAdd.forEach(id => {
                    throughModel.create({
                        [fromFieldName]: thisId,
                        [toFieldName]: id,
                    });
                });
            };

            qs.clear = function clear() {
                throughQs.delete();
            };

            qs.remove = function remove(...entities) {
                const idsToRemove = entities.map(normalizeEntity);

                const attrInIdsToRemove = reverse ? fromFieldName : toFieldName;
                const entitiesToDelete = throughQs.withRefs
                    .filter(through => includes(idsToRemove, through[attrInIdsToRemove]));

                if (entitiesToDelete.count() !== idsToRemove.length) {
                    // Tried deleting non-existing entities.
                    const entitiesToDeleteIds = entitiesToDelete.withRefs.map(through => through[attrInIdsToRemove]);
                    const unexistingIds = difference(idsToRemove, entitiesToDeleteIds);

                    const toDeleteModel = reverse
                        ? declaredFromModel.modelName
                        : declaredToModel.modelName;

                    const deleteFromModel = reverse
                        ? declaredToModel.modelName
                        : declaredFromModel.modelName;

                    throw new Error(`Tried to delete non-existing ${toDeleteModel} id(s) ${unexistingIds} from the ${deleteFromModel} instance with id ${thisId}`);
                }

                entitiesToDelete.delete();
            };

            return qs;
        },

        set() {
            throw new Error('Tried setting a M2M field. Please use the related QuerySet methods add and remove.');
        },
    };
}

export {
    forwardManyToOneDescriptor,
    forwardOneToOneDescriptor,
    backwardOneToOneDescriptor,
    backwardManyToOneDescriptor,
    manyToManyDescriptor,
};
<commit_msg>Remove unused imports<commit_after>import difference from 'lodash/difference';
import UPDATE from './constants';
import {
    normalizeEntity,
    includes,
} from './utils';

// Forwards side a Foreign Key: returns one object.
// Also works as forwardsOneToOneDescriptor.
function forwardManyToOneDescriptor(fieldName, declaredToModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredToModel = currentSession[declaredToModelName];
            const toId = this._fields[fieldName];
            if (typeof toId !== 'undefined' && toId !== null) {
                return declaredToModel.withId(toId);
            }
            return undefined;
        },
        set(value) {
            const currentSession = this.getClass().session;
            const declaredToModel = currentSession[declaredToModelName];
            const thisId = this.getId();
            let toId;
            if (value instanceof declaredToModel) {
                toId = value.getId();
            } else {
                toId = value;
            }

            this.getClass().addUpdate({
                type: UPDATE,
                payload: {
                    idArr: [thisId],
                    updater: {
                        [fieldName]: toId,
                    },
                },
            });
        },
    };
}

const forwardOneToOneDescriptor = forwardManyToOneDescriptor;

function backwardOneToOneDescriptor(declaredFieldName, declaredFromModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const thisId = this.getId();
            let found;
            try {
                found = declaredFromModel.get({ [declaredFieldName]: thisId });
            } catch (e) {
                return null;
            }
            return found;
        },
        set() {
            throw new Error('Can\'t mutate a reverse one-to-one relation.');
        },
    };
}

// Reverse side of a Foreign Key: returns many objects.
function backwardManyToOneDescriptor(declaredFieldName, declaredFromModelName) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const thisId = this.getId();
            return declaredFromModel.filter({ [declaredFieldName]: thisId });
        },
        set() {
            throw new Error('Can\'t mutate a reverse many-to-one relation.');
        },
    };
}

// Both sides of Many to Many, use the reverse flag.
function manyToManyDescriptor(
    declaredFromModelName,
    declaredToModelName,
    throughModelName,
    throughFields,
    reverse) {
    return {
        get() {
            const currentSession = this.getClass().session;
            const declaredFromModel = currentSession[declaredFromModelName];
            const declaredToModel = currentSession[declaredToModelName];
            const throughModel = currentSession[throughModelName];
            const thisId = this.getId();

            const fromFieldName = throughFields.from;
            const toFieldName = throughFields.to;

            const lookupObj = {};
            if (!reverse) {
                lookupObj[fromFieldName] = thisId;
            } else {
                lookupObj[toFieldName] = thisId;
            }

            const throughQs = throughModel.filter(lookupObj);
            const toIds = throughQs.withRefs.map(obj => obj[reverse ? fromFieldName : toFieldName]);

            const qsFromModel = reverse ? declaredFromModel : declaredToModel;
            const qs = qsFromModel.getQuerySetFromIds(toIds);

            qs.add = function add(...args) {
                const idsToAdd = args.map(normalizeEntity);

                const filterWithAttr = reverse ? fromFieldName : toFieldName;

                const existingQs = throughQs.withRefs
                    .filter(through => includes(idsToAdd, through[filterWithAttr]));

                if (existingQs.exists()) {
                    const existingIds = existingQs.withRefs.map(through => through[filterWithAttr]);

                    const toAddModel = reverse
                        ? declaredFromModel.modelName
                        : declaredToModel.modelName;

                    const addFromModel = reverse
                        ? declaredToModel.modelName
                        : declaredFromModel.modelName;

                    throw new Error(`Tried to add already existing ${toAddModel} id(s) ${existingIds} to the ${addFromModel} instance with id ${thisId}`);
                }

                idsToAdd.forEach(id => {
                    throughModel.create({
                        [fromFieldName]: thisId,
                        [toFieldName]: id,
                    });
                });
            };

            qs.clear = function clear() {
                throughQs.delete();
            };

            qs.remove = function remove(...entities) {
                const idsToRemove = entities.map(normalizeEntity);

                const attrInIdsToRemove = reverse ? fromFieldName : toFieldName;
                const entitiesToDelete = throughQs.withRefs
                    .filter(through => includes(idsToRemove, through[attrInIdsToRemove]));

                if (entitiesToDelete.count() !== idsToRemove.length) {
                    // Tried deleting non-existing entities.
                    const entitiesToDeleteIds = entitiesToDelete.withRefs.map(through => through[attrInIdsToRemove]);
                    const unexistingIds = difference(idsToRemove, entitiesToDeleteIds);

                    const toDeleteModel = reverse
                        ? declaredFromModel.modelName
                        : declaredToModel.modelName;

                    const deleteFromModel = reverse
                        ? declaredToModel.modelName
                        : declaredFromModel.modelName;

                    throw new Error(`Tried to delete non-existing ${toDeleteModel} id(s) ${unexistingIds} from the ${deleteFromModel} instance with id ${thisId}`);
                }

                entitiesToDelete.delete();
            };

            return qs;
        },

        set() {
            throw new Error('Tried setting a M2M field. Please use the related QuerySet methods add and remove.');
        },
    };
}

export {
    forwardManyToOneDescriptor,
    forwardOneToOneDescriptor,
    backwardOneToOneDescriptor,
    backwardManyToOneDescriptor,
    manyToManyDescriptor,
};
<|endoftext|>


<commit_before>$(document).ready(function(){
  var dataTableView = findViewObject(this.getElementsByClassName("bk-data-table")[0], Bokeh.index[findModelID("8dc6e575-0ae6-4b64-bc54-a9ffe282b510")])
  var dataSource = dataTableView.mget("source")
  var fields = getFieldNames(dataTableView.model.attributes.columns)
  $(".plotdiv").append("<form id='column-filters'><input type='submit'></form>")

  for (var i = 0; i < fields.length; i++){
    var optionsString = optionsConstructor(dataSource, fields[i].field)
    $("form#column-filters").append(""+fields[i].title+"<select name=" + fields[i].field + ">"+ optionsString+"</select>")
  }

  $("form#column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }

    var columns = dataSource.attributes.data

    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })
})

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] != filterToApply.value){
          rows.splice(j,1)
        }
      }
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns){
  var result = []
  for(var i = 0; i < columns.length; i++){
    var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
    result.push({field : columnData.attributes.field, title : columnData.attributes.title})
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value="+ elements[i] +">" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}<commit_msg>handles multiple filters properly<commit_after>$(document).ready(function(){
  var dataTableView = findViewObject(this.getElementsByClassName("bk-data-table")[0], Bokeh.index[findModelID("8dc6e575-0ae6-4b64-bc54-a9ffe282b510")])
  var dataSource = dataTableView.mget("source")
  var fields = getFieldNames(dataTableView.model.attributes.columns)
  $(".plotdiv").append("<form id='column-filters'><input type='submit'></form>")

  for (var i = 0; i < fields.length; i++){
    var optionsString = optionsConstructor(dataSource, fields[i].field)
    $("form#column-filters").append(""+fields[i].title+"<select name=" + fields[i].field + ">"+ optionsString+"</select>")
  }

  $("form#column-filters").submit(function(e){
    e.preventDefault();
    var options = $(e.target).find("select");
    var workingFilters = [];
    for(var i = 0; i < options.length; i++){
      if(options[i].type !== 'submit' && options[i].selectedOptions[0].value !== ""){
        workingFilters.push({name: options[i].name, value: options[i].selectedOptions[0].value})
      }
    }

    var columns = dataSource.attributes.data
    var rows = []
    var rowsToSelect = applyValueFilter(workingFilters, columns, rows)

    dataTableView.grid.setSelectedRows(rowsToSelect)
    selectionShift(dataTableView)
  })
})

var applyValueFilter = function(workingFilters, columns, rows){
  if(workingFilters.length === 0){
    return rows;
  }
  else{
    var filterToApply = workingFilters.pop()
    var column = columns[filterToApply.name]
    if (rows.length > 0) {
      var newRows = []
      for(var j = 0; j < rows.length; j++){
        if(column[rows[j]] == filterToApply.value){
          newRows.push(rows.slice(j,j+1)[0])
        }
      }
      rows = newRows
    }
    else {
      for(var j = 0; j < column.length; j++){
        if(column[j] == filterToApply.value){
          rows.push(j)
        }
      }
    }
    return applyValueFilter(workingFilters, columns, rows);
  }
}

var rowsIndices = function(array){
  var result = []
  for(var i = 0; i < array.length; i++){
    result.push(i)
  }
  return result
}

var getFieldNames = function(columns){
  var result = []
  for(var i = 0; i < columns.length; i++){
    var columnData = Bokeh.Collections("TableColumn").get(columns[i].id)
    result.push({field : columnData.attributes.field, title : columnData.attributes.title})
  }
  return result
}

var optionsConstructor = function(source, fieldName){
  var result = "<option value=''></option>";
  var elements = getUniqueElements(source.attributes.data[fieldName])
  for(var i = 0; i < elements.length; i++){
    result += "<option value="+ elements[i] +">" + elements[i] + "</option>"
  }
  return result
}

var getUniqueElements = function(array){
  var u = {}, a = [];
     for(var i = 0; i < array.length; ++i){
        if(u.hasOwnProperty(array[i])) {
           continue;
        }
        a.push(array[i]);
        u[array[i]] = 1;
     }
     return a;
}<|endoftext|>


<commit_before>'use strict';
const request = require('request');
const cheerio = require('cheerio');
const url = 'http://siemens.sv-restaurant.ch/de/menuplan/';

class SVPageParser{

  parseToday(callback) {
    request(url, (err, resp, body) => {
      let $ = cheerio.load(body);
      let offers = $('#menu-plan-tab1').find('.menu-item');
      let message = this.formatDayMenu($, offers);
      callback(message);
    });
  }

  parseWeek(callback) {
    request(url, (err, resp, body) => {
      let $ = cheerio.load(body);
      let message = this.formatWeek($);
      callback(message);
    });
  }

  formatWeek($) {
    let days = $('.day-nav').find('li');

    let text = '';
    $(days).each((i, day) => {
      let dayName = $(day).find('.day').text();
      let dayDate= $(day).find('.date').text();
      let offers = $(`#menu-plan-tab${i+1}`).find('.menu-item');

      text += `*${dayName} ${dayDate}* \n`;
      text += `========\n`;
      text += this.formatDayMenu($, offers) + '\n';
    });

    return text;
  }

  formatDayMenu($, offers) {
    let text = '';
    $(offers).each(function (i, offer) {
      let menuDescription = $(offer).find('.menu-description');
      menuDescription.find('br').replaceWith(' ');

      text += '*' + removeSpecialCharacters($(offer).find('.menu-title').text().trim()) + '*\n';
      text += removeSpecialCharacters(menuDescription.text()) + '\n';
      text += '_' + removeSpecialCharacters($(offer).find('.prices-3 .val').text() + ' ' + $(offer).find('.prices-3 .desc').text()) + "_\n";
      let provenance = removeSpecialCharacters($(offer).find('.menu-provenance').text());
      if (provenance.length > 1) {
        text += '_' + provenance + "_\n\n";
      } else {
        text += "\n";
      }
    });

    function removeSpecialCharacters(text) {
      return (text.replace(/[`*_#\r]/g, ''));
    }

    return text;
  }
}

module.exports = SVPageParser;
<commit_msg>feat(svParser): add location to message<commit_after>'use strict';
const request = require('request');
const cheerio = require('cheerio');
const url = 'http://siemens.sv-restaurant.ch/de/menuplan/';

class SVPageParser{

  parseToday(callback) {
    request(url, (err, resp, body) => {
      let $ = cheerio.load(body);
      let offers = $('#menu-plan-tab1').find('.menu-item');
      let message = this.formatDayMenu($, offers);
      callback(message);
    });
  }

  parseWeek(callback) {
    request(url, (err, resp, body) => {
      let $ = cheerio.load(body);
      let message = this.formatWeek($);
      callback(message);
    });
  }

  formatWeek($) {
    let days = $('.day-nav').find('li');

    let text = '';
    $(days).each((i, day) => {
      let dayName = $(day).find('.day').text();
      let dayDate= $(day).find('.date').text();
      let offers = $(`#menu-plan-tab${i+1}`).find('.menu-item');

      text += `*${dayName} ${dayDate}* \n`;
      text += `========\n`;
      text += this.formatDayMenu($, offers) + '\n';
    });

    return text;
  }

  formatDayMenu($, offers) {
    let text = '';
    $(offers).each(function (i, offer) {
      let menuDescription = $(offer).find('.menu-description');
      menuDescription.find('br').replaceWith(' ');

      text += '*' + removeSpecialCharacters($(offer).find('.menu-title').text().trim()) + '*';
      text += '_(' + removeSpecialCharacters($(offer).find('.menuline').text()) + ')_\n';
      text += removeSpecialCharacters(menuDescription.text()) + '\n';
      text += '_' + removeSpecialCharacters($(offer).find('.prices-3 .val').text() + ' ' + $(offer).find('.prices-3 .desc').text()) + "_\n";
      let provenance = removeSpecialCharacters($(offer).find('.menu-provenance').text());
      if (provenance.length > 1) {
        text += '_' + provenance + "_\n\n";
      } else {
        text += "\n";
      }
    });

    function removeSpecialCharacters(text) {
      return (text.replace(/[`*_#\r]/g, ''));
    }

    return text;
  }
}

module.exports = SVPageParser;
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'backbone',
    'jquery',
    'underscore',
    'find/app/page/search/filters/parametric/numeric-widget',
    'parametric-refinement/prettify-field-name',
    'parametric-refinement/selected-values-collection',
    'i18n!find/nls/bundle',
    'text!find/templates/app/page/search/filters/parametric/numeric-parametric-field-view.html'
], function (Backbone, $, _, numericWidget, prettifyFieldName, SelectedParametricValuesCollection, i18n, template) {
    "use strict";
    const GRAPH_HEIGHT = 110;

    function resetSelectedParametricValues(selectedParametricValues, fieldName) {
        const existingRestrictions = selectedParametricValues.where({field: fieldName});
        existingRestrictions.forEach(function (model) {
            selectedParametricValues.remove(model);
        });
    }

    function updateRestrictions(selectedParametricValues, fieldName, min, max) {
        selectedParametricValues.add({
            field: fieldName,
            range: [min, max],
            numeric: true
        });
    }

    function updateModel(model, queryModel, targetNumberOfBuckets, newMin, newMax) {
        $.ajax({
                url: '../api/public/parametric/buckets',
                traditional: true,
                data: {
                    fieldNames: [model.get('id')],
                    databases: queryModel.get('indexes'),
                    queryText: queryModel.get('queryText'),
                    targetNumberOfBuckets: [targetNumberOfBuckets],
                    bucketMin: [newMin],
                    bucketMax: [newMax]
                }
            })
            .success(function (results) {
                const result = results[0];
                model.set({
                    count: result.count,
                    min: result.min,
                    max: result.max,
                    bucketSize: result.bucketSize,
                    values: result.values
                });
            });
    }

    function roundInputNumber(x1) {
        return Math.round(x1 * 10) / 10;
    }

    return Backbone.View.extend({
        className: 'animated fadeIn',
        template: _.template(template),

        events: {
            'click .numeric-parametric-no-min': function () {
                //noinspection JSUnresolvedVariable
                const $minInput = this.$minInput;
                //noinspection JSUnresolvedVariable
                const absoluteMinValue = this.absoluteMinValue;
                if (absoluteMinValue !== $minInput.val()) {
                    $minInput.val(absoluteMinValue);
                    $minInput.trigger('change');
                }
            },
            'click .numeric-parametric-no-max': function () {
                //noinspection JSUnresolvedVariable
                const $maxInput = this.$maxInput;
                //noinspection JSUnresolvedVariable
                const absoluteMaxValue = this.absoluteMaxValue;
                if (absoluteMaxValue !== $maxInput.val()) {
                    $maxInput.val(absoluteMaxValue);
                    $maxInput.trigger('change');
                }
            },
            'click .numeric-parametric-reset': function () {
                //noinspection JSUnresolvedVariable
                resetSelectedParametricValues(this.selectedParametricValues, this.fieldName);
                //noinspection JSUnresolvedVariable
                updateModel(this.model, this.queryModel, Math.floor(this.$el.width() / this.pixelsPerBucket), this.absoluteMinValue, this.absoluteMaxValue);
            },
            'change .numeric-parametric-min-input': function () {
                //noinspection JSUnresolvedVariable
                updateRestrictions(this.selectedParametricValues, this.fieldName, this.$minInput.val(), this.$maxInput.val());
                //noinspection JSUnresolvedFunction
                this.drawSelection();
            },
            'change .numeric-parametric-max-input': function () {
                //noinspection JSUnresolvedVariable
                updateRestrictions(this.selectedParametricValues, this.fieldName, this.$minInput.val(), this.$maxInput.val());
                //noinspection JSUnresolvedFunction
                this.drawSelection();
            }
        },

        initialize: function (options) {
            this.queryModel = options.queryModel;
            this.selectedParametricValues = options.selectedParametricValues;
            this.pixelsPerBucket = options.pixelsPerBucket;
            this.viewWidth = options.viewWidth;
            this.fieldName = this.model.id;
            this.widget = numericWidget({});

            this.absoluteMinValue = this.model.get('min');
            this.absoluteMaxValue = this.model.get('max');
        },

        render: function () {
            //noinspection JSUnresolvedVariable,JSUnresolvedFunction
            this.$el.empty().append(this.template({
                i18n: i18n,
                fieldName: prettifyFieldName(this.model.get('name')),
                id: _.uniqueId('numeric-parametric-field')
            }));

            //noinspection JSUnresolvedFunction
            this.$minInput = this.$('.numeric-parametric-min-input');
            //noinspection JSUnresolvedFunction
            this.$maxInput = this.$('.numeric-parametric-max-input');

            this.$minInput.val(this.absoluteMinValue);
            this.$maxInput.val(this.absoluteMaxValue);

            const updateCallback = function (x1, x2) {
                // rounding to one decimal place
                this.$minInput.val(Math.max(roundInputNumber(x1), this.model.get('min')));
                this.$maxInput.val(Math.min(roundInputNumber(x2), this.model.get('max')));
            }.bind(this);
            const selectionCallback = function (x1, x2) {
                updateRestrictions(this.selectedParametricValues, this.fieldName, Math.max(x1, this.model.get('max')), Math.min(x2, this.model.get('min')));
            }.bind(this);
            const deselectionCallback = function () {
                this.$minInput.val(this.absoluteMinValue);
                this.$maxInput.val(this.absoluteMaxValue);
                resetSelectedParametricValues(this.selectedParametricValues, this.fieldName);
            }.bind(this);
            const zoomCallback = function (newMin, newMax) {
                //noinspection JSUnresolvedVariable
                updateModel(this.model, this.queryModel, Math.floor(this.$el.width() / this.pixelsPerBucket), newMin, newMax);
            }.bind(this);
            const buckets = this.model.get('values');
            //noinspection JSUnresolvedFunction
            this.graph = this.widget.drawGraph({
                chart: this.$('.chart')[0],
                data: {
                    buckets: buckets,
                    bucketSize: this.model.get('bucketSize'),
                    maxCount: _.max(_.pluck(buckets, 'count')),
                    minValue: this.model.get('min'),
                    maxValue: this.model.get('max')
                },
                updateCallback: updateCallback,
                selectionCallback: selectionCallback,
                deselectionCallback: deselectionCallback,
                zoomCallback: zoomCallback,
                xRange: this.viewWidth,
                yRange: GRAPH_HEIGHT,
                tooltip: i18n['search.numericParametricFields.tooltip']
            });

            this.drawSelection();
        },

        drawSelection: function () {
            const graph = this.graph;

            this.selectedParametricValues.where({
                field: this.fieldName
            }).forEach(function (restriction) {
                const range = restriction.get('range');
                if (range) {
                    this.$minInput.val(roundInputNumber(range[0]));
                    this.$maxInput.val(roundInputNumber(range[1]));

                    graph.selectionRect.init(graph.chart, GRAPH_HEIGHT, graph.scale.barWidth(range[0]));
                    graph.selectionRect.update(graph.scale.barWidth(range[1]));
                    graph.selectionRect.focus();
                }
            }, this);
        }
    });
});<commit_msg>FIND-127 Zooming :: we now zoom on existing data by filtering out buckets not in the current range before retrieving the new buckets form the server, to increase responsiveness; use collection to ensure only latest request is rendered [rev. alex.scown]<commit_after>/*
 * Copyright 2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'backbone',
    'jquery',
    'underscore',
    'find/app/model/find-base-collection',
    'find/app/page/search/filters/parametric/numeric-widget',
    'parametric-refinement/prettify-field-name',
    'parametric-refinement/selected-values-collection',
    'i18n!find/nls/bundle',
    'text!find/templates/app/page/search/filters/parametric/numeric-parametric-field-view.html'
], function (Backbone, $, _, FindBaseCollection, numericWidget, prettifyFieldName, SelectedParametricValuesCollection, i18n, template) {
    "use strict";
    const GRAPH_HEIGHT = 110;

    function resetSelectedParametricValues(selectedParametricValues, fieldName) {
        const existingRestrictions = selectedParametricValues.where({field: fieldName});
        existingRestrictions.forEach(function (model) {
            selectedParametricValues.remove(model);
        });
    }

    function updateRestrictions(selectedParametricValues, fieldName, min, max) {
        selectedParametricValues.add({
            field: fieldName,
            range: [min, max],
            numeric: true
        });
    }

    function roundInputNumber(x1) {
        return Math.round(x1 * 10) / 10;
    }

    return Backbone.View.extend({
        className: 'animated fadeIn',
        template: _.template(template),

        events: {
            'click .numeric-parametric-no-min': function () {
                //noinspection JSUnresolvedVariable
                const $minInput = this.$minInput;
                //noinspection JSUnresolvedVariable
                const absoluteMinValue = this.absoluteMinValue;
                if (absoluteMinValue !== $minInput.val()) {
                    $minInput.val(absoluteMinValue);
                    $minInput.trigger('change');
                }
            },
            'click .numeric-parametric-no-max': function () {
                //noinspection JSUnresolvedVariable
                const $maxInput = this.$maxInput;
                //noinspection JSUnresolvedVariable
                const absoluteMaxValue = this.absoluteMaxValue;
                if (absoluteMaxValue !== $maxInput.val()) {
                    $maxInput.val(absoluteMaxValue);
                    $maxInput.trigger('change');
                }
            },
            'click .numeric-parametric-reset': function () {
                //noinspection JSUnresolvedVariable
                resetSelectedParametricValues(this.selectedParametricValues, this.fieldName);
                //noinspection JSUnresolvedVariable,JSUnresolvedFunction
                this.updateModel(this.absoluteMinValue, this.absoluteMaxValue);
            },
            'change .numeric-parametric-min-input': function () {
                //noinspection JSUnresolvedVariable
                updateRestrictions(this.selectedParametricValues, this.fieldName, this.$minInput.val(), this.$maxInput.val());
                //noinspection JSUnresolvedFunction
                this.drawSelection();
            },
            'change .numeric-parametric-max-input': function () {
                //noinspection JSUnresolvedVariable
                updateRestrictions(this.selectedParametricValues, this.fieldName, this.$minInput.val(), this.$maxInput.val());
                //noinspection JSUnresolvedFunction
                this.drawSelection();
            }
        },

        initialize: function (options) {
            this.queryModel = options.queryModel;
            this.selectedParametricValues = options.selectedParametricValues;
            this.pixelsPerBucket = options.pixelsPerBucket;
            this.viewWidth = options.viewWidth;
            this.fieldName = this.model.id;
            this.widget = numericWidget({});
            this.localBucketingCollection = new (FindBaseCollection.extend({
                url: '../api/public/parametric/buckets'
            }))();

            this.absoluteMinValue = this.model.get('min');
            this.absoluteMaxValue = this.model.get('max');
        },

        render: function () {
            //noinspection JSUnresolvedVariable,JSUnresolvedFunction
            this.$el.empty().append(this.template({
                i18n: i18n,
                fieldName: prettifyFieldName(this.model.get('name')),
                id: _.uniqueId('numeric-parametric-field')
            }));

            //noinspection JSUnresolvedFunction
            this.$minInput = this.$('.numeric-parametric-min-input');
            //noinspection JSUnresolvedFunction
            this.$maxInput = this.$('.numeric-parametric-max-input');

            this.$minInput.val(this.absoluteMinValue);
            this.$maxInput.val(this.absoluteMaxValue);

            const updateCallback = function (x1, x2) {
                // rounding to one decimal place
                this.$minInput.val(Math.max(roundInputNumber(x1), this.model.get('min')));
                this.$maxInput.val(Math.min(roundInputNumber(x2), this.model.get('max')));
            }.bind(this);
            const selectionCallback = function (x1, x2) {
                updateRestrictions(this.selectedParametricValues, this.fieldName, Math.max(x1, this.model.get('max')), Math.min(x2, this.model.get('min')));
            }.bind(this);
            const deselectionCallback = function () {
                this.$minInput.val(this.absoluteMinValue);
                this.$maxInput.val(this.absoluteMaxValue);
                resetSelectedParametricValues(this.selectedParametricValues, this.fieldName);
            }.bind(this);
            const zoomCallback = function (newMin, newMax) {
                this.updateModel(newMin, newMax);
            }.bind(this);
            //noinspection JSUnresolvedFunction
            const buckets = _.filter(this.model.get('values'), function (value) {
                return value.min >= this.model.get('min') && value.max <= this.model.get('max');
            }.bind(this));
            //noinspection JSUnresolvedFunction
            this.graph = this.widget.drawGraph({
                chart: this.$('.chart')[0],
                data: {
                    buckets: buckets,
                    bucketSize: this.model.get('bucketSize'),
                    maxCount: _.max(_.pluck(buckets, 'count')),
                    minValue: this.model.get('min'),
                    maxValue: this.model.get('max')
                },
                updateCallback: updateCallback,
                selectionCallback: selectionCallback,
                deselectionCallback: deselectionCallback,
                zoomCallback: zoomCallback,
                xRange: this.viewWidth,
                yRange: GRAPH_HEIGHT,
                tooltip: i18n['search.numericParametricFields.tooltip']
            });

            this.drawSelection();
        },

        drawSelection: function () {
            const graph = this.graph;

            this.selectedParametricValues.where({
                field: this.fieldName
            }).forEach(function (restriction) {
                const range = restriction.get('range');
                if (range) {
                    this.$minInput.val(roundInputNumber(range[0]));
                    this.$maxInput.val(roundInputNumber(range[1]));

                    graph.selectionRect.init(graph.chart, GRAPH_HEIGHT, graph.scale.barWidth(range[0]));
                    graph.selectionRect.update(graph.scale.barWidth(range[1]));
                    graph.selectionRect.focus();
                }
            }, this);
        },

        updateModel: function (newMin, newMax) {
            // immediate update, just rescaling the existing buckets
            this.model.set({
                min: newMin,
                max: newMax
            });

            // proper update, regenerating the buckets from the given bounds
            //noinspection JSUnresolvedVariable
            this.localBucketingCollection.fetch({
                data: {
                    fieldNames: [this.model.get('id')],
                    databases: this.queryModel.get('indexes'),
                    queryText: this.queryModel.get('queryText'),
                    targetNumberOfBuckets: [Math.floor(this.$el.width() / this.pixelsPerBucket)],
                    bucketMin: [newMin],
                    bucketMax: [newMax]
                },
                reset: true,
                remove: true,
                success: function() {
                    const result = this.localBucketingCollection.models[0];
                    this.model.set({
                        count: result.get('count'),
                        min: result.get('min'),
                        max: result.get('max'),
                        bucketSize: result.get('bucketSize'),
                        values: result.get('values')
                    });
                }.bind(this)
            });
        }
    });
});<|endoftext|>


<commit_before>/**
 * @file 图层管理
 * @author mj(zoumiaojiang@gmail.com)
 */

'use strict';

const Router = require('koa-router');
const router = new Router();
const Model = require('../model/Layer.js');
const randomColor = require('randomcolor');

router.prefix('/layers');

router.post('/add', function* () {

    const dbModels = yield this.getDBModels();
    const model = Model.getInstance(dbModels);
    const params = this.request.body;
    const sourceid = params.sourceid;
    const projectid = params.projectid;
    const parentid = '';
    const tag = randomColor({
        luminosity: 'random',
        hue: 'random'
    });
    const position = {
        x: { value: 0, fx: 'linear' },
        y: { value: 0, fx: 'linear' }
    };

    const rotate = {
        x: { value: 0, fx: 'linear' },
        y: { value: 0, fx: 'linear' }
    };

    const scale = {
        x: { value: 1, fx: 'linear' },
        y: { value: 1, fx: 'linear' }
    };

    const opacity = { value: 100, fx: 'linear' };

    const skew = {
        x: { value: 1, fx: 'linear' },
        y: { value: 1, fx: 'linear' }
    };

    const css = {};

    const namere = /^(.*?)(\d*?)?$/i;
    let name = '图层';
    let zIndex = 0;
    let alreadyHasLayer = yield model.alreadyHasLayer(projectid, name);
    let originLayerName;
    let num = 0;
    while (alreadyHasLayer) {
        let arr = name.match(namere);
        originLayerName = arr[1];
        num = arr[2] || 0;
        num++;
        name = `${originLayerName}${num}`;
        alreadyHasLayer = yield model.alreadyHasLayer(projectid, name);
    }
    zIndex = num * 10;

    yield model.add({
        sourceid,
        projectid,
        parentid,
        tag,
        name,
        position,
        scale,
        rotate,
        opacity,
        zIndex,
        skew,
        css,
        statusLayer: false,
        statusView: true,
        statusProp: false,
        statusDview: false,
        statusLock: false
    });

    const data = yield model.fetchLayers(projectid);

    try {
        this.body = {
            status: 0,
            data
        }
    }
    catch (e) {
        this.body = {
            status: 0,
            statusInfo: ''
        };
    }
});

router.post('/update', function* () {
    const dbModels = yield this.getDBModels();
    const model = Model.getInstance(dbModels);
    const params = this.request.body;

    yield model.update(params);

    this.body = {
        status: 0
    }
});

module.exports = router;
<commit_msg>add default value for size<commit_after>/**
 * @file 图层管理
 * @author mj(zoumiaojiang@gmail.com)
 */

'use strict';

const Router = require('koa-router');
const router = new Router();
const Model = require('../model/Layer.js');
const randomColor = require('randomcolor');

router.prefix('/layers');

router.post('/add', function* () {

    const dbModels = yield this.getDBModels();
    const model = Model.getInstance(dbModels);
    const params = this.request.body;
    const sourceid = params.sourceid;
    const projectid = params.projectid;
    const parentid = '';
    const tag = randomColor({
        luminosity: 'random',
        hue: 'random'
    });

    const size = {
        x: { value: 100, fx: 'linear' },
        y: { value: 100, fx: 'linear' }
    };

    const position = {
        x: { value: 0, fx: 'linear' },
        y: { value: 0, fx: 'linear' }
    };

    const rotate = {
        x: { value: 0, fx: 'linear' },
        y: { value: 0, fx: 'linear' },
        z: { value: 0, fx: 'linear' }
    };

    const scale = {
        x: { value: 1, fx: 'linear' },
        y: { value: 1, fx: 'linear' }
    };

    const opacity = { value: 100, fx: 'linear' };

    const skew = {
        x: { value: 1, fx: 'linear' },
        y: { value: 1, fx: 'linear' }
    };

    const css = {};

    const namere = /^(.*?)(\d*?)?$/i;
    let name = '图层';
    let zIndex = 0;
    let alreadyHasLayer = yield model.alreadyHasLayer(projectid, name);
    let originLayerName;
    let num = 0;
    while (alreadyHasLayer) {
        let arr = name.match(namere);
        originLayerName = arr[1];
        num = arr[2] || 0;
        num++;
        name = `${originLayerName}${num}`;
        alreadyHasLayer = yield model.alreadyHasLayer(projectid, name);
    }
    zIndex = num * 10;

    yield model.add({
        sourceid,
        projectid,
        parentid,
        tag,
        name,
        position,
        scale,
        rotate,
        opacity,
        zIndex,
        skew,
        size,
        css,
        statusLayer: false,
        statusView: true,
        statusProp: false,
        statusDview: false,
        statusLock: false
    });

    const data = yield model.fetchLayers(projectid);

    try {
        this.body = {
            status: 0,
            data
        }
    }
    catch (e) {
        this.body = {
            status: 0,
            statusInfo: ''
        };
    }
});

router.post('/update', function* () {
    const dbModels = yield this.getDBModels();
    const model = Model.getInstance(dbModels);
    const params = this.request.body;

    yield model.update(params);

    this.body = {
        status: 0
    }
});

module.exports = router;
<|endoftext|>


<commit_before>import * as globalEvents from '../../src/notebook/global-events';
import * as kernel from '../../src/notebook/kernel/shutdown';

import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

chai.use(sinonChai);

import { dummyStore } from '../utils';

describe('unload', () => {
  it('should force a kernel shutdown', () => {
    const store = dummyStore();
    const forceShutdownKernel = sinon.spy(kernel, 'forceShutdownKernel');

    globalEvents.unload(store);

    forceShutdownKernel.restore();
    expect(forceShutdownKernel).to.be.called;
  });
});
<commit_msg>Add tests for global-events<commit_after>import * as globalEvents from '../../src/notebook/global-events';
import * as kernel from '../../src/notebook/kernel/shutdown';

import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

chai.use(sinonChai);

import { dummyStore } from '../utils';

describe('unload', () => {
  it('should force a kernel shutdown', () => {
    const store = dummyStore();
    const forceShutdownKernel = sinon.spy(kernel, 'forceShutdownKernel');

    globalEvents.unload(store);

    forceShutdownKernel.restore();
    expect(forceShutdownKernel).to.be.called;
  });
});

describe('initGlobalHandlers', () => {
  it('adds an unload poperty to the window object', () => {
    const store = dummyStore();
    globalEvents.initGlobalHandlers(store);
    expect(global.window.onunload).to.not.be.undefined;
  });
});
<|endoftext|>


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

    Editors.directive('templateBlockEditor', ['$compile', 'limitToWithEllipsisFilter', 'TemplateBlockService', 'MeasureService', 'AssessmentVariableManager',
                                              function($compile, limitToWithEllipsisFilter, TemplateBlockService, MeasureService, AssessmentVariableManager) {

        // TODO Move to service or domain object to be shared and encapsulated elsewhere?
	    var blockTypes = [
		    { name: 'If', value: 'if' },
		    { name: 'Else If', value: 'elseif' },
		    { name: 'Else', value: 'else' },
		    { name: 'Text', value: 'text' },
			{ name: 'Table', value: 'table' }
	    ];

        function getBlockTypes(parentBlock) {
            var types = [blockTypes[0], blockTypes[3], blockTypes[4]];

            //dropping at root
            if(!Object.isDefined(parentBlock) || parentBlock.type == "else"){
                return types;
            }

            var grandParentType = parentBlock.getParent() ? parentBlock.getParent().type : null;
            var ifParent = null;
            if(parentBlock.type == "if"){
                ifParent = parentBlock;
            }
            else if(grandParentType == "if"){
                ifParent = parentBlock.getParent();
            }

            // if we can't find a parent block that is an If then if and text
            if(!ifParent){
                return types;
            }

            //if the parent is text and this isn't the last text, then we don't include else or elseif
            var lastText;
            if(parentBlock.type == 'text'){
                lastText = parentBlock.getParent().lastText().block;

                if(!lastText.equals(parentBlock)){
                    return types;
                }
            }

            //at this point we know that elseif is going to make it
            types.push(blockTypes[1]);

            // if there is another else then don't allow another one
            if(ifParent.getElse().index != -1){
                return types;
            }

            var lastElseIf = ifParent.lastElseIf().block;

            //if parent is text and there is an elseIf not include else
            if(parentBlock.type == 'text' && Object.isDefined(lastElseIf)){
                return types;
            }

            //if the parent is an elseif and this isn't the last elseif, then we don't include else
            if(parentBlock.type == 'elseif' && !lastElseIf.equals(parentBlock)){
                return types;
            }

            // add else
            types.push(blockTypes[2]);

            return types;
        }

        return {
            restrict: 'E',
	        require: '^form',
            scope: {
                block: '=',
                assessmentVariables: '=',
	            showValidationMessages: '=',
	            enableTypes: '=enableTypesDropdown'
            },
            templateUrl: 'resources/editors/views/templates/templateblockeditor.html',
            link: function(scope, element, attrs, formController) {

                /* Temporarily disabled until further notice: 11/03/14
                 var collectionTemplate = '<template-block-editor block="member" ng-repeat="member in block.children | limitTo:2" assessment-variables="assessmentVariables"></template-block-editor>';
                  */

                /*
                 The compile function cannot handle directives that recursively use themselves
                 in their own templates or compile functions. Compiling these directives results
                 in an infinite loop and a stack overflow errors. This can be avoided by manually
                 using $compile in the postLink function to imperatively compile a directive's template
                 */
				/* Temporarily disabled until further notice: 11/03/14
                $compile(collectionTemplate)(scope, function (clonedTemplate, scope) {
                    // Append the template and pass in the cloned scope
                    element.append(clonedTemplate);
                });
                */

	            scope.templateBlockEditorForm = formController;

	            scope.showValidationMessages = false;

                scope.blockTypes = (scope.block) ? getBlockTypes(scope.block.getParent()) : blockTypes;

                // TODO Move to service to be shared elsewhere?
                scope.operators = [
	                { name: 'Equals',                    value: 'eq',    categories: ['numerical'] },
	                { name: 'Doesn\'t Equals',           value: 'neq',   categories: ['numerical'] },
	                { name: 'Is Less Than',              value: 'lt',    categories: ['numerical'] },
	                { name: 'Is Greater Than',           value: 'gt',    categories: ['numerical'] },
	                { name: 'Is Less Than or Equals',    value: 'lte',   categories: ['numerical'] },
	                { name: 'Is Greater Than or Equals', value: 'gte',   categories: ['numerical'] },

	                { name: 'Was Answered',     value: 'answered',   categories: ['question', 'table'] },
	                { name: 'Wasn\'t Answered', value: 'nanswered', categories: ['question', 'table'] },

					{ name: 'Was Answer None',     value: 'none',   categories: ['table'] },
					{ name: 'Wasn\'t Answer None', value: 'nnone', categories: ['table'] },

	                { name: 'Has Result',      value: 'result',   categories: ['formula', 'matrix'] },
	                { name: 'Has No Result',   value: 'nresult', categories: ['formula', 'matrix'] },

	                { name: 'Response is',     value: 'response',  categories: ['select'] },
	                { name: 'Response isn\'t',  value: 'nresponse', categories: ['select'] }
                ];

                var filterOperators = function(operator) {
                    var includeOperator = false;

	                if (operator && this.type) {
		                if(_.contains(operator.categories, 'numerical')) {
							if (this.measureTypeId === 4 && this.transformations && this.transformations[0].name === 'numberOfEntries') {
								includeOperator = true;
							}
			                if(this.type.toUpperCase() === 'CUSTOM' || this.type.toUpperCase() === 'FORMULA') {
				                includeOperator = true;
			                } else if (this.type.toUpperCase() === "QUESTION" && this.measureTypeId === 1) {
				                includeOperator = true;
			                }
		                } else if((this.type.toUpperCase() === 'QUESTION') && _.contains(operator.categories, 'question')) {
			                includeOperator = true;
		                } else if((this.type.toUpperCase() === 'FORMULA') && _.contains(operator.categories, 'formula')) {
			                includeOperator = true;
		                } else if((this.type.toUpperCase() === 'QUESTION') && _.contains(operator.categories, 'select')) {
			                if(Object.isDefined(this.measureTypeId) && (this.measureTypeId === 2 || this.measureTypeId === 3)){
				                includeOperator = true;
			                }
		                }
						else if((this.measureTypeId === 4) && _.contains(operator.categories, 'table')) {
							includeOperator = true;
						}
						else if((this.type.toUpperCase() === 'MATRIX') && _.contains(operator.categories, 'matrix')) {
							includeOperator = true;
						} else if (this.type.toUpperCase() === 'CUSTOM' && _.contains(operator.categories, 'formula')) {
							includeOperator = true;
						}
	                }
                    return includeOperator;
                };

                scope.$watch('block.type', function(newValue, oldValue) {
                    if(newValue !== null && newValue !== oldValue){
                        scope.block.reset();
                    }
                });

	            scope.filterOperators = function() {
		            return filterOperators;
	            };

                scope.addBlock = function(selectedBlock) {
                    selectedBlock.children = selectedBlock.children || [];
                    selectedBlock.children.push(TemplateBlockService.newBlock(EScreeningDashboardApp.models.TemplateBlock.RightLeftMinimumConfig));
                };

                scope.addAndConditionBlock = function(selectedBlock, form) {
	                scope.showValidationMessages = true;
	                if (form.$valid) {
		                selectedBlock.conditions = selectedBlock.conditions || [];
		                selectedBlock.conditions.push(new EScreeningDashboardApp.models.TemplateCondition(EScreeningDashboardApp.models.TemplateCondition.AndConditionMinimumConfig));
	                }
                };

                scope.addOrConditionBlock = function(selectedBlock, form) {
	                scope.showValidationMessages = true;
	                if (form.$valid) {
		                selectedBlock.conditions = selectedBlock.conditions || [];
		                selectedBlock.conditions.push(new EScreeningDashboardApp.models.TemplateCondition(EScreeningDashboardApp.models.TemplateCondition.OrConditionMinimumConfig));
	                }
                };

                scope.removeConditionBlock = function(selectedBlockConditions, selectedCondition) {
                    var selectedConditionIndex = selectedBlockConditions.indexOf(selectedCondition);
                    if(selectedConditionIndex > -1) {
                        selectedBlockConditions.splice(selectedConditionIndex, 1);
                    }
                };

	            scope.showBlockConditionRight = function(operatorValue) {
		            var result = (operatorValue);
		            angular.forEach(scope.operators, function(operator) {
			            if (operator.value === "" + operatorValue) {
				            if (_.contains(operator.categories, 'formula') || _.contains(operator.categories, 'question') || _.contains(operator.categories, 'table')) {
					            result = false;
					            return;
				            }
			            }
		            });
		            return result;
	            };

	            scope.updateLogicalOptions = function(item) {

		            var av = (item.left && item.left.content) ? item.left.content : {};

		            // (Re-)initialize the answers and validations
		            item.measureAnswers = [];
		            item.measureValidations = {};

		            // Filter the operators and add the results to the item
		            item.operators = scope.operators.filter(filterOperators, av);

		            scope.dt = new Date();

		            if (av.type && av.type.toLowerCase() === "formula") {
			            // Assessment Variable is a formula, which must be a number
			            item.measureValidations.number = 'number';
			            item.measureValidations.minValue = '';
			            item.measureValidations.maxValue = '';

		            } else if (av.measureId && av.measureTypeId) {
		                // Assessment variable is a question

			            if (av.measureTypeId == 1) {

				            // Get the validations for freetext
				            MeasureService.one(av.measureId).getList('validations').then(function (validations) {
					            angular.forEach(validations, function(validation) {
									AssessmentVariableManager.setValidations(item.measureValidations, validation);
					            });
				            });

			            } else if (av.measureTypeId === 2 || av.measureTypeId === 3) {

				            // Get the answer list for multi or single select questions
				            MeasureService.one(av.measureId).getList('answers').then(function (answers) {
					            item.measureAnswers = answers;
				            });

				            // Ensure right variable is numeric
				            if (item.right) item.right.content = parseInt(item.right.content);
			            }

		            }

	            };

	            if(scope.block) {
		            scope.updateLogicalOptions(scope.block);
	            }
            }
        };

    }]);
})(angular);
<commit_msg>t568 Updated matrix with transformations to include has result and has no result<commit_after>(function(angular) {
    "use strict";

    Editors.directive('templateBlockEditor', ['$compile', 'limitToWithEllipsisFilter', 'TemplateBlockService', 'MeasureService', 'AssessmentVariableManager',
                                              function($compile, limitToWithEllipsisFilter, TemplateBlockService, MeasureService, AssessmentVariableManager) {

        // TODO Move to service or domain object to be shared and encapsulated elsewhere?
	    var blockTypes = [
		    { name: 'If', value: 'if' },
		    { name: 'Else If', value: 'elseif' },
		    { name: 'Else', value: 'else' },
		    { name: 'Text', value: 'text' },
			{ name: 'Table', value: 'table' }
	    ];

        function getBlockTypes(parentBlock) {
            var types = [blockTypes[0], blockTypes[3], blockTypes[4]];

            //dropping at root
            if(!Object.isDefined(parentBlock) || parentBlock.type == "else"){
                return types;
            }

            var grandParentType = parentBlock.getParent() ? parentBlock.getParent().type : null;
            var ifParent = null;
            if(parentBlock.type == "if"){
                ifParent = parentBlock;
            }
            else if(grandParentType == "if"){
                ifParent = parentBlock.getParent();
            }

            // if we can't find a parent block that is an If then if and text
            if(!ifParent){
                return types;
            }

            //if the parent is text and this isn't the last text, then we don't include else or elseif
            var lastText;
            if(parentBlock.type == 'text'){
                lastText = parentBlock.getParent().lastText().block;

                if(!lastText.equals(parentBlock)){
                    return types;
                }
            }

            //at this point we know that elseif is going to make it
            types.push(blockTypes[1]);

            // if there is another else then don't allow another one
            if(ifParent.getElse().index != -1){
                return types;
            }

            var lastElseIf = ifParent.lastElseIf().block;

            //if parent is text and there is an elseIf not include else
            if(parentBlock.type == 'text' && Object.isDefined(lastElseIf)){
                return types;
            }

            //if the parent is an elseif and this isn't the last elseif, then we don't include else
            if(parentBlock.type == 'elseif' && !lastElseIf.equals(parentBlock)){
                return types;
            }

            // add else
            types.push(blockTypes[2]);

            return types;
        }

        return {
            restrict: 'E',
	        require: '^form',
            scope: {
                block: '=',
                assessmentVariables: '=',
	            showValidationMessages: '=',
	            enableTypes: '=enableTypesDropdown'
            },
            templateUrl: 'resources/editors/views/templates/templateblockeditor.html',
            link: function(scope, element, attrs, formController) {

                /* Temporarily disabled until further notice: 11/03/14
                 var collectionTemplate = '<template-block-editor block="member" ng-repeat="member in block.children | limitTo:2" assessment-variables="assessmentVariables"></template-block-editor>';
                  */

                /*
                 The compile function cannot handle directives that recursively use themselves
                 in their own templates or compile functions. Compiling these directives results
                 in an infinite loop and a stack overflow errors. This can be avoided by manually
                 using $compile in the postLink function to imperatively compile a directive's template
                 */
				/* Temporarily disabled until further notice: 11/03/14
                $compile(collectionTemplate)(scope, function (clonedTemplate, scope) {
                    // Append the template and pass in the cloned scope
                    element.append(clonedTemplate);
                });
                */

	            scope.templateBlockEditorForm = formController;

	            scope.showValidationMessages = false;

                scope.blockTypes = (scope.block) ? getBlockTypes(scope.block.getParent()) : blockTypes;

                // TODO Move to service to be shared elsewhere?
                scope.operators = [
	                { name: 'Equals',                    value: 'eq',    categories: ['numerical'] },
	                { name: 'Doesn\'t Equals',           value: 'neq',   categories: ['numerical'] },
	                { name: 'Is Less Than',              value: 'lt',    categories: ['numerical'] },
	                { name: 'Is Greater Than',           value: 'gt',    categories: ['numerical'] },
	                { name: 'Is Less Than or Equals',    value: 'lte',   categories: ['numerical'] },
	                { name: 'Is Greater Than or Equals', value: 'gte',   categories: ['numerical'] },

	                { name: 'Was Answered',     value: 'answered',   categories: ['question', 'table'] },
	                { name: 'Wasn\'t Answered', value: 'nanswered', categories: ['question', 'table'] },

					{ name: 'Was Answer None',     value: 'none',   categories: ['table'] },
					{ name: 'Wasn\'t Answer None', value: 'nnone', categories: ['table'] },

	                { name: 'Has Result',      value: 'result',   categories: ['formula', 'matrix'] },
	                { name: 'Has No Result',   value: 'nresult', categories: ['formula', 'matrix'] },

	                { name: 'Response is',     value: 'response',  categories: ['select'] },
	                { name: 'Response isn\'t',  value: 'nresponse', categories: ['select'] }
                ];

                var filterOperators = function(operator) {
                    var includeOperator = false;

	                if (operator && this.type) {
		                if(_.contains(operator.categories, 'numerical')) {
							if (this.measureTypeId === 4 && this.transformations && this.transformations[0].name === 'numberOfEntries') {
								includeOperator = true;
							}
			                if(this.type.toUpperCase() === 'CUSTOM' || this.type.toUpperCase() === 'FORMULA') {
				                includeOperator = true;
			                } else if (this.type.toUpperCase() === "QUESTION" && this.measureTypeId === 1) {
				                includeOperator = true;
			                }
		                } else if((this.type.toUpperCase() === 'QUESTION') && _.contains(operator.categories, 'question')) {
			                includeOperator = true;
		                } else if((this.type.toUpperCase() === 'FORMULA') && _.contains(operator.categories, 'formula')) {
			                includeOperator = true;
		                } else if((this.type.toUpperCase() === 'QUESTION') && _.contains(operator.categories, 'select')) {
			                if(Object.isDefined(this.measureTypeId) && (this.measureTypeId === 2 || this.measureTypeId === 3)){
				                includeOperator = true;
			                }
		                }
						else if((this.measureTypeId === 4) && _.contains(operator.categories, 'table')) {
							includeOperator = true;
						}
						else if((this.getMeasureTypeName() === 'single-matrix' || this.getMeasureTypeName() === 'multi-matrix') && _.contains(operator.categories, 'matrix')) {
							includeOperator = true;
						} else if (this.type.toUpperCase() === 'CUSTOM' && _.contains(operator.categories, 'formula')) {
							includeOperator = true;
						}
	                }
                    return includeOperator;
                };

                scope.$watch('block.type', function(newValue, oldValue) {
                    if(newValue !== null && newValue !== oldValue){
                        scope.block.reset();
                    }
                });

	            scope.filterOperators = function() {
		            return filterOperators;
	            };

                scope.addBlock = function(selectedBlock) {
                    selectedBlock.children = selectedBlock.children || [];
                    selectedBlock.children.push(TemplateBlockService.newBlock(EScreeningDashboardApp.models.TemplateBlock.RightLeftMinimumConfig));
                };

                scope.addAndConditionBlock = function(selectedBlock, form) {
	                scope.showValidationMessages = true;
	                if (form.$valid) {
		                selectedBlock.conditions = selectedBlock.conditions || [];
		                selectedBlock.conditions.push(new EScreeningDashboardApp.models.TemplateCondition(EScreeningDashboardApp.models.TemplateCondition.AndConditionMinimumConfig));
	                }
                };

                scope.addOrConditionBlock = function(selectedBlock, form) {
	                scope.showValidationMessages = true;
	                if (form.$valid) {
		                selectedBlock.conditions = selectedBlock.conditions || [];
		                selectedBlock.conditions.push(new EScreeningDashboardApp.models.TemplateCondition(EScreeningDashboardApp.models.TemplateCondition.OrConditionMinimumConfig));
	                }
                };

                scope.removeConditionBlock = function(selectedBlockConditions, selectedCondition) {
                    var selectedConditionIndex = selectedBlockConditions.indexOf(selectedCondition);
                    if(selectedConditionIndex > -1) {
                        selectedBlockConditions.splice(selectedConditionIndex, 1);
                    }
                };

	            scope.showBlockConditionRight = function(operatorValue) {
		            var result = (operatorValue);
		            angular.forEach(scope.operators, function(operator) {
			            if (operator.value === "" + operatorValue) {
				            if (_.contains(operator.categories, 'formula') || _.contains(operator.categories, 'question') || _.contains(operator.categories, 'table')) {
					            result = false;
					            return;
				            }
			            }
		            });
		            return result;
	            };

	            scope.updateLogicalOptions = function(item) {

		            var av = (item.left && item.left.content) ? item.left.content : {};

		            // (Re-)initialize the answers and validations
		            item.measureAnswers = [];
		            item.measureValidations = {};

		            // Filter the operators and add the results to the item
		            item.operators = scope.operators.filter(filterOperators, av);

		            scope.dt = new Date();

		            if (av.type && av.type.toLowerCase() === "formula") {
			            // Assessment Variable is a formula, which must be a number
			            item.measureValidations.number = 'number';
			            item.measureValidations.minValue = '';
			            item.measureValidations.maxValue = '';

		            } else if (av.measureId && av.measureTypeId) {
		                // Assessment variable is a question

			            if (av.measureTypeId == 1) {

				            // Get the validations for freetext
				            MeasureService.one(av.measureId).getList('validations').then(function (validations) {
					            angular.forEach(validations, function(validation) {
									AssessmentVariableManager.setValidations(item.measureValidations, validation);
					            });
				            });

			            } else if (av.measureTypeId === 2 || av.measureTypeId === 3) {

				            // Get the answer list for multi or single select questions
				            MeasureService.one(av.measureId).getList('answers').then(function (answers) {
					            item.measureAnswers = answers;
				            });

				            // Ensure right variable is numeric
				            if (item.right) item.right.content = parseInt(item.right.content);
			            }

		            }

	            };

	            if(scope.block) {
		            scope.updateLogicalOptions(scope.block);
	            }
            }
        };

    }]);
})(angular);
<|endoftext|>


<commit_before>import { getParentSvg } from './utils.js';

const normalizeEvent = e => {
  if (e.x == undefined) e.x = e.clientX;
  if (e.y == undefined) e.y = e.clientY;
};

export function register () {
  const store = {};
  const svg = this._svg;
  // DOM Events
  svg.addEventListener('mousedown', e => {
    normalizeEvent(e);
    this.lastSelected = null;
    const { target } = e;

    if (target.type === 'wire') {
      this.lastSelected = target.wrapper;
      return this.selectedWire = target.wrapper;
    }

    if (target.type === 'port' && target.direction === 'out') {
      return this.startAttach(target);
    }

    const parentSvg = getParentSvg(target);
    const shouldCapture = tagName => !['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(tagName);
    if (shouldCapture(target.tagName) && parentSvg && parentSvg.type == 'node') {
      const nodeNode = parentSvg;
      console.debug('Node selected:', nodeNode, 'Triggered by: ', target);
      var wrapper = nodeNode.wrapper;
      this.lastSelected = wrapper;
      this.dragging = nodeNode;
      var SVGbox = wrapper._svg.getBoundingClientRect();
      const offset = {
        x: e.x - SVGbox.left,
        y: e.y - SVGbox.top,
      };
      this._aux.mouseDown = { x: offset.x - wrapper.x, y: offset.y - wrapper.y };
      this._svg.appendChild(this.dragging);
      wrapper.wires.forEach(wire => this._svg.appendChild(wire._el));
    }

  }, false);

  document.addEventListener('keydown', e => {
    if (e.keyCode == 46) {
      console.debug('deleting', this.lastSelected);
      this.lastSelected.delete();
      // @TODO should remove from state (brick, wire, port)
    }
  }, false);

  svg.addEventListener('mouseup', e => {
    this.dragging = null;
    if(e.target.type === 'port')
      return this.endAttach(e.target);

    if(this.isState('attaching')) {
      this.setState(null)
      svg.removeChild(this._aux['wire']._el);
    }
  });

  svg.addEventListener('mousemove', e => {
    normalizeEvent(e);

    return this.attachMove(e);
  });

  svg.addEventListener('mousemove', e => {
    normalizeEvent(e);

    if (this.dragging) {
      var wrapper = this.dragging.wrapper;
      var SVGbox = wrapper._svg.getBoundingClientRect();
      var OffsetX = e.x - SVGbox.left;
      var OffsetY =  e.y - SVGbox.top;

      var firstState = this._aux.mouseDown;
      wrapper.x = OffsetX - firstState.x;
      wrapper.y = OffsetY - firstState.y;
      wrapper.updateWires();
    }
  });

  return store;
}<commit_msg>Add zoom wheel event<commit_after>import { getParentSvg } from './utils.js';

const normalizeEvent = e => {
  if (e.x == undefined) e.x = e.clientX;
  if (e.y == undefined) e.y = e.clientY;
};

export function register () {
  const store = {};
  const svg = this._svg;
  // DOM Events
  svg.addEventListener('mousedown', e => {
    normalizeEvent(e);
    this.lastSelected = null;
    const { target } = e;

    if (target.type === 'wire') {
      this.lastSelected = target.wrapper;
      return this.selectedWire = target.wrapper;
    }

    if (target.type === 'port' && target.direction === 'out') {
      return this.startAttach(target);
    }

    const parentSvg = getParentSvg(target);
    const shouldCapture = tagName => !['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(tagName);
    if (shouldCapture(target.tagName) && parentSvg && parentSvg.type == 'node') {
      const nodeNode = parentSvg;
      console.debug('Node selected:', nodeNode, 'Triggered by: ', target);
      var wrapper = nodeNode.wrapper;
      this.lastSelected = wrapper;
      this.dragging = nodeNode;
      var SVGbox = wrapper._svg.getBoundingClientRect();
      const offset = {
        x: e.x - SVGbox.left,
        y: e.y - SVGbox.top,
      };
      this._aux.mouseDown = { x: offset.x - wrapper.x, y: offset.y - wrapper.y };
      this._svg.appendChild(this.dragging);
      wrapper.wires.forEach(wire => this._svg.appendChild(wire._el));
    }

  }, false);

  document.addEventListener('keydown', e => {
    if (e.keyCode == 46) {
      console.debug('deleting', this.lastSelected);
      this.lastSelected.delete();
      // @TODO should remove from state (brick, wire, port)
    }
  }, false);

  svg.addEventListener('mouseup', e => {
    this.dragging = null;
    if(e.target.type === 'port')
      return this.endAttach(e.target);

    if(this.isState('attaching')) {
      this.setState(null)
      svg.removeChild(this._aux['wire']._el);
    }
  });

  svg.addEventListener('mousemove', e => {
    normalizeEvent(e);

    return this.attachMove(e);
  });

  svg.addEventListener('mousemove', e => {
    normalizeEvent(e);

    if (this.dragging) {
      const wrapper = this.dragging.wrapper;
      const SVGbox = wrapper._svg.getBoundingClientRect();
      const firstState = this._aux.mouseDown;
      let OffsetX = e.x - SVGbox.left;
      let OffsetY =  e.y - SVGbox.top;

      wrapper.x = OffsetX - firstState.x;
      wrapper.y = OffsetY - firstState.y;
      wrapper.updateWires();
    }
  });

  let zoom = 1;
  const zoomVelocity = 0.1;
  window.addEventListener("wheel", event => {
    const delta = Math.sign(event.deltaY);
    console.info('MouseWheel delta', delta);
    zoom = zoom - (zoomVelocity * delta);
    svg.style.transform = `scale(${zoom})`;
  });

  return store;
}<|endoftext|>


<commit_before>var assert = require("assert");
var state_machine = require("../../lib/state_machine");
var states = require("../../lib/states");
var dummy_api = require("../../lib/dummy_api");

function SingleStateIm(state) {
    this.state = state;
    this.states = new state_machine.StateCreator(state ? state.name : "start");
    this.api = new dummy_api.DummyApi();
    this.im = new state_machine.InteractionMachine(this.api, this.states);
    if (state) {
        this.states.add_state(state);
    }
}

describe("test InteractionMachine", function() {
    it("should be creatable", function() {
        var sim = new SingleStateIm();
        assert.ok(sim.im);
    });
    it("should implement switch_state", function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "next", "Foo"));
        var done = 0;
        sim.im.user = {};
        sim.im.config = {};
        var p = sim.im.switch_state("start");
        p.add_callback(function () {done += 1;});
        assert.equal(done, 1);
        assert.equal(sim.im.current_state, sim.state);
    });
    it("should throw an error on duplicate states", function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "next", "Foo"));
        assert.throws(
            function () {
                sim.states.add_state(
                    new states.FreeText("start", "next", "Duplicate"));
            },
            states.StateError);
    });
    it("should throw an error on switching to unknown states", function () {
        var sim = new SingleStateIm();
        sim.im.user = {};
        sim.im.config = {};
        assert.throws(
            function () {
                sim.im.switch_state("unknown");
            },
            states.StateError);
    });
    it('should generate an event after a config_read event', function() {
        var states = new state_machine.StateCreator("start");
        var store = {};
        states.on_config_read = function(event) {
            store.event = event;
        };
        assert.equal(store.config, undefined);
        states.on_event({event: 'config_read', config: {foo: 'bar'}});
        assert.equal(store.event.event, 'config_read');
        assert.equal(store.event.config.foo, 'bar');
    });
    it('should generate an event after an inbound_event event', function() {
        var states = new state_machine.StateCreator("start");
        var store = {};
        states.on_inbound_event = function(event) {
            store.event = event;
        };
        assert.equal(store.config, undefined);
        states.on_event({event: 'inbound_event'});
        assert.equal(store.event.event, 'inbound_event');
    });
    it('should fire an inbound_event event on_inbound_message', function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "start", "Foo"));
        var store = {};
        sim.states.on_inbound_event = function(event) {
            store.event = event;
        };
        sim.im.on_inbound_event({
            cmd: "inbound-event",
            msg: {
                session_event: "new"
            }
        });
        var event_data = store.event.data.event;
        assert.equal(event_data.session_event, 'new');
    });
    it('should fire a config_read event on_inbound_message', function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "start", "Foo"));
        var store = {};
        sim.states.on_config_read = function(event) {
            store.event = event;
        };
        sim.im.fetch_config_value = function(key, json, done) {
            assert.equal(key, 'config');
            assert.ok(json);
            done({'sample': 'config'});
        };
        sim.im.on_inbound_message({
            cmd: "inbound-message",
            msg: {
                from_addr: "from_addr",
                content: "content",
                message_id: "message_id",
                session_event: "continue"
            }
        });
        var config = store.event.data.config;
        assert.equal(config.sample, 'config');
    });
});

describe("test State", function() {
    // TODO:
});

describe("test StateError", function() {
    it("should be creatable", function() {
        var se = new states.StateError("msg");
        assert.equal(se.message, "msg");
    });
});

describe("test ChoiceState", function() {
});

describe("test FreeText", function() {
});

describe("test EndState", function() {
});<commit_msg>s/on_inbound_message/on_inbound_event/<commit_after>var assert = require("assert");
var state_machine = require("../../lib/state_machine");
var states = require("../../lib/states");
var dummy_api = require("../../lib/dummy_api");

function SingleStateIm(state) {
    this.state = state;
    this.states = new state_machine.StateCreator(state ? state.name : "start");
    this.api = new dummy_api.DummyApi();
    this.im = new state_machine.InteractionMachine(this.api, this.states);
    if (state) {
        this.states.add_state(state);
    }
}

describe("test InteractionMachine", function() {
    it("should be creatable", function() {
        var sim = new SingleStateIm();
        assert.ok(sim.im);
    });
    it("should implement switch_state", function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "next", "Foo"));
        var done = 0;
        sim.im.user = {};
        sim.im.config = {};
        var p = sim.im.switch_state("start");
        p.add_callback(function () {done += 1;});
        assert.equal(done, 1);
        assert.equal(sim.im.current_state, sim.state);
    });
    it("should throw an error on duplicate states", function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "next", "Foo"));
        assert.throws(
            function () {
                sim.states.add_state(
                    new states.FreeText("start", "next", "Duplicate"));
            },
            states.StateError);
    });
    it("should throw an error on switching to unknown states", function () {
        var sim = new SingleStateIm();
        sim.im.user = {};
        sim.im.config = {};
        assert.throws(
            function () {
                sim.im.switch_state("unknown");
            },
            states.StateError);
    });
    it('should generate an event after a config_read event', function() {
        var states = new state_machine.StateCreator("start");
        var store = {};
        states.on_config_read = function(event) {
            store.event = event;
        };
        assert.equal(store.config, undefined);
        states.on_event({event: 'config_read', config: {foo: 'bar'}});
        assert.equal(store.event.event, 'config_read');
        assert.equal(store.event.config.foo, 'bar');
    });
    it('should generate an event after an inbound_event event', function() {
        var states = new state_machine.StateCreator("start");
        var store = {};
        states.on_inbound_event = function(event) {
            store.event = event;
        };
        assert.equal(store.config, undefined);
        states.on_event({event: 'inbound_event'});
        assert.equal(store.event.event, 'inbound_event');
    });
    it('should fire an inbound_event event on_inbound_event', function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "start", "Foo"));
        var store = {};
        sim.states.on_inbound_event = function(event) {
            store.event = event;
        };
        sim.im.on_inbound_event({
            cmd: "inbound-event",
            msg: {
                session_event: "new"
            }
        });
        var event_data = store.event.data.event;
        assert.equal(event_data.session_event, 'new');
    });
    it('should fire a config_read event on_inbound_message', function() {
        var sim = new SingleStateIm(
            new states.FreeText("start", "start", "Foo"));
        var store = {};
        sim.states.on_config_read = function(event) {
            store.event = event;
        };
        sim.im.fetch_config_value = function(key, json, done) {
            assert.equal(key, 'config');
            assert.ok(json);
            done({'sample': 'config'});
        };
        sim.im.on_inbound_message({
            cmd: "inbound-message",
            msg: {
                from_addr: "from_addr",
                content: "content",
                message_id: "message_id",
                session_event: "continue"
            }
        });
        var config = store.event.data.config;
        assert.equal(config.sample, 'config');
    });
});

describe("test State", function() {
    // TODO:
});

describe("test StateError", function() {
    it("should be creatable", function() {
        var se = new states.StateError("msg");
        assert.equal(se.message, "msg");
    });
});

describe("test ChoiceState", function() {
});

describe("test FreeText", function() {
});

describe("test EndState", function() {
});<|endoftext|>