<commit_before>import { CHILD_CARE_EXPENSES } from '../../../data/state/massachusetts/name-cores';
import {
  toCashflow,
  sumCashflow,
  getGrossUnearnedIncomeMonthly
} from '../../../utils/cashflow';
import { Result } from '../../../utils/Result';
import { data } from '../../../config/snap';
import { getYearlyLimitBySize, getMonthlyLimitBySize } from '../../../utils/getGovData';
import { federalPovertyGuidelines } from '../../../data/federal/federalPovertyGuidelines';

const getSNAPBenefits = function ( client ) {
  var timeframe = 'current';

  // var missingProps = propsNeeded(client, requiredProps);

  // if (missingProps.length) {
  //   var details = 'Some required form fields have\'t been filled in yet.';
  //   var result = new Result({
  //     result: 'incomplete',
  //     details: 'Form incomplete',
  //     data: { missingProps: missingProps }
  //   });
  //   return result;
  // }

  var finalResult = null;
  var grossIncomeTestResult   = getGrossIncomeTestResult(client, timeframe);
  var netIncomeTestResult     = getNetIncomeTestResult(client, timeframe);
  var maxSnapAllotment        = getMaxSnapAllotment(client, timeframe);
  var thirtyPercentNetIncome  = getThirtyPercentNetIncome(client, timeframe);
  var maxClientAllotment      = maxSnapAllotment - thirtyPercentNetIncome;

  if (grossIncomeTestResult === true &&  netIncomeTestResult === true) {

    if ( maxClientAllotment <= data.smallHouseholdMinimumGrant ) {
      if (client[timeframe + 'HouseholdSize'] <= data.minHouseholdSize) {
        finalResult = data.smallHouseholdMinimumGrant;
      } else {
        finalResult = 0;
      }
    } else {
      finalResult = maxClientAllotment;
    }
  } else {
    finalResult = 0;
  }

  var result = new Result({
      result: 'good',
      details: 'IT IS AWESOME!!!',
      benefitValue: finalResult
    });
  return result;
}; // End getSNAPBenefits()

//Still needed, commented out requiredProps[] for removing warning
// const requiredProps = [
//   'currentDisabledOrElderlyMember',
//   'futureEarnedIncomeMonthly',
//   'currentTAFDCMonthly',
//   'currentSSIMonthly',
//   'currentSSDIMonthly',
//   'currentChildSupportInMonthly',
//   'currentUnemploymentMonthly',
//   'currentWorkersCompMonthly',
//   'currentPensionMonthly',
//   'currentSocialSecurityMonthly',
//   'currentAlimonyMonthly',
//   'currentOtherIncomeMonthly',
//   'currentHouseoldSize',
//   'currentChildSupportPaidOutMonthly',
//   'currentChildDirectCareCostsMonthly',
//   'currentChildBeforeAndAfterSchoolCareCostsMonthly',
//   'currentChildTransportationCostsMonthly',
//   'currentChildOtherCareCostsMonthly',
//   'currentAdultDirectCareCostsMonthly',
//   'currentAdultTransportationCostsMonthly',
//   'currentAdultOtherCareCostsMonthly',
//   'currentDisabledMedicalCostsMonthly',
//   'currentHomeless',
//   'currentMortgageMonthly',
//   'currentHousingInsuranceMonthly',
//   'currentPropertyTaxMonthly',
//   'currentPaidUtilities',
//   'currentClimateControl',
//   'currentNonHeatElectricity',
//   'currentPhone',
//
//
//   // 'currentHomeowner',
//   /* All question marks in spreadsheet
//   'currentIncomeExclusionsMonthly',
//   'currentDisabledAssistanceMonthly',
//   'currentOtherMedicalCostsMonthly',
//   'currentRentShareMonthly',
//   'currentContractRentMonthly',
//   'currentRentMonthly'
//   */
// ];

// const getAllowance = function(client, timeframe, data, baseRate ) {
//   if ( client[timeframe + 'HouseholdSize'] > 8  ) {
//     return data[8] + (client[timeframe + 'HouseholdSize'] - 8) * baseRate;
//   } else {
//     return data[client[timeframe + 'HouseholdSize']];
//   }
// };

//GROSS INCOME TEST
const hasDisabledOrElderlyMember = function (client, timeframe) {
  return client[timeframe + 'DisabledOrElderlyMember'];
};

const getTotalMonthlyGross = function (client, timeframe) {
  // return getSimpleGrossIncomeMonthly(client, timeframe) - toCashflow(client, timeframe, 'ChildSupportPaidOut');
  return toCashflow(client, 'future', 'EarnedIncome') + getGrossUnearnedIncomeMonthly(client, timeframe) - toCashflow(client, timeframe, 'ChildSupportPaidOut');
};

const getPovertyGrossIncomeLevel = function (client, timeframe ) {
  return getMonthlyLimitBySize(federalPovertyGuidelines, client[timeframe + 'HouseholdSize'], 200);
   // return getAllowance(client, timeframe, data.povertyGrossIncome, data.overNumberHouseholdRate);
};

const checkIncome = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe);
  var povertyGrossIncomeLevel = getPovertyGrossIncomeLevel(client, timeframe);
  var isDisabledOrElderlyMember = hasDisabledOrElderlyMember(client, timeframe);
  if ((totalMonthlyGross > povertyGrossIncomeLevel) && isDisabledOrElderlyMember) {
    return true;
  } else {
    return false;
  }
};

// const isAssetTest = function(client, timeframe) {
//   if (checkIncome(client, timeframe)) {
//     return true; // Yes, "Yes, assets must be <=$3,250"
//   } else {
//     return false; //No
//   }
// };

const isNetIncomeTest = function(client, timeframe) {
  if (checkIncome(client, timeframe)) {
    return true;
  } else {
    return false;
  }
};

const getGrossIncomeTestResult = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe);
  var povertyGrossIncomeLevel = getPovertyGrossIncomeLevel(client, timeframe);
  var isPassGrossIncomeTest = null;
  if ( hasDisabledOrElderlyMember(client, timeframe) ) {
    isPassGrossIncomeTest = true;
  } else {
    // TODO: must double checked in the documentation. Two different results in both excel calculator and website calculator
    // minor difference "<" in website calculator logic on line 469.
    if ( totalMonthlyGross <= povertyGrossIncomeLevel ) {
      isPassGrossIncomeTest = true;
    } else {
      isPassGrossIncomeTest = false;
    }
  }
  return isPassGrossIncomeTest;
};

// INCOME DEDUCTIONS
const getStandardDeduction = function (client, timeframe) {
  return getYearlyLimitBySize(data.standardDeduction, client[timeframe + 'HouseholdSize']);
};

const getEarnedIncomeDeduction = function (client, timeframe) {
  var totalMonthlyEarnedGross = toCashflow(client, 'future', 'EarnedIncome');
  return totalMonthlyEarnedGross * data.percentOfGrossMonthlyEarnedIncome;
};

const getMedicalDeduction = function (client, timeframe) {
  var medicalDeduce = null;
  if (client[timeframe + 'DisabledOrElderlyMember'] === false) {
    return 0;
  } else {
    // include currentDisabledMedicalCostsMonthly,  currentOtherMedicalCostsMonthly ??
    var medicalExpenses = client[timeframe + 'DisabledAssistanceMonthly'];
    if ((medicalExpenses >= data.beginRangeMedicalExpensesThreshold) && (medicalExpenses <= data.endRangeMedicalExpensesThreshold)) {
      medicalDeduce = data.standardMedicalDeduction;
      return medicalDeduce;
    } else {
      if (medicalExpenses >= data.endRangeMedicalExpensesThreshold++) {
        medicalDeduce = medicalExpenses - data.beginRangeMedicalExpensesThreshold;
        return medicalDeduce;
      }
    }
  }
  return 0;
};

const getDependentCareDeduction = function (client, timeframe) {
  //add ADULT_CARE_EXPENSES to name-cores.js ???
  const ADULT_CARE_EXPENSES = ['AdultDirectCareCosts', 'AdultTransportationCosts', 'AdultOtherCareCosts'];
  var totalDependentCare = sumCashflow( client, timeframe, CHILD_CARE_EXPENSES ) + sumCashflow( client, timeframe, ADULT_CARE_EXPENSES );

  return totalDependentCare;
};

const getChildPaymentDeduction = function (client, timeframe) {
    return toCashflow(client, timeframe, 'ChildSupportPaidOut');
};

const getAdjustedIncomeAfterDeduction = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe)
  var standardDeduction = getStandardDeduction(client, timeframe);
  var earnedIncomeDeduction = getEarnedIncomeDeduction(client, timeframe);
  var medicalDeduction = getMedicalDeduction(client, timeframe);
  var dependentCareDeduction = getDependentCareDeduction(client,timeframe);

  var totalDeduction = totalMonthlyGross - standardDeduction - earnedIncomeDeduction - medicalDeduction - dependentCareDeduction;

  if ( totalDeduction < 0  ) {
    return 0;
  }
  return  totalDeduction;
};

// EXPENSE DEDUCTIONS
const isHomeless = function(client, timeframe ) {
  return client[timeframe + 'Shelter' === 'homeless'];
};

const getShelterDeduction = function(client, timeframe) {
  var shelterCost = null;
  var isHomeowner = client[timeframe + 'Shelter'] === 'homeowner';

  if ( isHomeless(client, timeframe) ) {
    shelterCost = 0;
  } else if(isHomeowner) {
    shelterCost = toCashflow(client, timeframe, 'Mortgage') + toCashflow(client, timeframe, 'HousingInsurance') + toCashflow(client, timeframe, 'PropertyTax');
  }
  else {
    shelterCost = toCashflow(client, timeframe, 'Rent');
  }

  return shelterCost;
};

const utilityStatus = function(client, timeframe) {
  var paidUtilityCategory = null;
  var isPayHeatingCooling = client[timeframe + 'ClimateControl'] || client[timeframe + 'HasFuelAssistance'];
  var isPayElectricity = client[timeframe + 'NonHeatElectricity'];
  var isPayTelephone = client[timeframe + 'Phone'];
  if ( isPayHeatingCooling ) {
    paidUtilityCategory = "Heating";
  } else if (isPayElectricity) {
    paidUtilityCategory = "Non-heating";
  } else if (isPayTelephone) {
    paidUtilityCategory = "Telephone";
  } else {
    paidUtilityCategory = "Zero Utility Expenses";
  }
  return paidUtilityCategory;
};

const getStandardUtilityAllowance = function (client, timeframe) {
  if(isHomeless(client, timeframe)){
    return 0;
  }else{
    var paidUtilityCategory = utilityStatus(client, timeframe);
    return data.standardUtilityAllowance[paidUtilityCategory];
  }
};

const getTotalshelterCost = function (client, timeframe) {
  var shelterDeduction = getShelterDeduction(client, timeframe);
  var standardUtilityAllowance = getStandardUtilityAllowance(client, timeframe);

  return shelterDeduction + standardUtilityAllowance;
};

