<commit_before>require("ehr/triggers").initScript(this);
var WNPRC = require("wnprc_ehr/WNPRC").WNPRC;
let LABKEY = require("labkey");
let triggerHelper = new org.labkey.wnprc_ehr.TriggerScriptHelper.create(LABKEY.Security.currentUser.id, LABKEY.Security.currentContainer.id);
let allowUsersMap = {};

function onInit(event, helper){
    helper.setScriptOptions({
        allowFutureDates: true,
        allowDatesInDistantPast: true
    });
    LABKEY.Query.selectRows({
        requiredVersion: 9.1,
        schemaName: 'wnprc',
        queryName: 'watermonitoring_access',
        columns:['alloweduser/UserId,project'],
        scope: this,
        success: function(results) {
            var rows = results.rows;
            for(var i=0; i<rows.length; i++){
                var row = rows[i];
                if (allowUsersMap[row["alloweduser/UserId"]["value"]] === undefined){
                    let projectArray = [row["project"]["value"]];
                    allowUsersMap[row["alloweduser/UserId"]["value"]]=projectArray;
                }else{
                    var tempItem = allowUsersMap[row["alloweduser/UserId"]["value"]];
                    tempItem.push(row["project"]["value"]);
                    allowUsersMap[row["alloweduser/UserId"]["value"]]=tempItem;
                }
            }
        },
        failure: function (error) {
            console.log("Error getting data from wnprc.watermonitoring_access while uploading water orders data: " + error);
        }
    });
}

function onUpsert(helper, scriptErrors, row, oldRow){

    if (row.Id){
        EHR.Server.Utils.findDemographics({
            participant: row.Id,
            helper: helper,
            scope: this,
            callback: function(data){
                if(data){
                    if(!row.project){
                        EHR.Server.Utils.addError(scriptErrors, 'project', 'Must enter a project for all center animals.', 'WARN');

                    }
                }
            }
        });
        var animalRestricted = {};
        animalRestricted = WNPRC.Utils.getJavaHelper().checkIfAnimalInCondition(row.Id, row.date);
        //console.log (animalRestricted);
        if (!animalRestricted && !row.skipWaterRegulationCheck){
            EHR.Server.Utils.addError(scriptErrors,'Id', 'Animal not assigned to water restriction protocol or is already in ' + row.waterSource + ' condition.', 'ERROR');
        }

    }

    var today = new Date();
    today.setHours(0,0,0,0);

    var rowDate = new Date(row.date);
    rowDate.setHours(0,0,0,0);
    EHR.Server.Utils.removeTimeFromDate(row,scriptErrors,"date");

    //TODO: allow updates of existing records.
    if(!(rowDate.getTime() >= today.getTime()) && (oldRow.objectid != row.objectid)){
        EHR.Server.Utils.addError(scriptErrors, 'date', 'Only Dates in the Future Allow', 'ERROR');
    }

    var endDate = new Date(row.enddate);
    endDate.setHours(0,0,0,0);

    //This does not get checked when the dataset if updated, only when using Ext4 form
    if (rowDate.getTime() > endDate.getTime()){
        EHR.Server.Utils.addError(scriptErrors,'endDate', 'EndDate cannot be before StartDate', 'ERROR');
    }

    if (!row.frequency && row.waterSource == 'regulated'){
        EHR.Server.Utils.addError(scriptErrors, 'frequency', 'Frequency is required when entering regulated water orders.', 'ERROR');
    }
    if (!row.volume && row.waterSource == 'regulated'){
        //console.log ("water vol "+ row.volume);
        EHR.Server.Utils.addError(scriptErrors, 'volume', 'Volume is required when entering regulated water orders.', 'ERROR');
    }

    if (!row.assignedTo && row.waterSource == 'regulated'){
        EHR.Server.Utils.addError(scriptErrors, 'assignedTo', 'Assigned To is required when entering regulated water orders.', 'ERROR');
    }

    if (!row.waterSource){
        EHR.Server.Utils.addError(scriptErrors, 'waterSource', 'Water Source is required when entering new orders.', 'ERROR');
    }

    //console.log ("value of ObjectId "+oldRow.objectid + " Value of new objectId "+ row.objectid);
   //console.log ('skipWaterRegulation '+ row.skipWaterRegulationCheck);
   // if (oldRow && row.date && row.Id && row.frequency && (oldRow.objectid != row.objectid)) {
    if (row.project && row.objectid && row.Id && row.date && row.frequency && row.assignedTo && row.waterSource != 'lixit' && !row.skipWaterRegulationCheck) {
        console.log('value of row: frequency ' + row.frequency + ' value of waterSource ' + row.waterSource);
        let jsonArray = WNPRC.Utils.getJavaHelper().checkWaterRegulation(row.id, row.date, row.enddate ? row.enddate : null, row.frequency, row.waterSource, row.objectid, row.project, this.extraContext);
        if (jsonArray != null) {
            for (var i = 0; i < jsonArray.length; i++) {
                var errorObject = JSON.parse(jsonArray[i]);
                EHR.Server.Utils.addError(scriptErrors, errorObject.field, errorObject.message, errorObject.severity);
            }
        }
        row.date = rowDate;
    }

    //if (oldRow && oldRow.waterSource == 'regulated' && row.waterSource == 'lixit'){
    //TODO: by pass water regulation to change water order to lixit and also chnage the water regulated animals data
    if ( row.waterSource == 'lixit' && !row.skipWaterRegulationCheck && !oldRow){

        let jsonArray = WNPRC.Utils.getJavaHelper().changeWaterScheduled(row.id,row.date,row.waterSource, row.project, row.objectid,this.extraContext);
        let jsonExtraContext = this.extraContext.extraContextArray;

        if (jsonArray != null){
            for (var i=0; i < jsonArray.length; i++){
                let errorObject = JSON.parse(jsonArray[i]);
                EHR.Server.Utils.addError(scriptErrors,errorObject.field, errorObject.message, errorObject.severity);

            }
            if (jsonExtraContext != null){
                for (var i = 0; i < jsonExtraContext.length; i++){
                    let extraContextObject = jsonExtraContext[i];
                    let date =  extraContextObject.date;
                    let dateOnly = new Date(date.getTime());
                    dateOnly = dateOnly.getFullYear()+ "-" +dateOnly.getMonth()+ "-" + dateOnly.getDate();
                    let infoMessage = "Water Order for "+ row.Id + " started on " + dateOnly + " with frequency of " + extraContextObject.frequency + " and volume of " + extraContextObject.volume + "ml will close.";
                    EHR.Server.Utils.addError(scriptErrors,"waterSource",infoMessage,"INFO")

                }

            }

        }
    }

}

function onUpdate(helper, scriptErrors, row, oldRow){

    let waterOrdersAdmin = false;

    if (row && row.project){
        let currentUser = LABKEY.Security.currentUser.id;
        let allowProjects = allowUsersMap[currentUser];
        if (allowProjects){
            allowProjects.forEach(function (project){
                if (row.project === project){
                    waterOrdersAdmin = true;
                }
            })
        }

        if (!triggerHelper.isDataAdmin() && !waterOrdersAdmin ){
            EHR.Server.Utils.addError(scriptErrors,'project','User does not have permission to edit water order under this project.','ERROR');
            console.error("Water System error, user: "+ currentUser + " trying to modify water order for project: "+ row.project +" and they do not have permissions");

        }
    }
    console.log ("value of triggerHelper admin "+ triggerHelper.isDataAdmin());

    if (!triggerHelper.isDataAdmin()){
        let errorField = null;
        if(oldRow){
            console.log(oldRow);
            //console.log(oldRow.)
            console.log(row);

            var tempKeys = Object.keys(oldRow);
            for (var i = 0; i <= tempKeys.length; i++){
                var key = tempKeys[i];
                console.log(key + '  ' +oldRow[key]);
                console.log('new row  ' +key + ' ' + row[key]);
                if (key !== 'enddate' && oldRow[key] != row[key]){
                    console.log('field checked '+ key)
                    switch (key){
                        case "id":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "date":
                            var newDate = new Date(row[key]);
                            var oldDate = new Date(oldRow[key]);
                            console.log (newDate.getMilliseconds());
                            console.log (oldDate.toString() + oldDate.getMilliseconds());
                            if (new Date(oldRow[key]) !== new Date(row[key])){
                                addErrorMessage('date', scriptErrors);
                            }

                            break;
                        case "volume":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "frequency":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "assignedTo":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "waterSource":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "provideFruit":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "project":
                            addErrorMessage(key, scriptErrors);
                            break;
                    }

                }
            }
            /*for (const key in oldRow){
                console.log(`${key}: ${oldRow[key]}`);
            }*/
            /*for(const [key,value] of Object.keys(oldRow)){
                console.log("rowItem value "+ value);
                console.log("rowItem label "+ key);
            }*/
            // Object.entries(oldRow).forEach(([key, value]) => {
            //     if (value){
            //         console.log("rowItem value "+ value);
            //         console.log("rowItem label "+ key);
            //     }
            //
            // });
        }

        if (row.id !== oldRow.id || row.date !== oldRow.date || row.volume !== oldRow.volume)
        {
            //EHR.Server.Utils.addError(scriptErrors,'date');

        }

    }
}