const getHalfAdjustedIncome = function(client, timeframe ) {
  var adjustedIncomeAfterDeduction = getAdjustedIncomeAfterDeduction(client, timeframe);
  return adjustedIncomeAfterDeduction * 0.50;
};

const excessHalfAdjustedIncome = function(client, timeframe ) {
  var totalShelterDeduction = null;
  var totalshelterCost = getTotalshelterCost(client, timeframe);
  var halfAdjustedIncome = getHalfAdjustedIncome(client, timeframe);
  if ( totalshelterCost - halfAdjustedIncome < 0   ) {
    totalShelterDeduction = 0;
  } else {
    totalShelterDeduction = totalshelterCost - halfAdjustedIncome;
  }
  return totalShelterDeduction;
};

const getShelterDeductionResult = function(client, timeframe ) {
    if ( hasDisabledOrElderlyMember(client, timeframe) ) {
      return excessHalfAdjustedIncome(client, timeframe);
    } else {
      return Math.min(excessHalfAdjustedIncome(client, timeframe), data.standardShelterDeductionCap);
    }
};

const getHomelessDeduction = function(client, timeframe ) {
    if ( isHomeless(client, timeframe) ) {
      return data.homelessDeduction;
    } else {
      return 0;
    }
};

// NET INCOME CALCULATION
const monthlyNetIncome = function(client, timeframe ) {
    var totalMonthlyEarnedGross = toCashflow(client, 'future', 'EarnedIncome');
    var earnedIncomeDeduction = getEarnedIncomeDeduction(client, timeframe);
    var totalMonthlyUnearnedGross =  getGrossUnearnedIncomeMonthly(client, timeframe);
    var standardDeduction = getStandardDeduction(client, timeframe);
    var medicalDeduction = getMedicalDeduction(client, timeframe);
    var dependentCareDeduction = getDependentCareDeduction(client,timeframe);
    var childPaymentDeduction = getChildPaymentDeduction(client, timeframe);
    var hasHomelessDeduction = getHomelessDeduction(client, timeframe);
    var shelterDeductionResult = getShelterDeductionResult(client, timeframe);

    var totalIncome     = totalMonthlyEarnedGross + totalMonthlyUnearnedGross;
    var totalDeductions = earnedIncomeDeduction + standardDeduction + medicalDeduction
                        + hasHomelessDeduction + shelterDeductionResult
                        + dependentCareDeduction + childPaymentDeduction;

    return totalIncome - totalDeductions;
};

const maxTotalNetMonthlyIncome = function (client, timeframe) {
    var maxTotalNetIncome = null;
    //TODO: Logic different in website calculate; when (monthlyNetIncome < 0 ) = 0 while excel return a number
    if ( isNetIncomeTest(client, timeframe)===false ) {
      maxTotalNetIncome = "no limit";
      return maxTotalNetIncome;
    } else {
      return getYearlyLimitBySize(data.maxAllowableMonthlyNetIncome, client[timeframe + 'HouseholdSize']);
      //return getAllowance(client, timeframe, data.maxAllowableMonthlyNetIncome, data.maxAllowableMonthlyNetIncomeRate);
    }
};

// NET INCOME TEST RESULT
const getNetIncomeTestResult = function(client, timeframe ) {
  if ( maxTotalNetMonthlyIncome(client, timeframe) === "no limit" ) {
      return true;
    } else if ( monthlyNetIncome(client, timeframe) < maxTotalNetMonthlyIncome(client, timeframe) ) {
      return true;
    }
    else {
      return false;
    }
};

// FINAL DETERMINATION
const getThirtyPercentNetIncome = function(client, timeframe) {
  if ( monthlyNetIncome(client, timeframe) * data.percentOfIncome > 0 ) {
    return monthlyNetIncome(client, timeframe) * data.percentOfIncome;
  }
  return 0;
};

const getMaxSnapAllotment = function (client, timeframe) {
  return getYearlyLimitBySize(data.maxFoodStampAllotment, client[timeframe + 'HouseholdSize']);
  // return getAllowance(client, timeframe, data.maxFoodStampAllotment, data.maxFoodStampAllotmentRate);
};

// const bayStateCapCalculation = function (client, timeframe) {
//   var totalMonthlyEarnedGross = toCashflow(client, 'future', 'EarnedIncome');
//   var unearnedMonthlyIncome = getGrossUnearnedIncomeMonthly(client, timeframe);
//   var standardDeduction = getStandardDeduction(client, timeframe);
//   var shelterDeduction = getShelterDeduction(client, timeframe);
//   var income = unearnedMonthlyIncome - standardDeduction;
//   var halfIncome = income/2;
//   var maxFoodStamp = data.maxFoodStamp;
//   var imputedShelterExpense = null;
//   var standardUtilityAllowance = null;
//   var totalShelterCost = null;
//   var shelterDeduce = null;
//   var adjustedIncome = null;
//   var percentAdjustedIncome = null;
//
//   if ( client[timeframe + 'DisabledOrElderlyMember'] && totalMonthlyEarnedGross === 0  && client[timeframe + 'HouseholdSize']===1 ) {
//     (shelterDeduction >= 453)? imputedShelterExpense = 453: imputedShelterExpense = 223;
//     standardUtilityAllowance = 634;
//     totalShelterCost = imputedShelterExpense + standardUtilityAllowance;
//     shelterDeduce = totalShelterCost - halfIncome;
//     adjustedIncome = income - shelterDeduce;
//
//     if ( adjustedIncome > 0 ) {
//       percentAdjustedIncome = adjustedIncome * data.percentOfIncome;
//     } else {
//       percentAdjustedIncome = 0
//     }
//
//     if ( (maxFoodStamp - percentAdjustedIncome) > 0 ) {
//       return maxFoodStamp - percentAdjustedIncome;
//     } else {
//       return 0;
//     }
//   } else {
//     return 0;
//   }
// };
//
//
// const propsNeeded = function (client, props) {
//
//   var missingProps = [];
//
//   for (let propi = 0; propi < props.length; propi++) {
//     let key = props[propi];
//     if (client[key] === undefined) {
//       missingProps.push(key);
//     }
//   }
//
//   return missingProps;
// };

export { getSNAPBenefits };

// https://www.masslegalservices.org/SNAPCalculator

// REVIEW: Can this dead code be removed?
// It's the only consumer of helperFunctions.js,
// which is the only consumer of getGovData.js.
// If we don't need this dead code, we can cut out
// two more files for free.

//import { percentPovertyLevel,
//    percentStateMedianIncome } from '../../../utils/helperFunctions';
//
//function getSnapEligibility(client) {
//    let percentPov = percentPovertyLevel(parseInt(client.annualIncome), client.householdSize);
//    if (client.annualIncome == 0 || percentPov < 70) {
//        return {result: 'good', details: 'All good!', benefitValue: 1000};
//    } else if ( percentPov > 70 && percentPov < 80) {
//        return {result: 'information', details: `Your income puts you at ${percentPov.toFixed()}% of the federal poverty level, which is close to the 80% limit.`, benefitValue: 1000};
//    } else {
//        return {result: 'warning', details: `Your income puts you at ${percentPov.toFixed()}% of the federal poverty level, which is above the 80% limit.`, benefitValue: 0};
//    }
//}
//
//export {getSnapEligibility};
<commit_msg>Cleans up commented code that won't see the light of day<commit_after>import { CHILD_CARE_EXPENSES } from '../../../data/state/massachusetts/name-cores';
import {
  toCashflow,
  sumCashflow,
  getGrossUnearnedIncomeMonthly
} from '../../../utils/cashflow';
import { Result } from '../../../utils/Result';
import { data } from '../../../config/snap';
import { getYearlyLimitBySize, getMonthlyLimitBySize } from '../../../utils/getGovData';
import { federalPovertyGuidelines } from '../../../data/federal/federalPovertyGuidelines';

/** Based on https://www.masslegalservices.org/SNAPCalculator */
const getSNAPBenefits = function ( client ) {
  var timeframe = 'current';

  var finalResult = null;
  var grossIncomeTestResult   = getGrossIncomeTestResult(client, timeframe);
  var netIncomeTestResult     = getNetIncomeTestResult(client, timeframe);
  var maxSnapAllotment        = getMaxSnapAllotment(client, timeframe);
  var thirtyPercentNetIncome  = getThirtyPercentNetIncome(client, timeframe);
  var maxClientAllotment      = maxSnapAllotment - thirtyPercentNetIncome;

  if (grossIncomeTestResult === true &&  netIncomeTestResult === true) {

    if ( maxClientAllotment <= data.smallHouseholdMinimumGrant ) {
      if (client[timeframe + 'HouseholdSize'] <= data.minHouseholdSize) {
        finalResult = data.smallHouseholdMinimumGrant;
      } else {
        finalResult = 0;
      }
    } else {
      finalResult = maxClientAllotment;
    }
  } else {
    finalResult = 0;
  }

  var result = new Result({
      result: 'good',
      details: 'IT IS AWESOME!!!',
      benefitValue: finalResult
    });
  return result;
}; // End getSNAPBenefits()

//GROSS INCOME TEST
const hasDisabledOrElderlyMember = function (client, timeframe) {
  return client[timeframe + 'DisabledOrElderlyMember'];
};

const getTotalMonthlyGross = function (client, timeframe) {
  return toCashflow(client, 'future', 'EarnedIncome') + getGrossUnearnedIncomeMonthly(client, timeframe) - toCashflow(client, timeframe, 'ChildSupportPaidOut');
};

const getPovertyGrossIncomeLevel = function (client, timeframe ) {
  return getMonthlyLimitBySize(federalPovertyGuidelines, client[timeframe + 'HouseholdSize'], 200);
};

const checkIncome = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe);
  var povertyGrossIncomeLevel = getPovertyGrossIncomeLevel(client, timeframe);
  var isDisabledOrElderlyMember = hasDisabledOrElderlyMember(client, timeframe);
  if ((totalMonthlyGross > povertyGrossIncomeLevel) && isDisabledOrElderlyMember) {
    return true;
  } else {
    return false;
  }
};

const isNetIncomeTest = function(client, timeframe) {
  if (checkIncome(client, timeframe)) {
    return true;
  } else {
    return false;
  }
};

const getGrossIncomeTestResult = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe);
  var povertyGrossIncomeLevel = getPovertyGrossIncomeLevel(client, timeframe);
  var isPassGrossIncomeTest = null;
  if ( hasDisabledOrElderlyMember(client, timeframe) ) {
    isPassGrossIncomeTest = true;
  } else {
    // TODO: must double checked in the documentation. Two different results in both excel calculator and website calculator
    // minor difference "<" in website calculator logic on line 469.
    if ( totalMonthlyGross <= povertyGrossIncomeLevel ) {
      isPassGrossIncomeTest = true;
    } else {
      isPassGrossIncomeTest = false;
    }
  }
  return isPassGrossIncomeTest;
};

// INCOME DEDUCTIONS
const getStandardDeduction = function (client, timeframe) {
  return getYearlyLimitBySize(data.standardDeduction, client[timeframe + 'HouseholdSize']);
};