function addErrorMessage(key,scriptErrors){
    EHR.Server.Utils.addError(scriptErrors, key, 'User does not have permission to modify this field.', 'ERROR');
}
<commit_msg>More troubleshooting the date to reject changes to primary date<commit_after>require("ehr/triggers").initScript(this);
var WNPRC = require("wnprc_ehr/WNPRC").WNPRC;
let LABKEY = require("labkey");
let triggerHelper = new org.labkey.wnprc_ehr.TriggerScriptHelper.create(LABKEY.Security.currentUser.id, LABKEY.Security.currentContainer.id);
let allowUsersMap = {};

function onInit(event, helper){
    helper.setScriptOptions({
        allowFutureDates: true,
        allowDatesInDistantPast: true
    });
    LABKEY.Query.selectRows({
        requiredVersion: 9.1,
        schemaName: 'wnprc',
        queryName: 'watermonitoring_access',
        columns:['alloweduser/UserId,project'],
        scope: this,
        success: function(results) {
            var rows = results.rows;
            for(var i=0; i<rows.length; i++){
                var row = rows[i];
                if (allowUsersMap[row["alloweduser/UserId"]["value"]] === undefined){
                    let projectArray = [row["project"]["value"]];
                    allowUsersMap[row["alloweduser/UserId"]["value"]]=projectArray;
                }else{
                    var tempItem = allowUsersMap[row["alloweduser/UserId"]["value"]];
                    tempItem.push(row["project"]["value"]);
                    allowUsersMap[row["alloweduser/UserId"]["value"]]=tempItem;
                }
            }
        },
        failure: function (error) {
            console.log("Error getting data from wnprc.watermonitoring_access while uploading water orders data: " + error);
        }
    });
}

function onUpsert(helper, scriptErrors, row, oldRow){

    if (row.Id){
        EHR.Server.Utils.findDemographics({
            participant: row.Id,
            helper: helper,
            scope: this,
            callback: function(data){
                if(data){
                    if(!row.project){
                        EHR.Server.Utils.addError(scriptErrors, 'project', 'Must enter a project for all center animals.', 'WARN');

                    }
                }
            }
        });
        var animalRestricted = {};
        animalRestricted = WNPRC.Utils.getJavaHelper().checkIfAnimalInCondition(row.Id, row.date);
        //console.log (animalRestricted);
        if (!animalRestricted && !row.skipWaterRegulationCheck){
            EHR.Server.Utils.addError(scriptErrors,'Id', 'Animal not assigned to water restriction protocol or is already in ' + row.waterSource + ' condition.', 'ERROR');
        }

    }

    var today = new Date();
    today.setHours(0,0,0,0);

    var rowDate = new Date(row.date);
    rowDate.setHours(0,0,0,0);
    EHR.Server.Utils.removeTimeFromDate(row,scriptErrors,"date");

    //TODO: allow updates of existing records.
    if(!(rowDate.getTime() >= today.getTime()) && (oldRow.objectid != row.objectid)){
        EHR.Server.Utils.addError(scriptErrors, 'date', 'Only Dates in the Future Allow', 'ERROR');
    }

    var endDate = new Date(row.enddate);
    endDate.setHours(0,0,0,0);

    //This does not get checked when the dataset if updated, only when using Ext4 form
    if (rowDate.getTime() > endDate.getTime()){
        EHR.Server.Utils.addError(scriptErrors,'endDate', 'EndDate cannot be before StartDate', 'ERROR');
    }

    if (!row.frequency && row.waterSource == 'regulated'){
        EHR.Server.Utils.addError(scriptErrors, 'frequency', 'Frequency is required when entering regulated water orders.', 'ERROR');
    }
    if (!row.volume && row.waterSource == 'regulated'){
        //console.log ("water vol "+ row.volume);
        EHR.Server.Utils.addError(scriptErrors, 'volume', 'Volume is required when entering regulated water orders.', 'ERROR');
    }

    if (!row.assignedTo && row.waterSource == 'regulated'){
        EHR.Server.Utils.addError(scriptErrors, 'assignedTo', 'Assigned To is required when entering regulated water orders.', 'ERROR');
    }

    if (!row.waterSource){
        EHR.Server.Utils.addError(scriptErrors, 'waterSource', 'Water Source is required when entering new orders.', 'ERROR');
    }

    //console.log ("value of ObjectId "+oldRow.objectid + " Value of new objectId "+ row.objectid);
   //console.log ('skipWaterRegulation '+ row.skipWaterRegulationCheck);
   // if (oldRow && row.date && row.Id && row.frequency && (oldRow.objectid != row.objectid)) {
    if (row.project && row.objectid && row.Id && row.date && row.frequency && row.assignedTo && row.waterSource != 'lixit' && !row.skipWaterRegulationCheck) {
        console.log('value of row: frequency ' + row.frequency + ' value of waterSource ' + row.waterSource);
        let jsonArray = WNPRC.Utils.getJavaHelper().checkWaterRegulation(row.id, row.date, row.enddate ? row.enddate : null, row.frequency, row.waterSource, row.objectid, row.project, this.extraContext);
        if (jsonArray != null) {
            for (var i = 0; i < jsonArray.length; i++) {
                var errorObject = JSON.parse(jsonArray[i]);
                EHR.Server.Utils.addError(scriptErrors, errorObject.field, errorObject.message, errorObject.severity);
            }
        }
        row.date = rowDate;
    }

    //if (oldRow && oldRow.waterSource == 'regulated' && row.waterSource == 'lixit'){
    //TODO: by pass water regulation to change water order to lixit and also chnage the water regulated animals data
    if ( row.waterSource == 'lixit' && !row.skipWaterRegulationCheck && !oldRow){

        let jsonArray = WNPRC.Utils.getJavaHelper().changeWaterScheduled(row.id,row.date,row.waterSource, row.project, row.objectid,this.extraContext);
        let jsonExtraContext = this.extraContext.extraContextArray;

        if (jsonArray != null){
            for (var i=0; i < jsonArray.length; i++){
                let errorObject = JSON.parse(jsonArray[i]);
                EHR.Server.Utils.addError(scriptErrors,errorObject.field, errorObject.message, errorObject.severity);

            }
            if (jsonExtraContext != null){
                for (var i = 0; i < jsonExtraContext.length; i++){
                    let extraContextObject = jsonExtraContext[i];
                    let date =  extraContextObject.date;
                    let dateOnly = new Date(date.getTime());
                    dateOnly = dateOnly.getFullYear()+ "-" +dateOnly.getMonth()+ "-" + dateOnly.getDate();
                    let infoMessage = "Water Order for "+ row.Id + " started on " + dateOnly + " with frequency of " + extraContextObject.frequency + " and volume of " + extraContextObject.volume + "ml will close.";
                    EHR.Server.Utils.addError(scriptErrors,"waterSource",infoMessage,"INFO")

                }

            }

        }
    }

}

function onUpdate(helper, scriptErrors, row, oldRow){

    let waterOrdersAdmin = false;

    if (row && row.project){
        let currentUser = LABKEY.Security.currentUser.id;
        let allowProjects = allowUsersMap[currentUser];
        if (allowProjects){
            allowProjects.forEach(function (project){
                if (row.project === project){
                    waterOrdersAdmin = true;
                }
            })
        }

        if (!triggerHelper.isDataAdmin() && !waterOrdersAdmin ){
            EHR.Server.Utils.addError(scriptErrors,'project','User does not have permission to edit water order under this project.','ERROR');
            console.error("Water System error, user: "+ currentUser + " trying to modify water order for project: "+ row.project +" and they do not have permissions");

        }
    }
    console.log ("value of triggerHelper admin "+ triggerHelper.isDataAdmin());

    if (!triggerHelper.isDataAdmin()){
        let errorField = null;
        if(oldRow){
            //console.log(oldRow);
            //console.log(oldRow.)
            //console.log(row);

            var tempKeys = Object.keys(oldRow);
            for (var i = 0; i <= tempKeys.length; i++){
                var key = tempKeys[i];
                //console.log(key + '  ' +oldRow[key]);
                //console.log('new row  ' +key + ' ' + row[key]);
                if (key !== 'enddate' && oldRow[key] != row[key]){
                    console.log('field checked '+ key)
                    switch (key){
                        case "id":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "date":
                            var newDate = EHR.Server.Utils.normalizeDate(row[key]);
                            var oldDate = EHR.Server.Utils.normalizeDate(oldRow[key]);
                            console.log (newDate);
                            console.log (oldDate);
                            if (newDate == oldDate){
                                console.log("new date and oldDate equal "+ key);
                            }else{
                                console.log("new date and oldDate different "+ key);
                                addErrorMessage(key, scriptErrors);

                            }

                            break;
                        case "volume":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "frequency":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "assignedTo":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "waterSource":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "provideFruit":
                            addErrorMessage(key, scriptErrors);
                            break;
                        case "project":
                            addErrorMessage(key, scriptErrors);
                            break;
                    }

                }
            }
            /*for (const key in oldRow){
                console.log(`${key}: ${oldRow[key]}`);
            }*/
            /*for(const [key,value] of Object.keys(oldRow)){
                console.log("rowItem value "+ value);
                console.log("rowItem label "+ key);
            }*/
            // Object.entries(oldRow).forEach(([key, value]) => {
            //     if (value){
            //         console.log("rowItem value "+ value);
            //         console.log("rowItem label "+ key);
            //     }
            //
            // });
        }

        if (row.id !== oldRow.id || row.date !== oldRow.date || row.volume !== oldRow.volume)
        {
            //EHR.Server.Utils.addError(scriptErrors,'date');

        }

    }
}