const getEarnedIncomeDeduction = function (client, timeframe) {
  var totalMonthlyEarnedGross = toCashflow(client, 'future', 'EarnedIncome');
  return totalMonthlyEarnedGross * data.percentOfGrossMonthlyEarnedIncome;
};

const getMedicalDeduction = function (client, timeframe) {
  var medicalDeduce = null;
  if (client[timeframe + 'DisabledOrElderlyMember'] === false) {
    return 0;
  } else {
    // include currentDisabledMedicalCostsMonthly,  currentOtherMedicalCostsMonthly ??
    var medicalExpenses = client[timeframe + 'DisabledAssistanceMonthly'];
    if ((medicalExpenses >= data.beginRangeMedicalExpensesThreshold) && (medicalExpenses <= data.endRangeMedicalExpensesThreshold)) {
      medicalDeduce = data.standardMedicalDeduction;
      return medicalDeduce;
    } else {
      if (medicalExpenses >= data.endRangeMedicalExpensesThreshold++) {
        medicalDeduce = medicalExpenses - data.beginRangeMedicalExpensesThreshold;
        return medicalDeduce;
      }
    }
  }
  return 0;
};

const getDependentCareDeduction = function (client, timeframe) {
  //add ADULT_CARE_EXPENSES to name-cores.js ???
  const ADULT_CARE_EXPENSES = ['AdultDirectCareCosts', 'AdultTransportationCosts', 'AdultOtherCareCosts'];
  var totalDependentCare = sumCashflow( client, timeframe, CHILD_CARE_EXPENSES ) + sumCashflow( client, timeframe, ADULT_CARE_EXPENSES );

  return totalDependentCare;
};

const getChildPaymentDeduction = function (client, timeframe) {
    return toCashflow(client, timeframe, 'ChildSupportPaidOut');
};

const getAdjustedIncomeAfterDeduction = function (client, timeframe) {
  var totalMonthlyGross = getTotalMonthlyGross(client, timeframe)
  var standardDeduction = getStandardDeduction(client, timeframe);
  var earnedIncomeDeduction = getEarnedIncomeDeduction(client, timeframe);
  var medicalDeduction = getMedicalDeduction(client, timeframe);
  var dependentCareDeduction = getDependentCareDeduction(client,timeframe);

  var totalDeduction = totalMonthlyGross - standardDeduction - earnedIncomeDeduction - medicalDeduction - dependentCareDeduction;

  if ( totalDeduction < 0  ) {
    return 0;
  }
  return  totalDeduction;
};

// EXPENSE DEDUCTIONS
const isHomeless = function(client, timeframe ) {
  return client[timeframe + 'Shelter' === 'homeless'];
};

const getShelterDeduction = function(client, timeframe) {
  var shelterCost = null;
  var isHomeowner = client[timeframe + 'Shelter'] === 'homeowner';

  if ( isHomeless(client, timeframe) ) {
    shelterCost = 0;
  } else if(isHomeowner) {
    shelterCost = toCashflow(client, timeframe, 'Mortgage') + toCashflow(client, timeframe, 'HousingInsurance') + toCashflow(client, timeframe, 'PropertyTax');
  }
  else {
    shelterCost = toCashflow(client, timeframe, 'Rent');
  }

  return shelterCost;
};

const utilityStatus = function(client, timeframe) {
  var paidUtilityCategory = null;
  var isPayHeatingCooling = client[timeframe + 'ClimateControl'] || client[timeframe + 'HasFuelAssistance'];
  var isPayElectricity = client[timeframe + 'NonHeatElectricity'];
  var isPayTelephone = client[timeframe + 'Phone'];
  if ( isPayHeatingCooling ) {
    paidUtilityCategory = "Heating";
  } else if (isPayElectricity) {
    paidUtilityCategory = "Non-heating";
  } else if (isPayTelephone) {
    paidUtilityCategory = "Telephone";
  } else {
    paidUtilityCategory = "Zero Utility Expenses";
  }
  return paidUtilityCategory;
};

const getStandardUtilityAllowance = function (client, timeframe) {
  if(isHomeless(client, timeframe)){
    return 0;
  }else{
    var paidUtilityCategory = utilityStatus(client, timeframe);
    return data.standardUtilityAllowance[paidUtilityCategory];
  }
};

const getTotalshelterCost = function (client, timeframe) {
  var shelterDeduction = getShelterDeduction(client, timeframe);
  var standardUtilityAllowance = getStandardUtilityAllowance(client, timeframe);

  return shelterDeduction + standardUtilityAllowance;
};

const getHalfAdjustedIncome = function(client, timeframe ) {
  var adjustedIncomeAfterDeduction = getAdjustedIncomeAfterDeduction(client, timeframe);
  return adjustedIncomeAfterDeduction * 0.50;
};

const excessHalfAdjustedIncome = function(client, timeframe ) {
  var totalShelterDeduction = null;
  var totalshelterCost = getTotalshelterCost(client, timeframe);
  var halfAdjustedIncome = getHalfAdjustedIncome(client, timeframe);
  if ( totalshelterCost - halfAdjustedIncome < 0   ) {
    totalShelterDeduction = 0;
  } else {
    totalShelterDeduction = totalshelterCost - halfAdjustedIncome;
  }
  return totalShelterDeduction;
};

const getShelterDeductionResult = function(client, timeframe ) {
    if ( hasDisabledOrElderlyMember(client, timeframe) ) {
      return excessHalfAdjustedIncome(client, timeframe);
    } else {
      return Math.min(excessHalfAdjustedIncome(client, timeframe), data.standardShelterDeductionCap);
    }
};

const getHomelessDeduction = function(client, timeframe ) {
    if ( isHomeless(client, timeframe) ) {
      return data.homelessDeduction;
    } else {
      return 0;
    }
};

// NET INCOME CALCULATION
const monthlyNetIncome = function(client, timeframe ) {
    var totalMonthlyEarnedGross = toCashflow(client, 'future', 'EarnedIncome');
    var earnedIncomeDeduction = getEarnedIncomeDeduction(client, timeframe);
    var totalMonthlyUnearnedGross =  getGrossUnearnedIncomeMonthly(client, timeframe);
    var standardDeduction = getStandardDeduction(client, timeframe);
    var medicalDeduction = getMedicalDeduction(client, timeframe);
    var dependentCareDeduction = getDependentCareDeduction(client,timeframe);
    var childPaymentDeduction = getChildPaymentDeduction(client, timeframe);
    var hasHomelessDeduction = getHomelessDeduction(client, timeframe);
    var shelterDeductionResult = getShelterDeductionResult(client, timeframe);

    var totalIncome     = totalMonthlyEarnedGross + totalMonthlyUnearnedGross;
    var totalDeductions = earnedIncomeDeduction + standardDeduction + medicalDeduction
                        + hasHomelessDeduction + shelterDeductionResult
                        + dependentCareDeduction + childPaymentDeduction;

    return totalIncome - totalDeductions;
};

const maxTotalNetMonthlyIncome = function (client, timeframe) {
    var maxTotalNetIncome = null;
    //TODO: Logic different in website calculate; when (monthlyNetIncome < 0 ) = 0 while excel return a number
    if ( isNetIncomeTest(client, timeframe)===false ) {
      maxTotalNetIncome = "no limit";
      return maxTotalNetIncome;
    } else {
      return getYearlyLimitBySize(data.maxAllowableMonthlyNetIncome, client[timeframe + 'HouseholdSize']);
    }
};

// NET INCOME TEST RESULT
const getNetIncomeTestResult = function(client, timeframe ) {
  if ( maxTotalNetMonthlyIncome(client, timeframe) === "no limit" ) {
      return true;
    } else if ( monthlyNetIncome(client, timeframe) < maxTotalNetMonthlyIncome(client, timeframe) ) {
      return true;
    }
    else {
      return false;
    }
};

// FINAL DETERMINATION
const getThirtyPercentNetIncome = function(client, timeframe) {
  if ( monthlyNetIncome(client, timeframe) * data.percentOfIncome > 0 ) {
    return monthlyNetIncome(client, timeframe) * data.percentOfIncome;
  }
  return 0;
};

const getMaxSnapAllotment = function (client, timeframe) {
  return getYearlyLimitBySize(data.maxFoodStampAllotment, client[timeframe + 'HouseholdSize']);
};

// Bay State CAP not included as this prototype only deals with
// changes in earned income

export { getSNAPBenefits };
<|endoftext|>


<commit_before>/*
 * Run the code contained in the file manager, by compiling everything and
 * executing the main class, then display the result.
 */
/* global FileManager */

function runCodeCheerp() {
    // Get the main class's actual name..
    // drop the .java extension
    var mainName = FileManager.getMainFile().name.replace(/\.[^/.]+$/, "");
    // Get the file's package name
    var pkgReg = /package\s+([\w\.]+)\s*;/;
    var packageName = pkgReg.exec(FileManager.getMainFile().contents);
    // Add the package name to the class's name
    if (packageName === null || packageName.length < 2) {
        packageName = ["package", "default"];
    }

    // Prepare the request to run the code
    // Set up the request body for a compile-run request
    var body = {
        version: 1,
        compile: {
            version: 1,
            mainClass: packageName[1] + "." + mainName,
            mainFile: mainName,
            sourceFiles: []
        },
        data: {
            version: 1,
            dataFiles: []
        },
        "test-type": "run"
    };

    var root = FileManager.getRootFolder();
    // Add the files in the global file manager to the request body.
    addAllSourceFiles(root, body.compile.sourceFiles, true);

    // Add data files to the request
    addAllDataFiles(root, body.data.dataFiles);

    let newWindow = window.open('./cheerpjConsole.html');
    newWindow.compileData = body;
}<commit_msg>keep .java on mainFile<commit_after>/*
 * Run the code contained in the file manager, by compiling everything and
 * executing the main class, then display the result.
 */
/* global FileManager */

function runCodeCheerp() {
    // Get the main class's actual name..
    // drop the .java extension
    var mainFile = FileManager.getMainFile().name;
    var mainName = mainFile.replace(/\.[^/.]+$/, "");

    // Get the file's package name
    var pkgReg = /package\s+([\w\.]+)\s*;/;
    var packageName = pkgReg.exec(FileManager.getMainFile().contents);
    // Add the package name to the class's name
    if (packageName === null || packageName.length < 2) {
        packageName = ["package", "default"];
    }

    // Prepare the request to run the code
    // Set up the request body for a compile-run request
    var body = {
        version: 1,
        compile: {
            version: 1,
            mainClass: packageName[1] + "." + mainName,
            mainFile: mainFile,
            sourceFiles: []
        },
        data: {
            version: 1,
            dataFiles: []
        },
        "test-type": "run"
    };

    var root = FileManager.getRootFolder();
    // Add the files in the global file manager to the request body.
    addAllSourceFiles(root, body.compile.sourceFiles, true);

    // Add data files to the request
    addAllDataFiles(root, body.data.dataFiles);

    let newWindow = window.open('./cheerpjConsole.html');
    newWindow.compileData = body;
}<|endoftext|>


<commit_before>/**
 * @author Mugen87 / https://github.com/Mugen87
 */

import { WebGLLights } from './WebGLLights.js';

function WebGLRenderState() {

	var lights = new WebGLLights();

	var lightsArray = [];
	var shadowsArray = [];

	function init() {

		lightsArray.length = 0;
		shadowsArray.length = 0;

	}

	function pushLight( light ) {

		lightsArray.push( light );

	}

	function pushShadow( shadowLight ) {

		shadowsArray.push( shadowLight );

	}

	function setupLights( camera ) {

		lights.setup( lightsArray, shadowsArray, camera );

	}

	var state = {
		lightsArray: lightsArray,
		shadowsArray: shadowsArray,

		lights: lights
	};

	return {
		init: init,
		state: state,
		setupLights: setupLights,

		pushLight: pushLight,
		pushShadow: pushShadow
	};

}

function WebGLRenderStates() {

	var renderStates = new WeakMap();

	function onSceneDispose( event ) {

		var scene = event.target;

		scene.removeEventListener( 'dispose', onSceneDispose );

		renderStates.delete( scene );

	}

	function get( scene, camera ) {

		var renderState;

		if ( ! renderStates.has( scene ) ) {

			renderState = new WebGLRenderState();
			renderStates.set( scene, new WeakMap() );
			renderStates.get( scene ).set( camera, renderState );

			scene.addEventListener( 'dispose', onSceneDispose );

		} else {

			if ( ! renderStates.get( scene ).has( camera ) ) {

				renderState = new WebGLRenderState();
				renderStates.get( scene ).set( camera, renderState );

			} else {

				renderState = renderStates.get( scene ).get( camera );

			}

		}

		return renderState;

	}

	function dispose() {

		renderStates = new WeakMap();

	}

	return {
		get: get,
		dispose: dispose
	};

}

export { WebGLRenderStates };
<commit_msg>we don't like exclamation points here!<commit_after>/**
 * @author Mugen87 / https://github.com/Mugen87
 */

import { WebGLLights } from './WebGLLights.js';

function WebGLRenderState() {

	var lights = new WebGLLights();

	var lightsArray = [];
	var shadowsArray = [];

	function init() {

		lightsArray.length = 0;
		shadowsArray.length = 0;

	}

	function pushLight( light ) {

		lightsArray.push( light );

	}

	function pushShadow( shadowLight ) {

		shadowsArray.push( shadowLight );

	}

	function setupLights( camera ) {

		lights.setup( lightsArray, shadowsArray, camera );

	}

	var state = {
		lightsArray: lightsArray,
		shadowsArray: shadowsArray,

		lights: lights
	};

	return {
		init: init,
		state: state,
		setupLights: setupLights,

		pushLight: pushLight,
		pushShadow: pushShadow
	};

}

function WebGLRenderStates() {

	var renderStates = new WeakMap();

	function onSceneDispose( event ) {

		var scene = event.target;

		scene.removeEventListener( 'dispose', onSceneDispose );

		renderStates.delete( scene );

	}

	function get( scene, camera ) {

		var renderState;

		if ( renderStates.has( scene ) === false ) {

			renderState = new WebGLRenderState();
			renderStates.set( scene, new WeakMap() );
			renderStates.get( scene ).set( camera, renderState );

			scene.addEventListener( 'dispose', onSceneDispose );

		} else {

			if ( renderStates.get( scene ).has( camera ) === false ) {

				renderState = new WebGLRenderState();
				renderStates.get( scene ).set( camera, renderState );

			} else {

				renderState = renderStates.get( scene ).get( camera );

			}

		}

		return renderState;

	}

	function dispose() {

		renderStates = new WeakMap();

	}

	return {
		get: get,
		dispose: dispose
	};

}

export { WebGLRenderStates };
<|endoftext|>


<commit_before>var glimpse = require('glimpse'),
    _users = {},
    _userSelected = null;

function usersChanged() {
    glimpse.emit('shell.request.user.detail.changed', {
            allUsers: _users,
            selectedUserId: _userSelected
        });
}

(function() {
    function userClear() {
        _users[_userSelected].selected = false;
        _userSelected = null;

        usersChanged();
    }

    glimpse.on('shell.request.user.clear.selected', userClear);
})();

(function() {
    function userSwitch(payload) {
        var userId = payload.userId,
            oldUserId = _userSelected,
            user = _users[userId];

        if (oldUserId) {
            var oldUser = _users[oldUserId];
            if (oldUser) {
                oldUser.selected = false;
            }
        }
        if (user) {
            user.selected = true;
        }

        _userSelected = userId;

        usersChanged();
    }

    glimpse.on('shell.request.user.selected', userSwitch);
})();

(function() {
    // TODO: Need to update to make sure it can work with out of order/old
    //       requests coming in.
    // TODO: Timeouts should probably come from config
    // TODO: Should probably be abstracted out into its own module
    var manageRequest = (function() {
            function removeRequest(user, request) {
                var index = user.latestRequests.indexOf(request);
                if (index > -1) {
                    user.latestRequests.splice(index, 1);
                }

                usersChanged();
            }

            return function(user, rawRequest) {
                if (rawRequest) {
                    var request = {
                            id: rawRequest.id,
                            uri: rawRequest.uri
                        };

                    user.latestRequests.unshift(request);

                    setTimeout(function() { removeRequest(user, request); }, 5000);
                }
            };
        })(),
        manageOnline = (function() {
            function setOffline(user) {
                user.online = false;

                usersChanged();
            }

            return function(user, rawRequest) {
                user.lastActive = rawRequest.dateTime;
                user.online = true;

                if (user.onlineCallback) {
                    clearTimeout(user.onlineCallback);
                }

                user.onlineCallback = setTimeout(function() { setOffline(user); }, 12000);
            };
        })();

    function createUser(rawUser) {
        return {
                details: rawUser,
                latestRequests: [],
                lastActive: null,
                online: true,
                selected: false
            };
    }

    function dataFound(payload) {
        // TODO: This needs to be cleaned up bit messy atm but will do
        var rawRequests = payload.newRequests;
        for (var i = rawRequests.length - 1; i >= 0; i--) {
            var rawRequest = rawRequests[i],
                rawUser = rawRequest.user,
                user = _users[rawUser.id];

            if (user === undefined) {
                user = createUser(rawUser);
                _users[rawUser.id] = user;
            }

            manageRequest(user, rawRequest);
            manageOnline(user, rawRequest);
        }

        usersChanged();
    }

    // External data coming in
    glimpse.on('data.user.detail.found', dataFound);
})();
<commit_msg>Refactor user store to to bring it in line with new naming outlines<commit_after>var glimpse = require('glimpse'),
    _users = {},
    _userSelected = null;

function notifyUsersChanged() {
    glimpse.emit('shell.request.user.detail.changed', {
            allUsers: _users,
            selectedUserId: _userSelected
        });
}

(function() {
    function clearUser() {
        _users[_userSelected].selected = false;
        _userSelected = null;

        notifyUsersChanged();
    }

    glimpse.on('shell.request.user.clear.selected', clearUser);
})();

(function() {
    function selectUser(payload) {
        var userId = payload.userId,
            oldUserId = _userSelected,
            user = _users[userId];

        if (oldUserId) {
            var oldUser = _users[oldUserId];
            if (oldUser) {
                oldUser.selected = false;
            }
        }
        if (user) {
            user.selected = true;
        }

        _userSelected = userId;

        notifyUsersChanged();
    }

    glimpse.on('shell.request.user.selected', selectUser);
})();

(function() {
    // TODO: Need to update to make sure it can work with out of order/old
    //       requests coming in.
    // TODO: Timeouts should probably come from config
    // TODO: Should probably be abstracted out into its own module
    var manageRequest = (function() {
            function removeRequest(user, request) {
                var index = user.latestRequests.indexOf(request);
                if (index > -1) {
                    user.latestRequests.splice(index, 1);
                }

                notifyUsersChanged();
            }

            return function(user, rawRequest) {
                if (rawRequest) {
                    var request = {
                            id: rawRequest.id,
                            uri: rawRequest.uri
                        };

                    user.latestRequests.unshift(request);

                    setTimeout(function() { removeRequest(user, request); }, 5000);
                }
            };
        })(),
        manageOnline = (function() {
            function setOffline(user) {
                user.online = false;

                notifyUsersChanged();
            }

            return function(user, rawRequest) {
                user.lastActive = rawRequest.dateTime;
                user.online = true;

                if (user.onlineCallback) {
                    clearTimeout(user.onlineCallback);
                }

                user.onlineCallback = setTimeout(function() { setOffline(user); }, 12000);
            };
        })();

    function createUser(rawUser) {
        return {
                details: rawUser,
                latestRequests: [],
                lastActive: null,
                online: true,
                selected: false
            };
    }

    function foundUser(payload) {
        // TODO: This needs to be cleaned up bit messy atm but will do
        var rawRequests = payload.newRequests;
        for (var i = rawRequests.length - 1; i >= 0; i--) {
            var rawRequest = rawRequests[i],
                rawUser = rawRequest.user,
                user = _users[rawUser.id];

            if (user === undefined) {
                user = createUser(rawUser);
                _users[rawUser.id] = user;
            }

            manageRequest(user, rawRequest);
            manageOnline(user, rawRequest);
        }

        notifyUsersChanged();
    }

    // External data coming in
    glimpse.on('data.user.detail.found', foundUser);
})();
<|endoftext|>


<commit_before>const TITLE_CANDIDATES = ['title', 'name', 'label', 'heading', 'header', 'caption']
const DESCRIPTION_CANDIDATES = ['description', ...TITLE_CANDIDATES]
const MEDIA_CANDIDATES = ['image', 'video']

export default function guessPreviewFields(typeDef) {

  if (!typeDef.fields) {
    // Uh oh - this can not be a document, right?
    // todo throw?
    return null
  }

  // Check if we have fields with names that is listed in candidate fields
  let titleField = typeDef.fields.find(fieldDef => TITLE_CANDIDATES.includes(fieldDef.name))
  let descField = typeDef.fields.find(fieldDef => fieldDef !== titleField && DESCRIPTION_CANDIDATES.includes(fieldDef.name))

  if (!titleField) {
    // Pick first defined string field
    titleField = typeDef.fields.find(fieldDef => fieldDef.type === 'string')
    // Pick next as desc
    descField = typeDef.fields.find(fieldDef => fieldDef !== titleField && fieldDef.type === 'string')
  }

  const mediaField = typeDef.fields.find(fieldDef => MEDIA_CANDIDATES.includes(fieldDef.name))

  return {
    title: titleField && titleField.name,
    description: descField && descField.name,
    media: mediaField && mediaField.name
  }
}
<commit_msg>Don't guess media preview fields<commit_after>const TITLE_CANDIDATES = ['title', 'name', 'label', 'heading', 'header', 'caption']
const DESCRIPTION_CANDIDATES = ['description', ...TITLE_CANDIDATES]