function addErrorMessage(key,scriptErrors){
    EHR.Server.Utils.addError(scriptErrors, key, 'User does not have permission to modify this field.', 'ERROR');
}
<|endoftext|>


<commit_before>'use strict'

import request from 'request'
import IntentDialog from './intent-dialog'

class WitDialog extends IntentDialog {
  constructor (params) {
    super()
    this.serviceUri = params.serviceUri
    this.bearerToken = params.bearerToken
  }

  recognizeIntents (language, utterance, callback) {
    WitDialog.recognize(utterance, this.serviceUri, this.bearerToken, callback)
  }
}

WitDialog.recognize = function (utterance, serviceUri, bearerToken, callback) {
  let uri = serviceUri.trim()
  if (uri.lastIndexOf('&q=') !== (uri.length - 3)) {
    uri += '&q='
  }
  uri += encodeURIComponent(utterance || '')

  const reqOpts = {
    auth: {
      bearer: bearerToken
    }
  }

  request.get(uri, reqOpts, (err, res, body) => {
    let calledCallback = false
    try {
      if (!err) {
        const result = JSON.parse(body)

        console.log(result)

        const intents = (result.entities.intent || []).map((intent) => {
          return {
            score: intent.confidence,
            intent: intent.value
          }
        })

        console.log(intents)

        const entityKeys = Object.keys(result.entities).filter((key) => {
          return key !== 'intent'
        })

        const entities = entityKeys.map((key) => {
          return {
            confidence: result.entities[key][0].confidence,
            entity: result.entities[key][0].value,
            type: key,
            resolution: false
          }
        })

        console.log(body)

        calledCallback = true
        callback(null, intents, entities)
      } else {
        calledCallback = true
        callback(err)
      }
    } catch (e) {
      console.error(e.stack)
      if (!calledCallback) {
        callback(e)
      } else {
        console.error(e.toString())
      }
    }
  })
}

export default WitDialog
<commit_msg>Remove console.log from wit-dialog.js<commit_after>'use strict'

import request from 'request'
import IntentDialog from './intent-dialog'

class WitDialog extends IntentDialog {
  constructor (params) {
    super()
    this.serviceUri = params.serviceUri
    this.bearerToken = params.bearerToken
  }

  recognizeIntents (language, utterance, callback) {
    WitDialog.recognize(utterance, this.serviceUri, this.bearerToken, callback)
  }
}

WitDialog.recognize = function (utterance, serviceUri, bearerToken, callback) {
  let uri = serviceUri.trim()
  if (uri.lastIndexOf('&q=') !== (uri.length - 3)) {
    uri += '&q='
  }
  uri += encodeURIComponent(utterance || '')

  const reqOpts = {
    auth: {
      bearer: bearerToken
    }
  }

  request.get(uri, reqOpts, (err, res, body) => {
    let calledCallback = false
    try {
      if (!err) {
        const result = JSON.parse(body)

        // console.log(result)

        const intents = (result.entities.intent || []).map((intent) => {
          return {
            score: intent.confidence,
            intent: intent.value
          }
        })

        // console.log(intents)

        const entityKeys = Object.keys(result.entities).filter((key) => {
          return key !== 'intent'
        })

        const entities = entityKeys.map((key) => {
          return {
            confidence: result.entities[key][0].confidence,
            entity: result.entities[key][0].value,
            type: key,
            resolution: false
          }
        })

        calledCallback = true
        callback(null, intents, entities)
      } else {
        calledCallback = true
        callback(err)
      }
    } catch (e) {
      console.error(e.stack)
      if (!calledCallback) {
        callback(e)
      } else {
        console.error(e.toString())
      }
    }
  })
}

export default WitDialog
<|endoftext|>