export default function guessPreviewFields(typeDef) {

  if (!typeDef.fields) {
    // Uh oh - this can not be a document, right?
    // todo throw?
    return null
  }

  // Check if we have fields with names that is listed in candidate fields
  let titleField = typeDef.fields.find(fieldDef => TITLE_CANDIDATES.includes(fieldDef.name))
  let descField = typeDef.fields.find(fieldDef => fieldDef !== titleField && DESCRIPTION_CANDIDATES.includes(fieldDef.name))

  if (!titleField) {
    // Pick first defined string field
    titleField = typeDef.fields.find(fieldDef => fieldDef.type === 'string')
    // Pick next as desc
    descField = typeDef.fields.find(fieldDef => fieldDef !== titleField && fieldDef.type === 'string')
  }

  return {
    title: titleField && titleField.name,
    description: descField && descField.name
  }
}
<|endoftext|>


<commit_before>if (navigator.mozTCPSocket) {

function TCPSocket(host, port, options) {
    if (!options)
	options = {};
    options.binaryType = options.binaryType || 'arraybuffer';

    this.sock = navigator.mozTCPSocket.open(host, port, options);
    ['open', 'drain', 'close'].forEach(function(type) {
	this.sock['on' + type] = function(ev) {
	    var cb = this['on' + type];
	    if (cb)
		cb(ev);
	}.bind(this);
    }.bind(this));
    this.sock.ondata = function(ev) {
	console.log("ondata", ev.data);
	var cb = this.onmessage;
	if (cb)
	    cb(ev);
    }.bind(this);
}

['send', 'resume', 'suspend', 'close'].forEach(function(method) {
    TCPSocket.prototype[method] = function() {
	return this.sock[method].apply(this.sock, arguments);
    };
});

TCPSocket.prototype.__defineGetter__('readyState', function() {
  return this.sock.readyState;
});

TCPSocket.prototype.__defineGetter__('bufferedAmount', function() {
  return this.sock.bufferedAmount;
});

} else {

function TCPSocket(host, port, options) {
    chrome.socket.create('tcp', {}, function(createInfo) {
	if (createInfo.socketId)
	    chrome.socket.connect(createInfo.socketId, host, port, function(result) {
		if (result === 0) {
		    this.readyState = 'open';
		    this.socketId = createInfo.socketId;
		    this.bufferedAmount = 0;
		    this.emit('open', {});
		    if (!this.suspended)
			this.resume();
		} else
		    this.emit('error', new Error("Connect: " + result));
	    }.bind(this));
	else
	    this.emit('error', new Error("Cannot create TCP socket"));
    }.bind(this));
    this.readyState = 'connecting';
}

TCPSocket.prototype.emit =  function(type, event) {
    var cb = this['on' + type];
    if (cb) {
	try {
	    cb(event);
	} catch(e) {
	    console.error(e.stack || e.message || e);
	}
    }
};

TCPSocket.prototype.send = function(data) {
    if (this.readyState !== 'open')
	throw new Error("TCP socket not open");

    var len = data.byteLength;
    console.log("write", data);
    chrome.socket.write(this.socketId, data, function() {
	this.bufferedAmount -= len;
	if (this.bufferedAmount < 1)
	    this.emit('drain', {});
    });
    var buffered = this.bufferedAmount > 0;
    this.bufferedAmount += len;
    return !buffered;
};

TCPSocket.prototype.resume = function() {
    this.suspended = false;
    if (this.nextMessage) {
	this.emit('message', this.nextMessage);
	this.nextMessage = null;
    }

    if (this.reading)
	return;

    this.reading = true;
    chrome.socket.read(this.socketId, function(readInfo) {
	if (readInfo.resultCode < 0) {
	    this.emit('error', new Error("read: " + readInfo.resultCode));
	    this.close();
	} else {
	    this.reading = false;
	    this.nextMessage = {
		data: readInfo.data
	    };
	    if (!this.suspended)
		this.resume();
	}
    }.bind(this));
};

TCPSocket.prototype.suspend = function() {
    this.suspended = true;
};

TCPSocket.prototype.close = function() {
    if (this.readyState !== 'open')
	return;

    this.emit('close', {});
    chrome.socket.disconnect(this.socketId);
    chrome.socket.destroy(this.socketId);
    this.readyState = 'closed';
};

}

if (module && module.exports) {
    module.exports.TCPSocket = TCPSocket;
}
<commit_msg>pass TCPSocket instance to listeners<commit_after>if (navigator.mozTCPSocket) {

function TCPSocket(host, port, options) {
    if (!options)
	options = {};
    options.binaryType = options.binaryType || 'arraybuffer';

    this.sock = navigator.mozTCPSocket.open(host, port, options);
    ['open', 'drain', 'close'].forEach(function(type) {
	this.sock['on' + type] = function(ev) {
	    var cb = this['on' + type];
	    if (cb)
		cb.call(this, ev);
	}.bind(this);
    }.bind(this));
    this.sock.ondata = function(ev) {
	console.log("ondata", ev.data);
	var cb = this.onmessage;
	if (cb)
	    cb(ev);
    }.bind(this);
}

['send', 'resume', 'suspend', 'close'].forEach(function(method) {
    TCPSocket.prototype[method] = function() {
	return this.sock[method].apply(this.sock, arguments);
    };
});

TCPSocket.prototype.__defineGetter__('readyState', function() {
  return this.sock.readyState;
});

TCPSocket.prototype.__defineGetter__('bufferedAmount', function() {
  return this.sock.bufferedAmount;
});

} else {

function TCPSocket(host, port, options) {
    chrome.socket.create('tcp', {}, function(createInfo) {
	if (createInfo.socketId)
	    chrome.socket.connect(createInfo.socketId, host, port, function(result) {
		if (result === 0) {
		    this.readyState = 'open';
		    this.socketId = createInfo.socketId;
		    this.bufferedAmount = 0;
		    this.emit('open', {});
		    if (!this.suspended)
			this.resume();
		} else
		    this.emit('error', new Error("Connect: " + result));
	    }.bind(this));
	else
	    this.emit('error', new Error("Cannot create TCP socket"));
    }.bind(this));
    this.readyState = 'connecting';
}

TCPSocket.prototype.emit =  function(type, event) {
    var cb = this['on' + type];
    if (cb) {
	try {
	    cb.call(this, event);
	} catch(e) {
	    console.error(e.stack || e.message || e);
	}
    }
};

TCPSocket.prototype.send = function(data) {
    if (this.readyState !== 'open')
	throw new Error("TCP socket not open");

    var len = data.byteLength;
    console.log("write", data);
    chrome.socket.write(this.socketId, data, function() {
	this.bufferedAmount -= len;
	if (this.bufferedAmount < 1)
	    this.emit('drain', {});
    });
    var buffered = this.bufferedAmount > 0;
    this.bufferedAmount += len;
    return !buffered;
};

TCPSocket.prototype.resume = function() {
    this.suspended = false;
    if (this.nextMessage) {
	this.emit('message', this.nextMessage);
	this.nextMessage = null;
    }

    if (this.reading)
	return;

    this.reading = true;
    chrome.socket.read(this.socketId, function(readInfo) {
	if (readInfo.resultCode < 0) {
	    this.emit('error', new Error("read: " + readInfo.resultCode));
	    this.close();
	} else {
	    this.reading = false;
	    this.nextMessage = {
		data: readInfo.data
	    };
	    if (!this.suspended)
		this.resume();
	}
    }.bind(this));
};

TCPSocket.prototype.suspend = function() {
    this.suspended = true;
};

TCPSocket.prototype.close = function() {
    if (this.readyState !== 'open')
	return;

    this.emit('close', {});
    chrome.socket.disconnect(this.socketId);
    chrome.socket.destroy(this.socketId);
    this.readyState = 'closed';
};

}

if (module && module.exports) {
    module.exports.TCPSocket = TCPSocket;
}
<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */
/* eslint-disable class-methods-use-this */
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');

const { userModel } = require('../model');
const roleModel = require('../../role/model');

const getCurrentUserInfo = (id) => userModel.findById(id)
	.select('schoolId')
	.populate('roles')
	.lean()
	.exec();

const getRoles = () => roleModel.find()
	.select('name')
	.lean()
	.exec();

const getAllUsers = (ref, schoolId, role, clientQuery) => {
	const query = {
		schoolId,
		roles: role.toString(),
		$sort: clientQuery.$sort,
		$select: ['firstName', 'lastName', 'email', 'createdAt', 'importHash', 'birthday'],
	};
	if (clientQuery.createdAt) query.createdAt = clientQuery.createdAt;
	if (clientQuery.firstName) query.firstName = clientQuery.firstName;
	return ref.app.service('usersModel').find({ collation: { locale: 'de', caseLevel: true }, query });
};

const getClasses = (app, schoolId, schoolYearId) => app.service('classes')
	.find({
		query: {
			schoolId,
			$or: [
				{ year: schoolYearId },
				{ year: { $exists: false } },
			],
			$limit: 1000,
		},
	})
	.then((classes) => classes.data)
	.catch((err) => {
		logger.warning(`Can not execute app.service("classes").find for ${schoolId}`, err);
		return err;
	});

const findConsents = (ref, userIds, $limit) => ref.app.service('/consents')
	.find({
		query: {
			userId: { $in: userIds },
			$limit,
			$select: [
				'userId',
				'userConsent.form',
				'userConsent.privacyConsent',
				'userConsent.termsOfUseConsent',
				'parentConsents.parentId',
				'parentConsents.form',
				'parentConsents.privacyConsent',
				'parentConsents.termsOfUseConsent'],
		},
	})
	.then((consents) => consents.data);

const getCurrentYear = (ref, schoolId) => ref.app.service('schools')
	.get(schoolId, {
		query: { $select: ['currentYear'] },
	})
	.then(({ currentYear }) => currentYear.toString());

class AdminUsers {
	constructor(role) {
		this.role = role || {};
		this.docs = {};
		this.rolesThatCanAccess = ['teacher', 'administrator', 'superhero'];
	}