<commit_before>AvailabilityService =
    class AvailabilityService {

        static getSurroundingAvailability(user, start, end) {
            console.info("AvailabilityService.getAvailability start:", start, "end", end, "for user", user);
            var found;
            var start = new moment(start);
            if (typeof end === "undefined") var end = new moment(start);
            else
                var end = new moment(end);
            var end = new moment(end);
            user.availabilities.forEach(availability => {
                var availabilityStart = new moment(availability.start);
                var availabilityEnd = new moment(availability.end);
                if (( availabilityStart.isBefore(start) || availabilityStart.isSame(start))
                    && (availabilityEnd.isAfter(end) || availabilityEnd.isSame(end) )) {
                    found = availability;
                    return false;
                }
            });
            return found;
        }

        static getIndexOfSurroundingAvailability(user, start, end) {
            console.info("AvailabilityService.getAvailability start:", start, "end", end, "for user", user);
            var found;
            var start = new moment(start);
            if (typeof end === "undefined") var end = new moment(start);
            else
                var end = new moment(end);
            var end = new moment(end);
            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityStart = new moment(availability.start);
                var availabilityEnd = new moment(availability.end);
                if (( availabilityStart.isBefore(start) || availabilityStart.isSame(start))
                    && (availabilityEnd.isAfter(end) || availabilityEnd.isSame(end) )) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static removeAvailabilities(user, start, end) {
            console.info("AvailabilityService.splitAvailabilities for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;

            var availabilityIndex = AvailabilityService.getIndexOfSurroundingAvailability(user, start, end);
            //remove old availability
            var availability = availabilities.splice(availabilityIndex, 1)[0];
            //add new availabilities and prevent creating a 0minutes availability
            if (!new moment(availability.start).isSame(new moment(start)))
                availabilities.push({
                    start: availability.start,
                    end: start
                });
            if (!new moment(end).isSame(new moment(availability.end)))
                availabilities.push({
                    start: end,
                    end: availability.end
                });

            Users.update({_id: user._id}, {$set: {availabilities: availabilities}});

        }

        static getIndexOfAvailabilityWhichEndWhenParamStart(user, start) {
            console.info("AvailabilityService.getIndexOfAvailabilityWhichEndWhenParamStart start:", start, "for user", user);
            var found;
            var start = new moment(start);

            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityEnd = new moment(availability.end);
                if (availabilityEnd.isSame(start)) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static getIndexOfAvailabilityWhichStartWhenParamEnd(user, end) {
            console.info("AvailabilityService.getIndexOfAvailabilityWhichStartWhenParamEnd end:", end, "for user", user);
            var found;
            var end = new moment(end);

            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityStart = new moment(availability.start);
                if (availabilityStart.isSame(end)) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static restoreAvailabilities(user, start, end) {
            console.info("AvailabilityService.restoreAvailabilities for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;
            var previousAvailability, nextAvailability;

            //if exits, get direct previous availabilty
            var previousAvailabilityIndex = AvailabilityService.getIndexOfAvailabilityWhichEndWhenParamStart(user, start);

            //if exits, get direct next availabilty
            var nextAvailabilityIndex = AvailabilityService.getIndexOfAvailabilityWhichStartWhenParamEnd(user, end);

            //if possible

            //remove old availability(ies)
            if (previousAvailabilityIndex) {
                previousAvailability = availabilities.splice(previousAvailabilityIndex, 1)[0];
            }
            if (nextAvailabilityIndex) {
                nextAvailability = availabilities.splice(nextAvailabilityIndex, 1)[0];
            }

            var newAvailability = {};

            //merge availability
            if (previousAvailability) {
                newAvailability.start = previousAvailability.start;
            } else {
                newAvailability.start = start;
            }
            if (nextAvailability) {
                nextAvailability.end = nextAvailability.end;
            } else {
                newAvailability.end = end;
            }

            Users.update({_id: user._id}, {$set: {availabilities: availabilities}});

        }

        static getAvailabilityByStart(availabilities, start) {
            return TimeSlotService.getTimeSlotByStart(availabilities, start);
        }

        static checkUserAvailabilty(user, start, end) {
            console.info("AvailabilityService.checkUserAvailabilty for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;

            var availabilityIndex = AvailabilityService.getIndexOfSurroundingAvailability(user, start, end);

            if (availabilityIndex == null) {
                return false;
            }
            return true;

        }
    }
<commit_msg>[RPI] fix #85<commit_after>AvailabilityService =
    class AvailabilityService {

        static getSurroundingAvailability(user, start, end) {
            console.info("AvailabilityService.getAvailability start:", start, "end", end, "for user", user);
            var found;
            var start = new moment(start);
            if (typeof end === "undefined") var end = new moment(start);
            else
                var end = new moment(end);
            var end = new moment(end);
            user.availabilities.forEach(availability => {
                var availabilityStart = new moment(availability.start);
                var availabilityEnd = new moment(availability.end);
                if (( availabilityStart.isBefore(start) || availabilityStart.isSame(start))
                    && (availabilityEnd.isAfter(end) || availabilityEnd.isSame(end) )) {
                    found = availability;
                    return false;
                }
            });
            return found;
        }

        static getIndexOfSurroundingAvailability(user, start, end) {
            console.info("AvailabilityService.getAvailability start:", start, "end", end, "for user", user);
            var found;
            var start = new moment(start);
            if (typeof end === "undefined") var end = new moment(start);
            else
                var end = new moment(end);
            var end = new moment(end);
            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityStart = new moment(availability.start);
                var availabilityEnd = new moment(availability.end);
                if (( availabilityStart.isBefore(start) || availabilityStart.isSame(start))
                    && (availabilityEnd.isAfter(end) || availabilityEnd.isSame(end) )) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static removeAvailabilities(user, start, end) {
            console.info("AvailabilityService.splitAvailabilities for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;

            var availabilityIndex = AvailabilityService.getIndexOfSurroundingAvailability(user, start, end);
            //remove old availability
            var availability = availabilities.splice(availabilityIndex, 1)[0];
            //add new availabilities and prevent creating a 0minutes availability
            if (!new moment(availability.start).isSame(new moment(start)))
                availabilities.push({
                    start: availability.start,
                    end: start
                });
            if (!new moment(end).isSame(new moment(availability.end)))
                availabilities.push({
                    start: end,
                    end: availability.end
                });

            Users.update({_id: user._id}, {$set: {availabilities: availabilities}});

        }

        static getIndexOfAvailabilityWhichEndWhenParamStart(user, start) {
            console.info("AvailabilityService.getIndexOfAvailabilityWhichEndWhenParamStart start:", start, "for user", user);
            var found;
            var start = new moment(start);

            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityEnd = new moment(availability.end);
                if (availabilityEnd.isSame(start)) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static getIndexOfAvailabilityWhichStartWhenParamEnd(user, end) {
            console.info("AvailabilityService.getIndexOfAvailabilityWhichStartWhenParamEnd end:", end, "for user", user);
            var found;
            var end = new moment(end);

            user.availabilities.forEach(function (availability, index, availabilities) {
                var availabilityStart = new moment(availability.start);
                if (availabilityStart.isSame(end)) {
                    found = index;
                    return false;
                }
            });
            return found;
        }

        static restoreAvailabilities(user, start, end) {
            console.info("AvailabilityService.restoreAvailabilities for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;
            var previousAvailability, nextAvailability;

            //if exits, get direct previous availabilty
            var previousAvailabilityIndex = AvailabilityService.getIndexOfAvailabilityWhichEndWhenParamStart(user, start);

            //if exits, get direct next availabilty
            var nextAvailabilityIndex = AvailabilityService.getIndexOfAvailabilityWhichStartWhenParamEnd(user, end);

            //if possible

            //remove old availability(ies)
            if (typeof previousAvailabilityIndex !== "undefined") {
                previousAvailability = availabilities.splice(previousAvailabilityIndex, 1)[0];
            }
            if (typeof nextAvailabilityIndex !== "undefined") {
                nextAvailability = availabilities.splice(nextAvailabilityIndex, 1)[0];
            }

            var newAvailability = {};

            //merge availability
            if (previousAvailability) {
                newAvailability.start = previousAvailability.start;
            } else {
                newAvailability.start = start;
            }
            if (nextAvailability) {
                newAvailability.end = nextAvailability.end;
            } else {
                newAvailability.end = end;
            }

            availabilities.push(newAvailability);

            Users.update({_id: user._id}, {$set: {availabilities: availabilities}});

        }

        static getAvailabilityByStart(availabilities, start) {
            return TimeSlotService.getTimeSlotByStart(availabilities, start);
        }

        static checkUserAvailabilty(user, start, end) {
            console.info("AvailabilityService.checkUserAvailabilty for user", user, " from", start, "to", end);
            var availabilities = user.availabilities;

            var availabilityIndex = AvailabilityService.getIndexOfSurroundingAvailability(user, start, end);

            if (availabilityIndex == null) {
                return false;
            }
            return true;

        }
    }
<|endoftext|>


<commit_before>'use strict';

function insertChoice(paramName, paramWidgetText, paramTextColor, paramFillColor, paramGoal, paramChoiceText, paramChoiceColor, paramValue, paramFont) {
  var theChoice = {
    username:paramName,
    widgetText:paramWidgetText,
    textColor:paramTextColor,
    fillColor:paramFillColor,
    goal:paramGoal,
    choiceText:paramChoiceText,
    choiceColor:paramChoiceColor,
    value:paramValue
    googleFont:paramFont
  }
  $.post('/choices', theChoice);
}

function deleteChoices(callback) {
  $.ajax({
    url: `/choices/${userInfo.currentUser}`,
    method: 'DELETE'
  }).then(callback());
}

// function getPageInfo() {
//   return $.get(`/choices/${userInfo.currentUser}`);
// }
// Article.prototype.insertRecord = function(callback) {
//   $.post('/articles', {author: this.author, authorUrl: this.authorUrl, body: this.body, category: this.category, publishedOn: this.publishedOn, title: this.title})
//   .then(console.log)
//   .then(callback);
// };
<commit_msg>testing fonts<commit_after>'use strict';

function insertChoice(paramName, paramWidgetText, paramTextColor, paramFillColor, paramGoal, paramChoiceText, paramChoiceColor, paramValue, paramFont) {
  var theChoice = {
    username:paramName,
    widgetText:paramWidgetText,
    textColor:paramTextColor,
    fillColor:paramFillColor,
    goal:paramGoal,
    choiceText:paramChoiceText,
    choiceColor:paramChoiceColor,
    value:paramValue,
    googleFont:paramFont
  }
  $.post('/choices', theChoice);
}

function deleteChoices(callback) {
  $.ajax({
    url: `/choices/${userInfo.currentUser}`,
    method: 'DELETE'
  }).then(callback());
}

// function getPageInfo() {
//   return $.get(`/choices/${userInfo.currentUser}`);
// }
// Article.prototype.insertRecord = function(callback) {
//   $.post('/articles', {author: this.author, authorUrl: this.authorUrl, body: this.body, category: this.category, publishedOn: this.publishedOn, title: this.title})
//   .then(console.log)
//   .then(callback);
// };
<|endoftext|>


<commit_before>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  xpath.push("//*[@onclick]");
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true); }

function activateLinkHintsMode(openInNewTab) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  shouldOpenLinkHintInNewTab = openInNewTab
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0; i < visibleElements.length; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.body.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  var visibleElements = [];

  // Prune all invisible clickable elements.
  for (var i = 0; i < resultSet.snapshotLength; i++) {
    var element = resultSet.snapshotItem(i);

    // Note that getBoundingClientRect() is relative to the viewport
    var boundingRect = element.getBoundingClientRect();
    // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
    var zoomFactor = currentZoomLevel / 100.0;
    if (boundingRect.bottom <= 4 || boundingRect.top * zoomFactor >= window.innerHeight - 4 ||
        boundingRect.left <= 0 || boundingRect.left * zoomFactor >= window.innerWidth - 4)
      continue;

    if (boundingRect.width < 3 || boundingRect.height < 3)
      continue;

    // eliminate invisible elements (see test_harnesses/visibility_test.html)
    var computedStyle = window.getComputedStyle(element, null);
    if (computedStyle.getPropertyValue('visibility') != 'visible' ||
        computedStyle.getPropertyValue('display') == 'none')
      continue;

    var clientRect = element.getClientRects()[0];
    if (!clientRect)
        continue;

    visibleElements.push(element);
  }
  return visibleElements;
}

function onKeyDownInLinkHintsMode(event) {
  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (!shouldOpenLinkHintInNewTab)
        setTimeout(function() { simulateClick(matchedLink); }, 400);
      else
        simulateClick(matchedLink);
      matchedLink.focus();
    }
    deactivateLinkHintsMode();
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      for (var j = 0; j < linkMarker.childNodes.length; j++)
        linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  var hintStringLength = hintString.length;
  for (var i = 0; i < numHintDigits - hintStringLength; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  linkHintsModeActivated = false;
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.getClientRects()[0];
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link;
  return marker;
}
<commit_msg>Show link hints for floated elements<commit_after>/*
 * This implements link hinting. Typing "F" will enter link-hinting mode, where all clickable items on
 * the page have a hint marker displayed containing a sequence of letters. Typing those letters will select
 * a link.
 *
 * The characters we use to show link hints are a user-configurable option. By default they're the home row.
 * The CSS which is used on the link hints is also a configurable option.
 */

var hintMarkers = [];
var hintMarkerContainingDiv = null;
// The characters that were typed in while in "link hints" mode.
var hintKeystrokeQueue = [];
var linkHintsModeActivated = false;
var shouldOpenLinkHintInNewTab = false;
// Whether we have added to the page the CSS needed to display link hints.
var linkHintsCssAdded = false;

/*
 * Generate an XPath describing what a clickable element is.
 * The final expression will be something like "//button | //xhtml:button | ..."
 */
var clickableElementsXPath = (function() {
  var clickableElements = ["a", "textarea", "button", "select", "input[not(@type='hidden')]"];
  var xpath = [];
  for (var i in clickableElements)
    xpath.push("//" + clickableElements[i], "//xhtml:" + clickableElements[i]);
  xpath.push("//*[@onclick]");
  return xpath.join(" | ")
})();

// We need this as a top-level function because our command system doesn't yet support arguments.
function activateLinkHintsModeToOpenInNewTab() { activateLinkHintsMode(true); }

function activateLinkHintsMode(openInNewTab) {
  if (!linkHintsCssAdded)
    addCssToPage(linkHintCss); // linkHintCss is declared by vimiumFrontend.js
  linkHintCssAdded = true;
  linkHintsModeActivated = true;
  shouldOpenLinkHintInNewTab = openInNewTab
  buildLinkHints();
  document.addEventListener("keydown", onKeyDownInLinkHintsMode, true);
}

/*
 * Builds and displays link hints for every visible clickable item on the page.
 */
function buildLinkHints() {
  var visibleElements = getVisibleClickableElements();

  // Initialize the number used to generate the character hints to be as many digits as we need to
  // highlight all the links on the page; we don't want some link hints to have more chars than others.
  var digitsNeeded = Math.ceil(logXOfBase(visibleElements.length, settings.linkHintCharacters.length));
  var linkHintNumber = 0;
  for (var i = 0; i < visibleElements.length; i++) {
    hintMarkers.push(createMarkerFor(visibleElements[i], linkHintNumber, digitsNeeded));
    linkHintNumber++;
  }
  // Note(philc): Append these markers as top level children instead of as child nodes to the link itself,
  // because some clickable elements cannot contain children, e.g. submit buttons. This has the caveat
  // that if you scroll the page and the link has position=fixed, the marker will not stay fixed.
  // Also note that adding these nodes to document.body all at once is significantly faster than one-by-one.
  hintMarkerContainingDiv = document.createElement("div");
  hintMarkerContainingDiv.className = "internalVimiumHintMarker";
  for (var i = 0; i < hintMarkers.length; i++)
    hintMarkerContainingDiv.appendChild(hintMarkers[i]);
  document.body.appendChild(hintMarkerContainingDiv);
}

function logXOfBase(x, base) { return Math.log(x) / Math.log(base); }

/*
 * Returns all clickable elements that are not hidden and are in the current viewport.
 * We prune invisible elements partly for performance reasons, but moreso it's to decrease the number
 * of digits needed to enumerate all of the links on screen.
 */
function getVisibleClickableElements() {
  var resultSet = document.evaluate(clickableElementsXPath, document.body,
    function (namespace) {
      return namespace == "xhtml" ? "http://www.w3.org/1999/xhtml" : null;
    },
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

  var visibleElements = [];

  // Find all visible clickable elements.
  for (var i = 0; i < resultSet.snapshotLength; i++) {
    var element = resultSet.snapshotItem(i);
    var boundingRect = element.getBoundingClientRect();

    if (isVisible(element, boundingRect))
      visibleElements.push(element);

    // If the link has zero dimensions, it may be wrapping visible
    // but floated elements. Check for this.
    if (boundingRect.width == 0 && boundingRect.height == 0) {
      for (var j = 0; j < element.childNodes.length; j++) {
        if (window.getComputedStyle(element.childNodes[j], null).getPropertyValue('float') != 'none'
            && isVisible(element.childNodes[j])) {
          visibleElements.push(element.childNodes[j]);
          break;
        }
      }
    }
  }
  return visibleElements;
}

/*
 * Returns true if element is visible.
 * boundingRect is an optional argument.
 */
function isVisible(element, boundingRect) {
  // Note that getBoundingClientRect() is relative to the viewport
  if (boundingRect === undefined)
    boundingRect = element.getBoundingClientRect();

  // Exclude links which have just a few pixels on screen, because the link hints won't show for them anyway.
  var zoomFactor = currentZoomLevel / 100.0;
  if (boundingRect.bottom <= 4 || boundingRect.top * zoomFactor >= window.innerHeight - 4 ||
      boundingRect.left <= 0 || boundingRect.left * zoomFactor >= window.innerWidth - 4)
    return false;

  if (boundingRect.width < 3 || boundingRect.height < 3)
    return false;

  // eliminate invisible elements (see test_harnesses/visibility_test.html)
  var computedStyle = window.getComputedStyle(element, null);
  if (computedStyle.getPropertyValue('visibility') != 'visible' ||
      computedStyle.getPropertyValue('display') == 'none')
    return false;

  var clientRect = element.getClientRects()[0];
  if (!clientRect)
    return false;

  return true;
}

function onKeyDownInLinkHintsMode(event) {
  var keyChar = getKeyChar(event);
  if (!keyChar)
    return;

  // TODO(philc): Ignore keys that have modifiers.
  if (isEscape(event)) {
    deactivateLinkHintsMode();
  } else if (event.keyCode == keyCodes.backspace || event.keyCode == keyCodes.deleteKey) {
    if (hintKeystrokeQueue.length == 0) {
      deactivateLinkHintsMode();
    } else {
      hintKeystrokeQueue.pop();
      updateLinkHints();
    }
  } else if (settings.linkHintCharacters.indexOf(keyChar) >= 0) {
    hintKeystrokeQueue.push(keyChar);
    updateLinkHints();
  } else {
    return;
  }

  event.stopPropagation();
  event.preventDefault();
}

/*
 * Updates the visibility of link hints on screen based on the keystrokes typed thus far. If only one
 * link hint remains, click on that link and exit link hints mode.
 */
function updateLinkHints() {
  var matchString = hintKeystrokeQueue.join("");
  var linksMatched = highlightLinkMatches(matchString);
  if (linksMatched.length == 0)
    deactivateLinkHintsMode();
  else if (linksMatched.length == 1) {
    var matchedLink = linksMatched[0];
    if (isSelectable(matchedLink)) {
      matchedLink.focus();
      // When focusing a textbox, put the selection caret at the end of the textbox's contents.
      matchedLink.setSelectionRange(matchedLink.value.length, matchedLink.value.length);
    } else {
      // When we're opening the link in the current tab, don't navigate to the selected link immediately;
      // we want to give the user some feedback depicting which link they've selected by focusing it.
      if (!shouldOpenLinkHintInNewTab)
        setTimeout(function() { simulateClick(matchedLink); }, 400);
      else
        simulateClick(matchedLink);
      matchedLink.focus();
    }
    deactivateLinkHintsMode();
  }
}

/*
 * Selectable means the element has a text caret; this is not the same as "focusable".
 */
function isSelectable(element) {
  var selectableTypes = ["search", "text", "password"];
  return (element.tagName == "INPUT" && selectableTypes.indexOf(element.type) >= 0) ||
      element.tagName == "TEXTAREA";
}

/*
 * Hides link hints which do not match the given search string. To allow the backspace key to work, this
 * will also show link hints which do match but were previously hidden.
 */
function highlightLinkMatches(searchString) {
  var linksMatched = [];
  for (var i = 0; i < hintMarkers.length; i++) {
    var linkMarker = hintMarkers[i];
    if (linkMarker.getAttribute("hintString").indexOf(searchString) == 0) {
      if (linkMarker.style.display == "none")
        linkMarker.style.display = "";
      for (var j = 0; j < linkMarker.childNodes.length; j++)
        linkMarker.childNodes[j].className = (j >= searchString.length) ? "" : "matchingCharacter";
      linksMatched.push(linkMarker.clickableItem);
    } else {
      linkMarker.style.display = "none";
    }
  }
  return linksMatched;
}

/*
 * Converts a number like "8" into a hint string like "JK". This is used to sequentially generate all of
 * the hint text. The hint string will be "padded with zeroes" to ensure its length is equal to numHintDigits.
 */
function numberToHintString(number, numHintDigits) {
  var base = settings.linkHintCharacters.length;
  var hintString = [];
  var remainder = 0;
  do {
    remainder = number % base;
    hintString.unshift(settings.linkHintCharacters[remainder]);
    number -= remainder;
    number /= Math.floor(base);
  } while (number > 0);

  // Pad the hint string we're returning so that it matches numHintDigits.
  var hintStringLength = hintString.length;
  for (var i = 0; i < numHintDigits - hintStringLength; i++)
    hintString.unshift(settings.linkHintCharacters[0]);
  return hintString.join("");
}

function simulateClick(link) {
  var event = document.createEvent("MouseEvents");
  // When "clicking" on a link, dispatch the event with the appropriate meta key (CMD on Mac, CTRL on windows)
  // to open it in a new tab if necessary.
  var metaKey = (platform == "Mac" && shouldOpenLinkHintInNewTab);
  var ctrlKey = (platform != "Mac" && shouldOpenLinkHintInNewTab);
  event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, ctrlKey, false, false, metaKey, 0, null);

  // Debugging note: Firefox will not execute the link's default action if we dispatch this click event,
  // but Webkit will. Dispatching a click on an input box does not seem to focus it; we do that separately
  link.dispatchEvent(event);
}

function deactivateLinkHintsMode() {
  if (hintMarkerContainingDiv)
    hintMarkerContainingDiv.parentNode.removeChild(hintMarkerContainingDiv);
  hintMarkerContainingDiv = null;
  hintMarkers = [];
  hintKeystrokeQueue = [];
  document.removeEventListener("keydown", onKeyDownInLinkHintsMode, true);
  linkHintsModeActivated = false;
}

/*
 * Creates a link marker for the given link.
 */
function createMarkerFor(link, linkHintNumber, linkHintDigits) {
  var hintString = numberToHintString(linkHintNumber, linkHintDigits);
  var marker = document.createElement("div");
  marker.className = "internalVimiumHintMarker vimiumHintMarker";
  var innerHTML = [];
  // Make each hint character a span, so that we can highlight the typed characters as you type them.
  for (var i = 0; i < hintString.length; i++)
    innerHTML.push("<span>" + hintString[i].toUpperCase() + "</span>");
  marker.innerHTML = innerHTML.join("");
  marker.setAttribute("hintString", hintString);

  // Note: this call will be expensive if we modify the DOM in between calls.
  var clientRect = link.getClientRects()[0];
  // The coordinates given by the window do not have the zoom factor included since the zoom is set only on
  // the document node.
  var zoomFactor = currentZoomLevel / 100.0;
  marker.style.left = clientRect.left + window.scrollX / zoomFactor + "px";
  marker.style.top = clientRect.top  + window.scrollY / zoomFactor + "px";

  marker.clickableItem = link;
  return marker;
}
<|endoftext|>


<commit_before>var async = require('async'),
	_ = require('underscore')._,
	auth = require('../lib/auth'),
	error = require('../lib/error'),
	facebook = require('../accounts/facebook.js'),
	gadgets = require('../gadgets/gadgets');

exports.registerController = function(app) {
	//Require login
	app.get(/^\/(dashboard|settings)$/, auth.middleware(true, '/'));

	//Show dashboard with gadgets
	app.get(/^\/(dashboard|settings)$/, function(req, res, next) {
		//Find user's gadgets
		var userGadgets = { };
		_.each(gadgets, function(gadget, name) {
			if (req.user.gadgets[name.toLowerCase()]) {
				userGadgets[name] = new gadget(req.user);
			}
		});

		//Regular request
		if (!req.xhr) {
			//Renew Facebook access token when expiry is imminent
			var fb = req.user.accounts.facebook;

			if (fb && new Date(fb.expires) - new Date <= 24 * 60 * 60 * 1000) {
				res.redirect(app.set('uri') + 'dashboard/facebook?_method=put');
			}

			//Build location string for usage in view
			var location = _.values(_.pick(req.user.location || [ ],
				[ 'area', 'province', 'country' ])).join(', ');

			//Render view
			res.render('dashboard', {
				stylesheet: 'dashboard',
				gadgets: gadgets,
				userGadgets: userGadgets,
				activeGadgetCount: _.size(userGadgets),
				location: location
			});

			app.winston.debug('%s %s complete', req.method, req.url);
		//AJAX request for live updating
		} else {
			//Find stale data
			var staleGadgets = _.filter(userGadgets, function(gadget, name) {
				var age = new Date - req.user.gadgets[name.toLowerCase()].lastUpdate;

				//Shortest interval is used for AJAX updating
				var minAge = _.min(gadgets[name].intervals) * 1000;

				//Also request update for gadgets that failed to update
				//as the retreat interval might have passed
				return age >= minAge || req.user.gadgets[name.toLowerCase()].consecutiveErrors > 0;
			});

			//Request immediate update of user
			app.queue.cutqueue(req.user, _.values(staleGadgets), function(updated) {
				//Return JSON
				var gadgetData = { };

				_.each(userGadgets, function(gadget, name) {
					if (gadget.consecutiveErrors < 4) {
						gadgetData[name.toLowerCase()] = gadget.value;
					} else {
						gadgetData[name.toLowerCase()] = null;
					}
				});

				res.json(gadgetData);

				app.winston.debug('%s %s complete', req.method, req.url);
			});
		}
	});
};<commit_msg>Disabled cache for dashboard<commit_after>var async = require('async'),
	_ = require('underscore')._,
	auth = require('../lib/auth'),
	error = require('../lib/error'),
	facebook = require('../accounts/facebook.js'),
	gadgets = require('../gadgets/gadgets');

exports.registerController = function(app) {
	//Require login
	app.get(/^\/(dashboard|settings)$/, auth.middleware(true, '/'));

	//Show dashboard with gadgets
	app.get(/^\/(dashboard|settings)$/, function(req, res, next) {
		//Find user's gadgets
		var userGadgets = { };
		_.each(gadgets, function(gadget, name) {
			if (req.user.gadgets[name.toLowerCase()]) {
				userGadgets[name] = new gadget(req.user);
			}
		});

		res.set({
			'Cache-Control': 'no-cache, no-store, must-revalidate',
			'Pragma': 'no-cache',
			'Expires': 0
		});

		//Regular request
		if (!req.xhr) {
			//Renew Facebook access token when expiry is imminent
			var fb = req.user.accounts.facebook;

			if (fb && new Date(fb.expires) - new Date <= 24 * 60 * 60 * 1000) {
				res.redirect(app.set('uri') + 'dashboard/facebook?_method=put');
			}

			//Build location string for usage in view
			var location = _.values(_.pick(req.user.location || [ ],
				[ 'area', 'province', 'country' ])).join(', ');

			//Render view
			res.render('dashboard', {
				stylesheet: 'dashboard',
				gadgets: gadgets,
				userGadgets: userGadgets,
				activeGadgetCount: _.size(userGadgets),
				location: location
			});

			app.winston.debug('%s %s complete', req.method, req.url);
		//AJAX request for live updating
		} else {
			//Find stale data
			var staleGadgets = _.filter(userGadgets, function(gadget, name) {
				var age = new Date - req.user.gadgets[name.toLowerCase()].lastUpdate;

				//Shortest interval is used for AJAX updating
				var minAge = _.min(gadgets[name].intervals) * 1000;

				//Also request update for gadgets that failed to update
				//as the retreat interval might have passed
				return age >= minAge || req.user.gadgets[name.toLowerCase()].consecutiveErrors > 0;
			});

			//Request immediate update of user
			app.queue.cutqueue(req.user, _.values(staleGadgets), function(updated) {
				//Return JSON
				var gadgetData = { };

				_.each(userGadgets, function(gadget, name) {
					if (gadget.consecutiveErrors < 4) {
						gadgetData[name.toLowerCase()] = gadget.value;
					} else {
						gadgetData[name.toLowerCase()] = null;
					}
				});

				res.json(gadgetData);

				app.winston.debug('%s %s complete', req.method, req.url);
			});
		}
	});
};<|endoftext|>


<commit_before>import React from "react"

import axios from "axios"

const renderChange = (change, i) => {
  return
{change}

}

export default class Changelog extends React.Component {

  constructor(props){
    super(props)
    this.state = {
      changes: null
    }
  }

  componentDidMount(){
    this._fetchChanges()
  }

  _fetchChanges(){
    axios.get('https://raw.githubusercontent.com/envkey/envkey-app/master/CHANGELOG.json')
         .then(({data}) => {
           this.setState({changes: data[this.props.version]})
         })
  }

  render(){
    return

{this._renderChanges()}

  }

  _renderChanges(){
    if (this.state.changes){
      return this.state.changes.map(renderChange)
    } else {
      return
Loading CHANGELOG…

    }
  }

}
<commit_msg>fix updater changelog<commit_after>import React from "react"

import axios from "axios"

const renderChange = (change, i) => {
  return
{change}

}

export default class Changelog extends React.Component {

  constructor(props){
    super(props)
    this.state = {
      changes: null
    }
  }

  componentDidMount(){
    this._fetchChanges()
  }

  _fetchChanges(){
    axios.get('https://raw.githubusercontent.com/envkey/envkey-app/master/CHANGELOG.json')
         .then(({data}) => {
           this.setState({changes: data})
         })
  }

  render(){
    return

{this._renderChanges()}

  }

  _renderChanges(){
    if (this.state.changes && this.props.version && this.state.changes[this.props.version]){
      return this.state.changes[this.props.version].map(renderChange)
    } else {
      return
Loading CHANGELOG…

    }
  }

}
<|endoftext|>


<commit_before>"use strict";

var modes = require('./modes');

// options.encrypt(plain) -> encrypted
// options.decrypt(encrypted) -> plain
// options.shouldEncrypt(path) -> boolean
// options.getRootTree() => hash
// options.setRootTree(hash) =>
module.exports = function (repo, options) {
  var toWrite = {};
  var callbacks = [];
  var writing = false;

  return {
    readFile: readFile,
    writeFile: writeFile,
    readDir: readDir
  };

  function readFile(path, callback) {
    if (!callback) return readFile.bind(null, path);
    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (!hash) return callback(err);
      repo.pathToEntry(hash, path, onEntry);
    }

    function onEntry(err, entry) {
      if (!entry || !modes.isFile(entry.mode)) return callback(err);
      repo.loadAs("blob", entry.hash, callback);
    }
  }

  function writeFile(path, binary, callback) {
    if (!callback) return writeFile.bind(null, path, binary);
    toWrite[path] = binary;
    callbacks.push(callback);
    check();
  }

  function readDir(path, callback) {
    if (!callback) return readDir.bind(null, path);

    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (!hash) return callback(err);
      repo.pathToEntry(hash, path, onEntry);
    }

    function onEntry(err, entry) {
      if (!entry || entry.mode !== modes.tree) return callback(err);
      repo.loadAs("tree", entry.hash, onTree);
    }

    function onTree(err, tree) {
      if (!tree) return callback(err);
      callback(null, Object.keys(tree));
    }
  }

  function check() {
    if (writing || !callbacks.length) return;
    writing = true;
    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (err) return callall(err);
      var files = pullFiles();
      if (hash) files.base = hash;
      repo.createTree(files, onNewTree);
    }

    function onNewTree(err, hash) {
      if (err) return callall(err);
      options.setRootTree(hash, onSaveRoot);

    }

    function onSaveRoot(err) {
      if (err) return callall(err);
      toWrite = {};
      writing = false;
      callall();
    }
  }

  function pullFiles() {
    var files = Object.keys(toWrite).map(function (path) {
      var content = toWrite[path];
      var mode = modes.blob;
      if (options.shouldEncrypt && options.shouldEncrypt(path)) {
        mode = modes.exec;
        content = options.encrypt(content);
      }
      return {
        path: path,
        mode: mode,
        content: content
      };
    });
    return files;
  }

  function callall(err) {
    callbacks.splice(0, callbacks.length).forEach(function (callback) {
      callback(err);
    });
  }
};
<commit_msg>Allow reading from write cache.<commit_after>"use strict";

var modes = require('./modes');

// options.encrypt(plain) -> encrypted
// options.decrypt(encrypted) -> plain
// options.shouldEncrypt(path) -> boolean
// options.getRootTree() => hash
// options.setRootTree(hash) =>
module.exports = function (repo, options) {
  var toWrite = {};
  var callbacks = [];
  var writing = false;

  return {
    readFile: readFile,
    writeFile: writeFile,
    readDir: readDir
  };

  function readFile(path, callback) {
    if (!callback) return readFile.bind(null, path);

    // If there is a pending write for this path, pull from the cache.
    if (toWrite[path]) return callback(null, toWrite[path]);

    // Otherwise read from the persistent storage
    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (!hash) return callback(err);
      repo.pathToEntry(hash, path, onEntry);
    }

    function onEntry(err, entry) {
      if (!entry || !modes.isFile(entry.mode)) return callback(err);
      repo.loadAs("blob", entry.hash, callback);
    }
  }

  function writeFile(path, binary, callback) {
    if (!callback) return writeFile.bind(null, path, binary);
    toWrite[path] = binary;
    callbacks.push(callback);
    check();
  }

  function readDir(path, callback) {
    if (!callback) return readDir.bind(null, path);

    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (!hash) return callback(err);
      repo.pathToEntry(hash, path, onEntry);
    }

    function onEntry(err, entry) {
      if (!entry || entry.mode !== modes.tree) return callback(err);
      repo.loadAs("tree", entry.hash, onTree);
    }

    function onTree(err, tree) {
      if (!tree) return callback(err);
      callback(null, Object.keys(tree));
    }
  }

  function check() {
    if (writing || !callbacks.length) return;
    writing = true;
    options.getRootTree(onRootTree);

    function onRootTree(err, hash) {
      if (err) return callall(err);
      var files = pullFiles();
      if (hash) files.base = hash;
      repo.createTree(files, onNewTree);
    }

    function onNewTree(err, hash) {
      if (err) return callall(err);
      options.setRootTree(hash, onSaveRoot);

    }

    function onSaveRoot(err) {
      if (err) return callall(err);
      toWrite = {};
      writing = false;
      callall();
    }
  }

  function pullFiles() {
    var files = Object.keys(toWrite).map(function (path) {
      var content = toWrite[path];
      var mode = modes.blob;
      if (options.shouldEncrypt && options.shouldEncrypt(path)) {
        mode = modes.exec;
        content = options.encrypt(content);
      }
      return {
        path: path,
        mode: mode,
        content: content
      };
    });
    return files;
  }

  function callall(err) {
    callbacks.splice(0, callbacks.length).forEach(function (callback) {
      callback(err);
    });
  }
};
<|endoftext|>


<commit_before>// Copyright (c) 2011 Andrew Paprocki

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var dgram = require('dgram');
var V4Address = require('ip-address').Address4;
var parser = require('./parser');
var protocol = require('./protocol');

function Client(options) {
    if (options) {
        if (typeof(options) !== 'object')
            throw new TypeError('options must be an object');
    } else {
        options = {};
    }

    var self = this;
    EventEmitter.call(this, options);

    this.client = dgram.createSocket('udp4');
    this.client.on('message', function(msg) {
        var pkt = parser.parse(msg);
        switch (pkt.options.dhcpMessageType.value) {
            case protocol.DHCPMessageType.DHCPOFFER.value:
                self.emit('dhcpOffer', pkt);
                break;
            case protocol.DHCPMessageType.DHCPACK.value:
                self.emit('dhcpAck', pkt);
                break;
            case protocol.DHCPMessageType.DHCPNAK.value:
                self.emit('dhcpNak', pkt);
                break;
        default:
        assert(!'Client: received unhandled DHCPMessageType ' +
               pkt.options.dhcpMessageType.value);
        }
    });
    this.client.on('listening', function() {
        var address = self.client.address();
        self.emit('listening', address.address + ':' + address.port);
    });
}
util.inherits(Client, EventEmitter);
module.exports = Client;

Client.prototype.bind = function(host, port, cb) {
    var that = this;
    if (!port) port = 68;
    this.client.bind(port, host, function() {
        that.client.setBroadcast(true);
        if (cb && cb instanceof Function) {
            process.nextTick(cb);
        }
    });
}

Client.prototype.broadcastPacket = function(pkt, options, cb) {
    var port = 67;
    var host = '255.255.255.255';
    if (options) {
        if ('port' in options) port = options.port;
        if ('host' in options) host = options.host;
    }
    this.client.send(pkt, 0, pkt.length, port, host, cb);
}

Client.prototype.createPacket = function(pkt) {
    if (!('xid' in pkt))
        throw new Error('pkt.xid required');

    var ci = new Buffer(('ciaddr' in pkt) ?
        new V4Address(pkt.ciaddr).toArray() : [0, 0, 0, 0]);
    var yi = new Buffer(('yiaddr' in pkt) ?
        new V4Address(pkt.yiaddr).toArray() : [0, 0, 0, 0]);
    var si = new Buffer(('siaddr' in pkt) ?
        new V4Address(pkt.siaddr).toArray() : [0, 0, 0, 0]);
    var gi = new Buffer(('giaddr' in pkt) ?
        new V4Address(pkt.giaddr).toArray() : [0, 0, 0, 0]);

    if (!('chaddr' in pkt))
        throw new Error('pkt.chaddr required');
    var hw = new Buffer(pkt.chaddr.split(':').map(function(part) {
        return parseInt(part, 16);
    }));
    if (hw.length !== 6)
        throw new Error('pkt.chaddr malformed, only ' + hw.length + ' bytes');

    var p = new Buffer(1500);
    var i = 0;

    p.writeUInt8(pkt.op,    i++);
    p.writeUInt8(pkt.htype, i++);
    p.writeUInt8(pkt.hlen,  i++);
    p.writeUInt8(pkt.hops,  i++);
    p.writeUInt32BE(pkt.xid,   i); i += 4;
    p.writeUInt16BE(pkt.secs,  i); i += 2;
    p.writeUInt16BE(pkt.flags, i); i += 2;
    ci.copy(p, i); i += ci.length;
    yi.copy(p, i); i += yi.length;
    si.copy(p, i); i += si.length;
    gi.copy(p, i); i += gi.length;
    hw.copy(p, i); i += hw.length;
    p.fill(0, i, i + 10); i += 10; // hw address padding
    p.fill(0, i, i + 192); i += 192;
    p.writeUInt32BE(0x63825363, i); i += 4;

    if (pkt.options && 'requestedIpAddress' in pkt.options) {
        p.writeUInt8(50, i++); // option 50
        var requestedIpAddress = new Buffer(
            new V4Address(pkt.options.requestedIpAddress).toArray());
        p.writeUInt8(requestedIpAddress.length, i++);
        requestedIpAddress.copy(p, i); i += requestedIpAddress.length;
    }
    if (pkt.options && 'dhcpMessageType' in pkt.options) {
        p.writeUInt8(53, i++); // option 53
        p.writeUInt8(1, i++);  // length
        p.writeUInt8(pkt.options.dhcpMessageType.value, i++);
    }
    if (pkt.options && 'serverIdentifier' in pkt.options) {
        p.writeUInt8(54, i++); // option 54
        var serverIdentifier = new Buffer(
            new V4Address(pkt.options.serverIdentifier).toArray());
        p.writeUInt8(serverIdentifier.length, i++);
        serverIdentifier.copy(p, i); i += serverIdentifier.length;
    }
    if (pkt.options && 'parameterRequestList' in pkt.options) {
        p.writeUInt8(55, i++); // option 55
        var parameterRequestList = new Buffer(pkt.options.parameterRequestList);
        if (parameterRequestList.length > 16)
            throw new Error('pkt.options.parameterRequestList malformed');
        p.writeUInt8(parameterRequestList.length, i++);
        parameterRequestList.copy(p, i); i += parameterRequestList.length;
    }
    if (pkt.options && 'clientIdentifier' in pkt.options) {
        var clientIdentifier = new Buffer(pkt.options.clientIdentifier);
        var optionLength = 1 + clientIdentifier.length;
        if (optionLength > 0xff)
            throw new Error('pkt.options.clientIdentifier malformed');
        p.writeUInt8(61, i++);           // option 61
        p.writeUInt8(optionLength, i++); // length
        p.writeUInt8(0, i++);            // hardware type 0
        clientIdentifier.copy(p, i); i += clientIdentifier.length;
    }
    if (pkt.options && 'vendorOptions' in pkt.options){
        for ( var key in pkt.options.vendorOptions) {
            var value = pkt.options.vendorOptions[key];
            p.writeUInt8(parseInt(key), i++); // option 'key'
            p.writeUInt8(value.length, i++); // length
            value.copy(p, i); i += value.length;
        }
    }

    // option 255 - end
    p.writeUInt8(0xff, i++);

    // padding
    if ((i % 2) > 0) {
        p.writeUInt8(0, i++);
    } else {
        p.writeUInt16BE(0, i++);
    }

    var remaining = 300 - i;
    if (remaining) {
        p.fill(0, i, i + remaining); i+= remaining;
    }

    //console.log('createPacket:', i, 'bytes');
    return p.slice(0, i);
}

Client.prototype.createDiscoverPacket = function(user) {
    var pkt = {
        op:     0x01,
        htype:  0x01,
        hlen:   0x06,
        hops:   0x00,
        xid:    0x00000000,
        secs:   0x0000,
        flags:  0x0000,
        ciaddr: '0.0.0.0',
        yiaddr: '0.0.0.0',
        siaddr: '0.0.0.0',
        giaddr: '0.0.0.0',
    };
    if ('xid' in user) pkt.xid = user.xid;
    if ('chaddr' in user) pkt.chaddr = user.chaddr;
    if ('options' in user) pkt.options = user.options;
    return Client.prototype.createPacket(pkt);
}
<commit_msg>Whitespace formatting<commit_after>// Copyright (c) 2011 Andrew Paprocki

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var dgram = require('dgram');
var V4Address = require('ip-address').Address4;
var parser = require('./parser');
var protocol = require('./protocol');

function Client(options) {
    if (options) {
        if (typeof(options) !== 'object')
            throw new TypeError('options must be an object');
    } else {
        options = {};
    }

    var self = this;
    EventEmitter.call(this, options);

    this.client = dgram.createSocket('udp4');
    this.client.on('message', function(msg) {
        var pkt = parser.parse(msg);
        switch (pkt.options.dhcpMessageType.value) {
            case protocol.DHCPMessageType.DHCPOFFER.value:
                self.emit('dhcpOffer', pkt);
                break;
            case protocol.DHCPMessageType.DHCPACK.value:
                self.emit('dhcpAck', pkt);
                break;
            case protocol.DHCPMessageType.DHCPNAK.value:
                self.emit('dhcpNak', pkt);
                break;
        default:
        assert(!'Client: received unhandled DHCPMessageType ' +
               pkt.options.dhcpMessageType.value);
        }
    });
    this.client.on('listening', function() {
        var address = self.client.address();
        self.emit('listening', address.address + ':' + address.port);
    });
}
util.inherits(Client, EventEmitter);
module.exports = Client;

Client.prototype.bind = function(host, port, cb) {
    var that = this;
    if (!port) port = 68;
    this.client.bind(port, host, function() {
        that.client.setBroadcast(true);
        if (cb && cb instanceof Function) {
            process.nextTick(cb);
        }
    });
}

Client.prototype.broadcastPacket = function(pkt, options, cb) {
    var port = 67;
    var host = '255.255.255.255';
    if (options) {
        if ('port' in options) port = options.port;
        if ('host' in options) host = options.host;
    }
    this.client.send(pkt, 0, pkt.length, port, host, cb);
}

Client.prototype.createPacket = function(pkt) {
    if (!('xid' in pkt))
        throw new Error('pkt.xid required');

    var ci = new Buffer(('ciaddr' in pkt) ?
        new V4Address(pkt.ciaddr).toArray() : [0, 0, 0, 0]);
    var yi = new Buffer(('yiaddr' in pkt) ?
        new V4Address(pkt.yiaddr).toArray() : [0, 0, 0, 0]);
    var si = new Buffer(('siaddr' in pkt) ?
        new V4Address(pkt.siaddr).toArray() : [0, 0, 0, 0]);
    var gi = new Buffer(('giaddr' in pkt) ?
        new V4Address(pkt.giaddr).toArray() : [0, 0, 0, 0]);

    if (!('chaddr' in pkt))
        throw new Error('pkt.chaddr required');
    var hw = new Buffer(pkt.chaddr.split(':').map(function(part) {
        return parseInt(part, 16);
    }));
    if (hw.length !== 6)
        throw new Error('pkt.chaddr malformed, only ' + hw.length + ' bytes');

    var p = new Buffer(1500);
    var i = 0;

    p.writeUInt8(pkt.op,    i++);
    p.writeUInt8(pkt.htype, i++);
    p.writeUInt8(pkt.hlen,  i++);
    p.writeUInt8(pkt.hops,  i++);
    p.writeUInt32BE(pkt.xid,   i); i += 4;
    p.writeUInt16BE(pkt.secs,  i); i += 2;
    p.writeUInt16BE(pkt.flags, i); i += 2;
    ci.copy(p, i); i += ci.length;
    yi.copy(p, i); i += yi.length;
    si.copy(p, i); i += si.length;
    gi.copy(p, i); i += gi.length;
    hw.copy(p, i); i += hw.length;
    p.fill(0, i, i + 10); i += 10; // hw address padding
    p.fill(0, i, i + 192); i += 192;
    p.writeUInt32BE(0x63825363, i); i += 4;

    if (pkt.options && 'requestedIpAddress' in pkt.options) {
        p.writeUInt8(50, i++); // option 50
        var requestedIpAddress = new Buffer(
            new V4Address(pkt.options.requestedIpAddress).toArray());
        p.writeUInt8(requestedIpAddress.length, i++);
        requestedIpAddress.copy(p, i); i += requestedIpAddress.length;
    }
    if (pkt.options && 'dhcpMessageType' in pkt.options) {
        p.writeUInt8(53, i++); // option 53
        p.writeUInt8(1, i++);  // length
        p.writeUInt8(pkt.options.dhcpMessageType.value, i++);
    }
    if (pkt.options && 'serverIdentifier' in pkt.options) {
        p.writeUInt8(54, i++); // option 54
        var serverIdentifier = new Buffer(
            new V4Address(pkt.options.serverIdentifier).toArray());
        p.writeUInt8(serverIdentifier.length, i++);
        serverIdentifier.copy(p, i); i += serverIdentifier.length;
    }
    if (pkt.options && 'parameterRequestList' in pkt.options) {
        p.writeUInt8(55, i++); // option 55
        var parameterRequestList = new Buffer(pkt.options.parameterRequestList);
        if (parameterRequestList.length > 16)
            throw new Error('pkt.options.parameterRequestList malformed');
        p.writeUInt8(parameterRequestList.length, i++);
        parameterRequestList.copy(p, i); i += parameterRequestList.length;
    }
    if (pkt.options && 'clientIdentifier' in pkt.options) {
        var clientIdentifier = new Buffer(pkt.options.clientIdentifier);
        var optionLength = 1 + clientIdentifier.length;
        if (optionLength > 0xff)
            throw new Error('pkt.options.clientIdentifier malformed');
        p.writeUInt8(61, i++);           // option 61
        p.writeUInt8(optionLength, i++); // length
        p.writeUInt8(0, i++);            // hardware type 0
        clientIdentifier.copy(p, i); i += clientIdentifier.length;
    }
    if (pkt.options && 'vendorOptions' in pkt.options) {
        for (var key in pkt.options.vendorOptions) {
            var value = pkt.options.vendorOptions[key];
            p.writeUInt8(parseInt(key), i++); // option 'key'
            p.writeUInt8(value.length, i++);  // length
            value.copy(p, i); i += value.length;
        }
    }

    // option 255 - end
    p.writeUInt8(0xff, i++);

    // padding
    if ((i % 2) > 0) {
        p.writeUInt8(0, i++);
    } else {
        p.writeUInt16BE(0, i++);
    }

    var remaining = 300 - i;
    if (remaining) {
        p.fill(0, i, i + remaining); i+= remaining;
    }

    //console.log('createPacket:', i, 'bytes');
    return p.slice(0, i);
}

Client.prototype.createDiscoverPacket = function(user) {
    var pkt = {
        op:     0x01,
        htype:  0x01,
        hlen:   0x06,
        hops:   0x00,
        xid:    0x00000000,
        secs:   0x0000,
        flags:  0x0000,
        ciaddr: '0.0.0.0',
        yiaddr: '0.0.0.0',
        siaddr: '0.0.0.0',
        giaddr: '0.0.0.0',
    };
    if ('xid' in user) pkt.xid = user.xid;
    if ('chaddr' in user) pkt.chaddr = user.chaddr;
    if ('options' in user) pkt.options = user.options;
    return Client.prototype.createPacket(pkt);
}
<|endoftext|>