	async find(params) {
		try {
			const { query, account } = params;
			const currentUserId = account.userId.toString();
			const {
				$limit,
				$skip,
				$sort,
				consentStatus,
			} = query;

			// fetch base data
			const [currentUser, roles] = await Promise.all([getCurrentUserInfo(currentUserId), getRoles()]);

			// permission check
			if (!currentUser.roles.some((role) => this.rolesThatCanAccess.includes(role.name))) {
				throw new Forbidden();
			}
			const { schoolId } = currentUser;
			const currentYear = await getCurrentYear(this, schoolId);

			// fetch data that are scoped to schoolId
			const searchedRole = roles.find((role) => role.name === this.role);
			const [usersData, classes] = await Promise.all([
				getAllUsers(this, schoolId, searchedRole._id, query),
				getClasses(this.app, schoolId, currentYear),
			]);
			const { total } = usersData;
			const users = usersData.data;
			const userIds = users.map((user) => user._id.toString());
			const consents = await findConsents(this, userIds, $limit).then((data) => {
				// rebuild consent to object for faster sorting
				const out = {};
				data.forEach((e) => {
					out[e.userId.toString()] = e;
				});
				return out;
			});
			// bsonId to stringId that it can use .includes for is in test
			classes.forEach((c) => {
				if (Array.isArray(c.userIds)) {
					c.userIds = c.userIds.map((id) => id.toString());
				} else {
					c.userIds = [];
				}
				if (Array.isArray(c.teacherIds)) {
					c.teacherIds = c.teacherIds.map((id) => id.toString());
				} else {
					c.teacherIds = [];
				}
			});

			// patch classes and consent into user
			users.forEach((user) => {
				user.classes = [];
				const userId = user._id.toString();
				user.consent = consents[userId] || {};
				classes.forEach((c) => {
					if (c.userIds.includes(userId) || c.teacherIds.includes(userId)) {
						user.classes.push(c.displayName);
					}
				});
				return user;
			});

			// sorting by class and by consent is implemented manually,
			// as classes and consents are fetched from seperate db collection
			const classSortParam = (($sort || {}).class || {}).toString();
			if (classSortParam === '1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() > (b.classes[0] || '').toLowerCase());
			} else if (classSortParam === '-1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() < (b.classes[0] || '').toLowerCase());
			}

			const sortOrder = {
				ok: 1,
				parentsAgreed: 2,
				missing: 3,
			};
			const consentSortParam = (($sort || {}).consent || {}).toString();
			if (consentSortParam === '1') {
				users.sort((a, b) => (sortOrder[a.consent.consentStatus || 'missing']
					- sortOrder[b.consent.consentStatus || 'missing']));
			} else if (consentSortParam === '-1') {
				users.sort((a, b) => (sortOrder[b.consent.consentStatus || 'missing']
					- sortOrder[a.consent.consentStatus || 'missing']));
			}

			const filteredUsers = users.filter((user) => {
				if ((consentStatus || {}).$in) {
					const userStatus = user.consent.consentStatus || 'missing';
					return consentStatus.$in.includes(userStatus);
				}
				return true;
			});

			return {
				total,
				limit: $limit,
				skip: $skip,
				data: filteredUsers,
			};
		} catch (err) {
			if ((err || {}).code === 403) {
				throw new Forbidden('You have not the permission to execute this.', err);
			}
			throw new BadRequest('Can not fetch data.', err);
		}
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = AdminUsers;
<commit_msg>add clientQuery lastName<commit_after>/* eslint-disable no-param-reassign */
/* eslint-disable class-methods-use-this */
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');

const { userModel } = require('../model');
const roleModel = require('../../role/model');

const getCurrentUserInfo = (id) => userModel.findById(id)
	.select('schoolId')
	.populate('roles')
	.lean()
	.exec();

const getRoles = () => roleModel.find()
	.select('name')
	.lean()
	.exec();

const getAllUsers = (ref, schoolId, role, clientQuery) => {
	const query = {
		schoolId,
		roles: role.toString(),
		$sort: clientQuery.$sort,
		$select: ['firstName', 'lastName', 'email', 'createdAt', 'importHash', 'birthday'],
	};
	if (clientQuery.createdAt) query.createdAt = clientQuery.createdAt;
	if (clientQuery.firstName) query.firstName = clientQuery.firstName;
	if (clientQuery.lastName) query.lastName = clientQuery.lastName;
	return ref.app.service('usersModel').find({ collation: { locale: 'de', caseLevel: true }, query });
};

const getClasses = (app, schoolId, schoolYearId) => app.service('classes')
	.find({
		query: {
			schoolId,
			$or: [
				{ year: schoolYearId },
				{ year: { $exists: false } },
			],
			$limit: 1000,
		},
	})
	.then((classes) => classes.data)
	.catch((err) => {
		logger.warning(`Can not execute app.service("classes").find for ${schoolId}`, err);
		return err;
	});

const findConsents = (ref, userIds, $limit) => ref.app.service('/consents')
	.find({
		query: {
			userId: { $in: userIds },
			$limit,
			$select: [
				'userId',
				'userConsent.form',
				'userConsent.privacyConsent',
				'userConsent.termsOfUseConsent',
				'parentConsents.parentId',
				'parentConsents.form',
				'parentConsents.privacyConsent',
				'parentConsents.termsOfUseConsent'],
		},
	})
	.then((consents) => consents.data);

const getCurrentYear = (ref, schoolId) => ref.app.service('schools')
	.get(schoolId, {
		query: { $select: ['currentYear'] },
	})
	.then(({ currentYear }) => currentYear.toString());

class AdminUsers {
	constructor(role) {
		this.role = role || {};
		this.docs = {};
		this.rolesThatCanAccess = ['teacher', 'administrator', 'superhero'];
	}

	async find(params) {
		try {
			const { query, account } = params;
			const currentUserId = account.userId.toString();
			const {
				$limit,
				$skip,
				$sort,
				consentStatus,
			} = query;

			// fetch base data
			const [currentUser, roles] = await Promise.all([getCurrentUserInfo(currentUserId), getRoles()]);

			// permission check
			if (!currentUser.roles.some((role) => this.rolesThatCanAccess.includes(role.name))) {
				throw new Forbidden();
			}
			const { schoolId } = currentUser;
			const currentYear = await getCurrentYear(this, schoolId);

			// fetch data that are scoped to schoolId
			const searchedRole = roles.find((role) => role.name === this.role);
			const [usersData, classes] = await Promise.all([
				getAllUsers(this, schoolId, searchedRole._id, query),
				getClasses(this.app, schoolId, currentYear),
			]);
			const { total } = usersData;
			const users = usersData.data;
			const userIds = users.map((user) => user._id.toString());
			const consents = await findConsents(this, userIds, $limit).then((data) => {
				// rebuild consent to object for faster sorting
				const out = {};
				data.forEach((e) => {
					out[e.userId.toString()] = e;
				});
				return out;
			});
			// bsonId to stringId that it can use .includes for is in test
			classes.forEach((c) => {
				if (Array.isArray(c.userIds)) {
					c.userIds = c.userIds.map((id) => id.toString());
				} else {
					c.userIds = [];
				}
				if (Array.isArray(c.teacherIds)) {
					c.teacherIds = c.teacherIds.map((id) => id.toString());
				} else {
					c.teacherIds = [];
				}
			});

			// patch classes and consent into user
			users.forEach((user) => {
				user.classes = [];
				const userId = user._id.toString();
				user.consent = consents[userId] || {};
				classes.forEach((c) => {
					if (c.userIds.includes(userId) || c.teacherIds.includes(userId)) {
						user.classes.push(c.displayName);
					}
				});
				return user;
			});

			// sorting by class and by consent is implemented manually,
			// as classes and consents are fetched from seperate db collection
			const classSortParam = (($sort || {}).class || {}).toString();
			if (classSortParam === '1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() > (b.classes[0] || '').toLowerCase());
			} else if (classSortParam === '-1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() < (b.classes[0] || '').toLowerCase());
			}

			const sortOrder = {
				ok: 1,
				parentsAgreed: 2,
				missing: 3,
			};
			const consentSortParam = (($sort || {}).consent || {}).toString();
			if (consentSortParam === '1') {
				users.sort((a, b) => (sortOrder[a.consent.consentStatus || 'missing']
					- sortOrder[b.consent.consentStatus || 'missing']));
			} else if (consentSortParam === '-1') {
				users.sort((a, b) => (sortOrder[b.consent.consentStatus || 'missing']
					- sortOrder[a.consent.consentStatus || 'missing']));
			}

			const filteredUsers = users.filter((user) => {
				if ((consentStatus || {}).$in) {
					const userStatus = user.consent.consentStatus || 'missing';
					return consentStatus.$in.includes(userStatus);
				}
				return true;
			});

			return {
				total,
				limit: $limit,
				skip: $skip,
				data: filteredUsers,
			};
		} catch (err) {
			if ((err || {}).code === 403) {
				throw new Forbidden('You have not the permission to execute this.', err);
			}
			throw new BadRequest('Can not fetch data.', err);
		}
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = AdminUsers;
<|endoftext|>


<commit_before>'use strict'

var createShader = require('gl-shader')
var createBuffer = require('gl-buffer')
var search = require('binary-search-bounds')
var snapPoints = require('snap-points-2d')
var pool = require('typedarray-pool')
var SHADERS = require('./lib/shader')
var normalize = require('array-normalize')
var getBounds = require('array-bounds')

module.exports = createScatter2D

function Scatter2D(plot, positionBufferHi, positionBufferLo, pickBuffer, weightBuffer, shader, pickShader) {
  this.plot             = plot
  this.positionBufferHi = positionBufferHi
  this.positionBufferLo = positionBufferLo
  this.pickBuffer       = pickBuffer
  this.weightBuffer     = weightBuffer
  this.shader           = shader
  this.pickShader       = pickShader
  this.scales           = []
  this.size             = 12.0
  this.borderSize       = 1.0
  this.pointCount       = 0
  this.color            = [1, 0, 0, 1]
  this.borderColor      = [0, 0, 0, 1]
  this.bounds           = [Infinity, Infinity, -Infinity, -Infinity]
  this.pickOffset       = 0
  this.points           = null
  this.xCoords          = null
  this.snapPoints       = true
}

var proto = Scatter2D.prototype
var scaleHi = new Float32Array(2)
var scaleLo = new Float32Array(2)
var translateHi = new Float32Array(2)
var translateLo = new Float32Array(2)
var PICK_VEC4 = [0, 0, 0, 0]

proto.dispose = function() {
  this.shader.dispose()
  this.pickShader.dispose()
  this.positionBufferHi.dispose()
  this.positionBufferLo.dispose()
  this.pickBuffer.dispose()
  if(this.xCoords) pool.free(this.xCoords)
  this.plot.removeObject(this)
}

proto.update = function(options) {
  options = options || {}

  function dflt(opt, value) {
    return opt in options ? options[opt] : value
  }

  this.size         = dflt('size', 12)
  this.color        = dflt('color', [1, 0, 0, 1]).slice()
  this.borderSize   = dflt('borderSize', 1)
  this.borderColor  = dflt('borderColor', [0, 0, 0, 1]).slice()
  this.snapPoints   = dflt('snapPoints', true)

  if(this.xCoords) pool.free(this.xCoords)

  //do not recalc points if there is no positions
  if (options.positions != null) {
    this.points             = options.positions
    var pointCount          = this.points.length >>> 1
    var packedId            = pool.mallocInt32(pointCount)
    var packedW             = pool.mallocFloat32(pointCount)
    var packed              = pool.mallocFloat64(2 * pointCount)
    packed.set(this.points)

      console.time('1')
    if (this.snapPoints) {
      this.scales = snapPoints(packed, packedId, packedW, this.bounds)
    }
    else {
      //get bounds
      this.bounds = getBounds(packed, 2)

      // rescale packed to unit box
      normalize(packed, 2, this.bounds)

      //generate fake ids
      for (var i = 0; i < pointCount; i++) {
        packedId[i] = i
        packedW[i] = 1
      }
    }
      console.timeEnd('1')

    var xCoords             = pool.mallocFloat64(pointCount)
    var packedHi            = pool.mallocFloat32(2 * pointCount)
    var packedLo            = pool.mallocFloat32(2 * pointCount)
    packedHi.set(packed)
    for(var i = 0, j = 0; i < pointCount; i++, j += 2) {
      packedLo[j] = packed[j] - packedHi[j]
      packedLo[j + 1] = packed[j + 1] - packedHi[j + 1]
      xCoords[i] = packed[j]
    }
    this.positionBufferHi.update(packedHi)
    this.positionBufferLo.update(packedLo)
    this.pickBuffer.update(packedId)
    this.weightBuffer.update(packedW)

    pool.free(packedId)
    pool.free(packed)
    pool.free(packedHi)
    pool.free(packedLo)
    pool.free(packedW)

    this.xCoords = xCoords
    this.pointCount = pointCount
    this.pickOffset = 0
  }
}

proto.draw = function(pickOffset) {

  var pick = pickOffset !== void(0)

  var plot             = this.plot
  var shader           = pick ? this.pickShader : this.shader
  var scales           = this.scales
  var positionBufferHi = this.positionBufferHi
  var positionBufferLo = this.positionBufferLo
  var pickBuffer       = this.pickBuffer
  var bounds           = this.bounds
  var size             = this.size
  var borderSize       = this.borderSize
  var gl               = plot.gl
  var pixelRatio       = pick ? plot.pickPixelRatio : plot.pixelRatio
  var viewBox          = plot.viewBox
  var dataBox          = plot.dataBox

  if(this.pointCount === 0)
    return pickOffset

  var boundX  = bounds[2] - bounds[0]
  var boundY  = bounds[3] - bounds[1]
  var dataX   = dataBox[2] - dataBox[0]
  var dataY   = dataBox[3] - dataBox[1]
  var screenX = (viewBox[2] - viewBox[0]) * pixelRatio / plot.pixelRatio
  var screenY = (viewBox[3] - viewBox[1]) * pixelRatio / plot.pixelRatio

  var pixelSize   = Math.min(dataX / screenX, dataY / screenY)

  var scaleX = 2 * boundX / dataX
  var scaleY = 2 * boundY / dataY

  scaleHi[0] = scaleX
  scaleHi[1] = scaleY

  scaleLo[0] = scaleX - scaleHi[0]
  scaleLo[1] = scaleY - scaleHi[1]

  var translateX = (bounds[0] - dataBox[0] - 0.5 * dataX) / boundX
  var translateY = (bounds[1] - dataBox[1] - 0.5 * dataY) / boundY

  translateHi[0] = translateX
  translateHi[1] = translateY

  translateLo[0] = translateX - translateHi[0]
  translateLo[1] = translateY - translateHi[1]

  shader.bind()
  shader.uniforms.scaleHi     = scaleHi
  shader.uniforms.scaleLo     = scaleLo
  shader.uniforms.translateHi = translateHi
  shader.uniforms.translateLo = translateLo
  shader.uniforms.color       = this.color
  shader.uniforms.borderColor = this.borderColor
  shader.uniforms.pointSize   = pixelRatio * (size + borderSize)
  shader.uniforms.centerFraction = this.borderSize === 0 ? 2 : size / (size + borderSize + 1.25)

  positionBufferHi.bind()
  shader.attributes.positionHi.pointer()

  positionBufferLo.bind()
  shader.attributes.positionLo.pointer()

  if(pick) {
    this.pickOffset = pickOffset
    PICK_VEC4[0] = ( pickOffset        & 0xff)
    PICK_VEC4[1] = ((pickOffset >> 8)  & 0xff)
    PICK_VEC4[2] = ((pickOffset >> 16) & 0xff)
    PICK_VEC4[3] = ((pickOffset >> 24) & 0xff)
    shader.uniforms.pickOffset = PICK_VEC4

    pickBuffer.bind()
    shader.attributes.pickId.pointer(gl.UNSIGNED_BYTE)

  } else {

    shader.uniforms.useWeight = 1
    this.weightBuffer.bind()
    shader.attributes.weight.pointer()

  }

  var xCoords = this.xCoords
  var xStart = (dataBox[0] - bounds[0] - pixelSize * size * pixelRatio) / boundX
  var xEnd   = (dataBox[2] - bounds[0] + pixelSize * size * pixelRatio) / boundX

  var firstLevel = true

  if (this.snapPoints) {
    for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) {
      var lod = scales[scaleNum]
      if(lod.pixelSize < pixelSize && scaleNum > 1)
        continue

      var intervalStart = lod.offset
      var intervalEnd   = lod.count + intervalStart

      var startOffset = search.ge(xCoords, xStart, intervalStart, intervalEnd - 1)
      var endOffset   = search.lt(xCoords, xEnd, startOffset, intervalEnd - 1) + 1

      if(endOffset > startOffset)
        gl.drawArrays(gl.POINTS, startOffset, endOffset - startOffset)

      if(!pick && firstLevel) {
        firstLevel = false
        shader.uniforms.useWeight = 0
      }
    }
  }
  else {
    gl.drawArrays(gl.POINTS, 0, this.pointCount)
  }

  return pickOffset + this.pointCount
}

proto.drawPick = proto.draw

proto.pick = function(x, y, value) {
  var pointId = value - this.pickOffset
  return pointId < 0 || pointId >= this.pointCount
    ? null : {
    object:  this,
    pointId: pointId,
    dataCoord: [ this.points[2 * pointId], this.points[2 * pointId + 1] ]
  }
}

function createScatter2D(plot, options) {
  var gl = plot.gl
  var positionBufferHi = createBuffer(gl)
  var positionBufferLo = createBuffer(gl)
  var pickBuffer = createBuffer(gl)
  var weightBuffer = createBuffer(gl)
  var shader = createShader(gl, SHADERS.pointVertex, SHADERS.pointFragment)
  var pickShader = createShader(gl, SHADERS.pickVertex, SHADERS.pickFragment)

  var result = new Scatter2D(plot, positionBufferHi, positionBufferLo, pickBuffer, weightBuffer, shader, pickShader)
  result.update(options)

  plot.addObject(result) // register with plot

  return result
}
<commit_msg>Externalize visible range function<commit_after>'use strict'

var createShader = require('gl-shader')
var createBuffer = require('gl-buffer')
var search = require('binary-search-bounds')
var snapPoints = require('snap-points-2d')
var pool = require('typedarray-pool')
var SHADERS = require('./lib/shader')
var normalize = require('array-normalize')
var getBounds = require('array-bounds')

module.exports = createScatter2D

function Scatter2D(plot, positionBufferHi, positionBufferLo, pickBuffer, weightBuffer, shader, pickShader) {
  this.plot             = plot
  this.positionBufferHi = positionBufferHi
  this.positionBufferLo = positionBufferLo
  this.pickBuffer       = pickBuffer
  this.weightBuffer     = weightBuffer
  this.shader           = shader
  this.pickShader       = pickShader
  this.scales           = []
  this.size             = 12.0
  this.borderSize       = 1.0
  this.pointCount       = 0
  this.color            = [1, 0, 0, 1]
  this.borderColor      = [0, 0, 0, 1]
  this.bounds           = [Infinity, Infinity, -Infinity, -Infinity]
  this.pickOffset       = 0
  this.points           = null
  this.xCoords          = null
  this.snapPoints       = true
}

var proto = Scatter2D.prototype
var scaleHi = new Float32Array(2)
var scaleLo = new Float32Array(2)
var translateHi = new Float32Array(2)
var translateLo = new Float32Array(2)
var PICK_VEC4 = [0, 0, 0, 0]

proto.dispose = function() {
  this.shader.dispose()
  this.pickShader.dispose()
  this.positionBufferHi.dispose()
  this.positionBufferLo.dispose()
  this.pickBuffer.dispose()
  if(this.xCoords) pool.free(this.xCoords)
  this.plot.removeObject(this)
}

proto.update = function(options) {
  options = options || {}

  function dflt(opt, value) {
    return opt in options ? options[opt] : value
  }

  this.size         = dflt('size', 12)
  this.color        = dflt('color', [1, 0, 0, 1]).slice()
  this.borderSize   = dflt('borderSize', 1)
  this.borderColor  = dflt('borderColor', [0, 0, 0, 1]).slice()
  this.snapPoints   = dflt('snapPoints', true)

  if(this.xCoords) pool.free(this.xCoords)

  //do not recalc points if there is no positions
  if (options.positions != null) {
    this.points             = options.positions
    var pointCount          = this.points.length >>> 1

    var packedId = pool.mallocInt32(pointCount)
    var packedW = pool.mallocFloat32(pointCount)
    var packed = pool.mallocFloat64(2 * pointCount)
    packed.set(this.points)

    if (this.snapPoints) {
      this.scales = snapPoints(packed, packedId, packedW, this.bounds)
    }
    else {
      //get bounds
      this.bounds = getBounds(packed, 2)

      // rescale packed to unit box
      normalize(packed, 2, this.bounds)

      // generate fake ids
      for (var i = 0; i < pointCount; i++) {
        packedId[i] = i
        packedW[i] = 1
      }
    }

    var xCoords             = pool.mallocFloat64(pointCount)
    var packedHi            = pool.mallocFloat32(2 * pointCount)
    var packedLo            = pool.mallocFloat32(2 * pointCount)
    packedHi.set(packed)
    for(var i = 0, j = 0; i < pointCount; i++, j += 2) {
      packedLo[j] = packed[j] - packedHi[j]
      packedLo[j + 1] = packed[j + 1] - packedHi[j + 1]
      xCoords[i] = packed[j]
    }
    this.positionBufferHi.update(packedHi)
    this.positionBufferLo.update(packedLo)
    this.pickBuffer.update(packedId)
    this.weightBuffer.update(packedW)

    pool.free(packedHi)
    pool.free(packedLo)
    pool.free(packedW)
    pool.free(packed)
    pool.free(packedId)

    this.xCoords = xCoords
    this.pointCount = pointCount
    this.pickOffset = 0
  }
}

proto.draw = function(pickOffset) {

  var pick = pickOffset !== void(0)

  var plot             = this.plot
  var shader           = pick ? this.pickShader : this.shader
  var scales           = this.scales
  var positionBufferHi = this.positionBufferHi
  var positionBufferLo = this.positionBufferLo
  var pickBuffer       = this.pickBuffer
  var bounds           = this.bounds
  var size             = this.size
  var borderSize       = this.borderSize
  var gl               = plot.gl
  var pixelRatio       = pick ? plot.pickPixelRatio : plot.pixelRatio
  var viewBox          = plot.viewBox
  var dataBox          = plot.dataBox

  if(this.pointCount === 0)
    return pickOffset

  var boundX  = bounds[2] - bounds[0]
  var boundY  = bounds[3] - bounds[1]
  var dataX   = dataBox[2] - dataBox[0]
  var dataY   = dataBox[3] - dataBox[1]
  var screenX = (viewBox[2] - viewBox[0]) * pixelRatio / plot.pixelRatio
  var screenY = (viewBox[3] - viewBox[1]) * pixelRatio / plot.pixelRatio

  var pixelSize = this.pixelSize = Math.min(dataX / screenX, dataY / screenY)

  var scaleX = 2 * boundX / dataX
  var scaleY = 2 * boundY / dataY

  scaleHi[0] = scaleX
  scaleHi[1] = scaleY

  scaleLo[0] = scaleX - scaleHi[0]
  scaleLo[1] = scaleY - scaleHi[1]

  var translateX = (bounds[0] - dataBox[0] - 0.5 * dataX) / boundX
  var translateY = (bounds[1] - dataBox[1] - 0.5 * dataY) / boundY

  translateHi[0] = translateX
  translateHi[1] = translateY

  translateLo[0] = translateX - translateHi[0]
  translateLo[1] = translateY - translateHi[1]

  shader.bind()
  shader.uniforms.scaleHi     = scaleHi
  shader.uniforms.scaleLo     = scaleLo
  shader.uniforms.translateHi = translateHi
  shader.uniforms.translateLo = translateLo
  shader.uniforms.color       = this.color
  shader.uniforms.borderColor = this.borderColor
  shader.uniforms.pointSize   = pixelRatio * (size + borderSize)
  shader.uniforms.centerFraction = this.borderSize === 0 ? 2 : size / (size + borderSize + 1.25)

  positionBufferHi.bind()
  shader.attributes.positionHi.pointer()

  positionBufferLo.bind()
  shader.attributes.positionLo.pointer()

  if(pick) {
    this.pickOffset = pickOffset
    PICK_VEC4[0] = ( pickOffset        & 0xff)
    PICK_VEC4[1] = ((pickOffset >> 8)  & 0xff)
    PICK_VEC4[2] = ((pickOffset >> 16) & 0xff)
    PICK_VEC4[3] = ((pickOffset >> 24) & 0xff)
    shader.uniforms.pickOffset = PICK_VEC4

    pickBuffer.bind()
    shader.attributes.pickId.pointer(gl.UNSIGNED_BYTE)

  } else {

    shader.uniforms.useWeight = 1
    this.weightBuffer.bind()
    shader.attributes.weight.pointer()

  }

  var firstLevel = true

  if (this.snapPoints) {
    for(var scaleNum = scales.length - 1; scaleNum >= 0; scaleNum--) {
      var lod = scales[scaleNum]
      if(lod.pixelSize < pixelSize && scaleNum > 1)
        continue

      var range = this.getVisibleRange(lod)
      var startOffset = range[0], endOffset = range[1]

      if(endOffset > startOffset)
        gl.drawArrays(gl.POINTS, startOffset, endOffset - startOffset)

      if(!pick && firstLevel) {
        firstLevel = false
        shader.uniforms.useWeight = 0
      }
    }
  }
  else {
    gl.drawArrays(gl.POINTS, 0, this.pointCount)
  }

  return pickOffset + this.pointCount
}

proto.getVisibleRange = function (lod) {
  var dataBox = this.plot.dataBox,
      bounds = this.bounds,
      pixelSize = this.pixelSize,
      size = this.size,
      pixelRatio = this.plot.pixelRatio,
      boundX  = bounds[2] - bounds[0],
      boundY  = bounds[3] - bounds[1]

  if (!lod) {
    for(var scaleNum = this.scales.length - 1, lod; scaleNum >= 0; scaleNum--) {
      lod = this.scales[scaleNum];
      if(!(lod.pixelSize < pixelSize && scaleNum > 1)) break;
    }
  }

  var xCoords = this.xCoords
  var xStart = (dataBox[0] - bounds[0] - pixelSize * size * pixelRatio) / boundX
  var xEnd   = (dataBox[2] - bounds[0] + pixelSize * size * pixelRatio) / boundX

  var intervalStart = lod.offset
  var intervalEnd   = lod.count + intervalStart

  var startOffset = search.ge(xCoords, xStart, intervalStart, intervalEnd - 1)
  var endOffset   = search.lt(xCoords, xEnd, startOffset, intervalEnd - 1) + 1

  return [startOffset, endOffset]
}

proto.drawPick = proto.draw

proto.pick = function(x, y, value) {
  var pointId = value - this.pickOffset
  return pointId < 0 || pointId >= this.pointCount
    ? null : {
    object:  this,
    pointId: pointId,
    dataCoord: [ this.points[2 * pointId], this.points[2 * pointId + 1] ]
  }
}

function createScatter2D(plot, options) {
  var gl = plot.gl
  var positionBufferHi = createBuffer(gl)
  var positionBufferLo = createBuffer(gl)
  var pickBuffer = createBuffer(gl)
  var weightBuffer = createBuffer(gl)
  var shader = createShader(gl, SHADERS.pointVertex, SHADERS.pointFragment)
  var pickShader = createShader(gl, SHADERS.pickVertex, SHADERS.pickFragment)

  var result = new Scatter2D(plot, positionBufferHi, positionBufferLo, pickBuffer, weightBuffer, shader, pickShader)
  result.update(options)

  plot.addObject(result) // register with plot

  return result
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2013-2017 by Tawi Jordan - ๖ۣۜĐJ - ɴᴇᴏɴ - TFL

 Permission to use this software for any purpose without fee is hereby granted, provided
 that the above copyright notice and this permission notice appear in all copies.

 Permission to copy and/or edit this software or parts of it for any purpose is permitted,
 provided that the following points are followed.
 - The above copyright notice and this permission notice appear in all copies
 - Within two (2) days after modification is proven working, any modifications are send back
   to the original authors to be inspected with the goal of inclusion in the official software
 - Any edited version are only test versions and not permitted to be run as a final product
 - Any edited version aren't to be distributed
 - Any edited version have the prerelease version set to something that can be distinguished
   from a version used in the original software

 TERMS OF REPRODUCTION USE

 Failure to follow these terms will result in me getting very angry at you
 and having your software tweaked or removed if possible. Either way, you're
 still an idiot for not following such a basic rule.
 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS
 BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

 * NOTE:  PLEASE CONTACT DJ-NEON FOR THIS SCRIPT (DO NOT CHANGE ANYTHING ON THIS SCRIPT OR USE AND EDIT THIS SCRIPT WHICH
 * WAS WRITTEN BY IT'S RIGHTFUL OWNER: DJ NOEN)
 *
 * @Author:    Tawi Jordan - ๖ۣۜĐJ - ɴᴇᴏɴ - TFL (Member. on Plug.dj)
 *
 */

var path = 'http://pastebin.com/raw.php?i=';

function message(contents) {
        var msg = '
PlugWoot  '+ contents +'
';
        $('#chat-messages').append(msg);
}

var scriptFail = window.setTimeout(function() {
    admin('Oops! An Error Occurred');
  }, 2000);

$.getScript(path + 'rM15aY1b', function() {
 message("version "+ Plugwoot.misc.version +" is now available!");
 API.chatLog("Type /commands to see the commands!", alert);
 window.clearTimeout(scriptFail);
});
<commit_msg>Update Plugwoot.js<commit_after>/*
 Copyright (c) 2013-2017 by Tawi Jordan - ๖ۣۜĐJ - ɴᴇᴏɴ - TFL

 Permission to use this software for any purpose without fee is hereby granted, provided
 that the above copyright notice and this permission notice appear in all copies.

 Permission to copy and/or edit this software or parts of it for any purpose is permitted,
 provided that the following points are followed.
 - The above copyright notice and this permission notice appear in all copies
 - Within two (2) days after modification is proven working, any modifications are send back
   to the original authors to be inspected with the goal of inclusion in the official software
 - Any edited version are only test versions and not permitted to be run as a final product
 - Any edited version aren't to be distributed
 - Any edited version have the prerelease version set to something that can be distinguished
   from a version used in the original software

 TERMS OF REPRODUCTION USE

 Failure to follow these terms will result in me getting very angry at you
 and having your software tweaked or removed if possible. Either way, you're
 still an idiot for not following such a basic rule.
 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS
 BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

 * NOTE:  PLEASE CONTACT DJ-NEON FOR THIS SCRIPT (DO NOT CHANGE ANYTHING ON THIS SCRIPT OR USE AND EDIT THIS SCRIPT WHICH
 * WAS WRITTEN BY IT'S RIGHTFUL OWNER: DJ NOEN)
 *
 * @Author:    Tawi Jordan - ๖ۣۜĐJ - ɴᴇᴏɴ - TFL (Member. on Plug.dj)
 *
 */

var path = 'http://pastebin.com/raw.php?i=';

function message(contents) {
        var msg = '
PlugWoot  '+ contents +'
';
        $('#chat-messages').append(msg);
}

var scriptFail = window.setTimeout(function() {
    admin('Oops! An Error Occurred');
  }, 2000);

$.getScript(path + 'QvSsBarg', function() {
 message("version "+ Plugwoot.misc.version +" is now available!");
 API.chatLog("Type /commands to see the commands!", alert);
 window.clearTimeout(scriptFail);
});
<|endoftext|>


<commit_before>var noEndTag = ['br', 'hr', 'img', 'input', 'link', 'meta'];
// var blocks = ['div', 'h1', 'h2', 'h3', 'p', 'ol', 'ul'];

function serialize(node, outer) {
  if (node.nodeType != 1)
    return node.nodeValue;

  str = '';
  if (outer) {
    str += '<' + node.localName;
    for (var i = 0; i < node.attributes.length; i++) {
      if (node.attributes[i].name[0] == '_')
        continue;
      str += ' ' + node.attributes[i].name + '=';
      str += '"' + node.attributes[i].value + '"';
    }
    if (noEndTag.indexOf(node.localName) >= 0 && node.childNodes.length == 0)
      return str + '/>';
    str += '>';
  }

  if (!node._placeholder) {
    for (var i = 0; i < node.childNodes.length; i++) {
      str += serialize(node.childNodes[i], true);
    }
  }

  if (outer) {
    str += '</' + node.localName + '>';
    // if (blocks.indexOf(node.localName) >= 0)
    //   str += '\n';
  }

  return str;
}

function escapeHTML(str) {
  return str.replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"');
}
<commit_msg>Escape special characters when serializing text<commit_after>var noEndTag = ['br', 'hr', 'img', 'input', 'link', 'meta'];
// var blocks = ['div', 'h1', 'h2', 'h3', 'p', 'ol', 'ul'];

function serialize(node, outer) {
  if (node.nodeType != 1)
    return escapeHTML(node.nodeValue);

  str = '';
  if (outer) {
    str += '<' + node.localName;
    for (var i = 0; i < node.attributes.length; i++) {
      if (node.attributes[i].name[0] == '_')
        continue;
      str += ' ' + node.attributes[i].name + '=';
      str += '"' + node.attributes[i].value + '"';
    }
    if (noEndTag.indexOf(node.localName) >= 0 && node.childNodes.length == 0)
      return str + '/>';
    str += '>';
  }

  if (!node._placeholder) {
    for (var i = 0; i < node.childNodes.length; i++) {
      str += serialize(node.childNodes[i], true);
    }
  }

  if (outer) {
    str += '</' + node.localName + '>';
    // if (blocks.indexOf(node.localName) >= 0)
    //   str += '\n';
  }

  return str;
}

function escapeHTML(str) {
  return str.replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"');
}
<|endoftext|>