<commit_before>/**
 * Created by Nicolas Zozol on 15/12/2016.
 */

import Parser from '../../lib/standard/document-parser';

let value = undefined;
let accepted = undefined;
let parser = null

// used to debug value easily. Avoid [Object object] notation.
function display(val, prefix = false) {
    if (val === undefined) {
        val = value;
    }
    if (prefix) {
        console.info(`${prefix} : ${JSON.stringify(val)}`);
    } else {
        console.info(JSON.stringify(val));
    }
}

function testBlock(block) {
    const parsing = Parser.parse(block);
    value = parsing.value;
    accepted = parsing.isAccepted();
    console.info('parsing', parsing, '\n\n');
}

const standardParagraph= `Their job is to destroy
Death Stars.`;
const expectedStandardParagraph = {
    paragraph: [
        {text: 'Their job is to destroy Death Stars.'}
    ]
};

const lev1Title='#Star Wars Rocks';
const otherLev1Title='#Star Trek also Rocks';
const lev1AltTitle='Star Wars Rocks\n======';
const lev2Title='##C3PO and R2D2';
const lev2AltTitle='C3PO and R2D2\n----';
const expectedLev1Title= { title: { level: 1, text: 'Star Wars Rocks' } };
const expectedOtherLev1Title= { title: { level: 1, text: 'Star Trek also Rocks' } };
const expectedLev2Title= { title: { level: 2, text: 'C3PO and R2D2' } };

const complexParagraph = `They know how to  *use the force*
  , speed up,  and destroy **Death Stars**.`;

const expectedComplexParagraph = {
    paragraph: [
        {text: 'They know how to  '},
        {italic: 'use the force'},
        {text: '   , speed up,  and destroy '},
        {bold: 'Death Stars'},
        {text:'.'}
    ]
};

const bullet1 = '* Look for plans';
const bullet2 = '* Find an **unexpensive** youngster';
const bullet3 = '- Kill an *old man*';
const bullet4level2 = '    - Use the `force`';
const bullet5level2 = '\t* Destroy Death Star   ';
const bullets= `${bullet1}\n${bullet2}\n${bullet3}\n${bullet4level2}\n${bullet5level2}`;

const expectedBullet1={bullet:{level:1, content:[{text:'Look for plans'}]}};
const expectedBullet2={bullet:{level:1, content:[
    {text:'Find an '},
    {bold:'unexpensive'},
    {text:' youngster'},
]}};
const expectedBullet3={bullet:{level:1, content:[
    {text:'Kill an '},
    {italic:'old man'}
]}};
const expectedBullet4level2={bullet:{level:2, content:[
    {text:'Use the '},
    {code:'force'}
]}};
const expectedBullet5level2={bullet:{level:2, content:[
    {text:'Destroy Death Star   '}
]}};

const expectedBullets=[
    expectedBullet1,
    expectedBullet2,
    expectedBullet3,
    expectedBullet4level2,
    expectedBullet5level2
];

const spaceCodeLine = '        Star Wars is an Atari game\n';
const tabCodeLine = '\t\tStar Wars is an Amstrad game\n';
const tabCodeBloc = `
\t\tStar Wars is an Amstrad game
\t\tStar Wars is an Atari game
`;

const expectedCodeLine=`
        Star Wars is an Amstrad game
        Star Wars is an Atari game
`;

export default {
    setUp: function (done) {
        done();
    },

    'Multilines lines paragraph should be accepted': function (test) {
        const block = `${standardParagraph}`;
        testBlock(block);
        const expected = [expectedStandardParagraph];
        test.deepEqual(expected, value, 'bad value for multiline');
        test.done();
    },

    'Complex Multilines lines should be accepted': function (test) {
        const block = `${complexParagraph}`;
        testBlock(block);
        const expected = [expectedComplexParagraph];
        test.deepEqual(expected, value, 'bad value for complex paragraph');
        test.done();
    },

    'Read title and its paragraphs': function (test) {
        const block = `${lev1Title}\n${standardParagraph}\n\n${complexParagraph}`;
        testBlock(block);
        const expected= [
            expectedLev1Title,
            expectedStandardParagraph,
            expectedComplexParagraph
        ];

        test.deepEqual(expected, value, 'bad value for title & paragraphs');
        test.done();
    },

    'Read bullets': function (test) {
        //test.expect(2);
        const block = `${bullets}`;
        testBlock(block);

        const expected= expectedBullets;

        //test.ok(accepted, 'should be accepted.');
        test.deepEqual(value, expected, 'bad value for bullets');
        test.done();
    }

    ,

    'Read title, paragraph & bullets': function (test) {
        //test.expect(2);
        const block = `${lev1Title}\n${bullets}\n${standardParagraph}`;
        testBlock(block);

        //test.ok(accepted, 'should be accepted.');
       // const expected= [expectedLev1Title, expectedBullets,
         //   expectedStandardParagraph];
        const  expected= [expectedLev1Title].concat(expectedBullets).concat([expectedStandardParagraph])

        test.deepEqual(expected, value, 'bad value for bullets');
        test.done();
    },

/*
    'Read multilevel chapters': function (test) {
        //test.expect(2);
        const block = `
${lev1AltTitle}
${bullets}
${lev2AltTitle}
${standardParagraph}
${otherLev1Title}
${complexParagraph}
`;
        testBlock(block);

        //test.ok(accepted, 'should be accepted.');
        const expected= [expectedLev1Title, expectedBullets,
            expectedLev2Title, expectedStandardParagraph,
        expectedOtherLev1Title, expectedComplexParagraph];

        test.deepEqual(value, expected, 'bad value for complex text');
        test.done();
    }  */

}<commit_msg>#10 : wip  - Adding new code block test<commit_after>/**
 * Created by Nicolas Zozol on 15/12/2016.
 */

import Parser from '../../lib/standard/document-parser';

let value = undefined;
let accepted = undefined;
let parser = null

// used to debug value easily and to avoid [Object object] notation.
function display(val, prefix = false) {
    if (val === undefined) {
        val = value;
    }
    if (prefix) {
        console.info(`${prefix} : ${JSON.stringify(val)}`);
    } else {
        console.info(JSON.stringify(val));
    }
}

function testBlock(block) {
    const parsing = Parser.parse(block);
    value = parsing.value;
    accepted = parsing.isAccepted();
    console.info('parsing', parsing, '\n\n');
}

const standardParagraph = `Their job is to destroy
Death Stars.`;
const expectedStandardParagraph = {
    paragraph: [
        { text: 'Their job is to destroy Death Stars.' }
    ]
};

const lev1Title = '#Star Wars Rocks';
const otherLev1Title = '#Star Trek also Rocks';
const lev1AltTitle = 'Star Wars Rocks\n======';
const lev2Title = '##C3PO and R2D2';
const lev2AltTitle = 'C3PO and R2D2\n----';
const expectedLev1Title = { title: { level: 1, text: 'Star Wars Rocks' } };
const expectedOtherLev1Title = { title: { level: 1, text: 'Star Trek also Rocks' } };
const expectedLev2Title = { title: { level: 2, text: 'C3PO and R2D2' } };

const complexParagraph = `They know how to  *use the force*
  , speed up,  and destroy **Death Stars**.`;

const expectedComplexParagraph = {
    paragraph: [
        { text: 'They know how to  ' },
        { italic: 'use the force' },
        { text: '   , speed up,  and destroy ' },
        { bold: 'Death Stars' },
        { text: '.' }
    ]
};

const bullet1 = '* Look for plans';
const bullet2 = '* Find an **unexpensive** youngster';
const bullet3 = '- Kill an *old man*';
const bullet4level2 = '    - Use the `force`';
const bullet5level2 = '\t* Destroy Death Star   ';
const bullets = `${bullet1}\n${bullet2}\n${bullet3}\n${bullet4level2}\n${bullet5level2}`;

const expectedBullet1 = { bullet: { level: 1, content: [{ text: 'Look for plans' }] } };
const expectedBullet2 = {
    bullet: {
        level: 1, content: [
            { text: 'Find an ' },
            { bold: 'unexpensive' },
            { text: ' youngster' },
        ]
    }
};
const expectedBullet3 = {
    bullet: {
        level: 1, content: [
            { text: 'Kill an ' },
            { italic: 'old man' }
        ]
    }
};
const expectedBullet4level2 = {
    bullet: {
        level: 2, content: [
            { text: 'Use the ' },
            { code: 'force' }
        ]
    }
};
const expectedBullet5level2 = {
    bullet: {
        level: 2, content: [
            { text: 'Destroy Death Star   ' }
        ]
    }
};

const expectedBullets = [
    expectedBullet1,
    expectedBullet2,
    expectedBullet3,
    expectedBullet4level2,
    expectedBullet5level2
];

export default {
    setUp: function (done) {
        done();
    },

    'Multilines lines paragraph should be accepted': function (test) {
        const block = `${standardParagraph}`;
        testBlock(block);
        const expected = [expectedStandardParagraph];
        test.deepEqual(expected, value, 'bad value for multiline');
        test.done();
    },

    'Complex Multilines lines should be accepted': function (test) {
        const block = `${complexParagraph}`;
        testBlock(block);
        const expected = [expectedComplexParagraph];
        test.deepEqual(expected, value, 'bad value for complex paragraph');
        test.done();
    },

    'Read title and its paragraphs': function (test) {
        const block = `${lev1Title}\n${standardParagraph}\n\n${complexParagraph}`;
        testBlock(block);
        const expected = [
            expectedLev1Title,
            expectedStandardParagraph,
            expectedComplexParagraph
        ];

        test.deepEqual(expected, value, 'bad value for title & paragraphs');
        test.done();
    },

    'Read bullets': function (test) {
        //test.expect(2);
        const block = `${bullets}`;
        testBlock(block);

        //test.ok(accepted, 'should be accepted.');
        test.deepEqual(expectedBullets, value, 'bad value for bullets');
        test.done();
    }

    ,

    'Read title, paragraph & bullets': function (test) {
        //test.expect(2);
        const block = `${lev1Title}\n${bullets}\n${standardParagraph}`;
        testBlock(block);

        //test.ok(accepted, 'should be accepted.');
        // const expected= [expectedLev1Title, expectedBullets,
        //   expectedStandardParagraph];
        const expected = [expectedLev1Title].concat(expectedBullets).concat([expectedStandardParagraph])

        test.deepEqual(expected, value, 'bad value for bullets');
        test.done();
    },

    /*
     'Read multilevel chapters': function (test) {
     //test.expect(2);
     const block = `
     ${lev1AltTitle}
     ${bullets}
     ${lev2AltTitle}
     ${standardParagraph}
     ${otherLev1Title}
     ${complexParagraph}
     `;
     testBlock(block);

     //test.ok(accepted, 'should be accepted.');
     const expected= [expectedLev1Title, expectedBullets,
     expectedLev2Title, expectedStandardParagraph,
     expectedOtherLev1Title, expectedComplexParagraph];

     test.deepEqual(value, expected, 'bad value for complex text');
     test.done();
     }  */

    'Read space code line': function (test) {

        const spaceCodeLine = '        Star Wars is an Atari game\n';
        const expectedSpaceCodeLine = { code: 'Star Wars is an Amstrad game\n' };

        testBlock(spaceCodeLine);
        test.deepEqual(expectedSpaceCodeLine, value, 'bad value for space code line');
        test.done();
    },

    'Read tab code line': function (test) {

        const tabCodeLine = '\t\tStar Wars is an Amstrad game\n';
        const expectedTabCodeLine = { code: 'Star Wars is an Amstrad game\n' };
        testBlock(tabCodeLine);
        test.deepEqual(expectedTabCodeLine, value, 'bad value for space code line');
        test.done();
    },

    'Read tab code blocks': function (test) {
        //test.expect(2);

        const haskellCode = ```\t\timport Control.Monad
\t\t
\t\tsolveTable :: [String] -> [String] -> [(String, Integer)]
```;
        const concatenation = 'import Control.Monad\n\nsolveTable :: [String] -> [String] -> [(String, Integer)]\n';
        const expectedHaskellCode = { code: concatenation };

        testBlock(haskellCode);
        test.deepEqual(expectedHaskellCode, value, 'bad value for bullets');
        test.done();
    },
    'Read space code blocks': function (test) {
        //test.expect(2);

        // there are 8 spaces for first line, then 10 for the second line
        const spaceCodeBloc = `        Star Wars is an Amstrad game
           Star Wars is an Atari game
`;

        const expectedSpaceCodeBloc = { code: 'Star Wars is an Amstrad game\n  Star Wars is an Atari game' };

        testBlock(spaceCodeBloc);
        test.deepEqual(expectedSpaceCodeBloc, value, 'bad value for bullets');
        test.done();
    }
}<|endoftext|>


<commit_before>import estraverse from 'estraverse';
import matchDefaultExport from './match-default-export';
import matchNamedExport from './match-named-export';
import {isFunctionExpression} from '../../utils/function-type';
import ExportNamedDeclaration from '../../syntax/export-named-declaration';
import VariableDeclaration from '../../syntax/variable-declaration';

export default function (ast) {
  estraverse.replace(ast, {
    enter(node, parent) {
      let m;
      if ((m = matchDefaultExport(node)) && parent.type === 'Program') {
        return {
          type: 'ExportDefaultDeclaration',
          declaration: m.value
        };
      }
      if ((m = matchNamedExport(node)) && parent.type === 'Program') {
        if (isFunctionExpression(m.value)) {
          // Exclude functions with different name than the assigned property name
          if (compatibleIdentifiers(m.id, m.value.id)) {
            return new ExportNamedDeclaration({
              declaration: functionExpressionToDeclaration(m.value, m.id)
            });
          }
        }
        else if (m.value.type === 'ClassExpression') {
          // Exclude classes with different name than the assigned property name
          if (compatibleIdentifiers(m.id, m.value.id)) {
            return new ExportNamedDeclaration({
              declaration: classExpressionToDeclaration(m.value, m.id)
            });
          }
        }
        else if (m.value.type === 'Identifier') {
          return new ExportNamedDeclaration({
            specifiers: [
              {
                type: 'ExportSpecifier',
                exported: m.id,
                local: m.value
              }
            ]
          });
        }
        else {
          return new ExportNamedDeclaration({
            declaration: new VariableDeclaration('var', [
              {
                type: 'VariableDeclarator',
                id: m.id,
                init: m.value
              }
            ])
          });
        }
      }
    }
  });
}

// True when one of the identifiers is null or their names are equal.
function compatibleIdentifiers(id1, id2) {
  return !id1 || !id2 || id1.name === id2.name;
}

function functionExpressionToDeclaration(func, id) {
  func.type = 'FunctionDeclaration';
  func.id = id;

  // Transform <expression> to { return <expression>; }
  if (func.body.type !== 'BlockStatement') {
    func.body = {
      type: 'BlockStatement',
      body: [
        {
          type: 'ReturnStatement',
          argument: func.body
        }
      ]
    };
  }

  return func;
}

function classExpressionToDeclaration(cls, id) {
  cls.type = 'ClassDeclaration';
  cls.id = id;
  return cls;
}
<commit_msg>Split export transform to two: default & named<commit_after>import estraverse from 'estraverse';
import matchDefaultExport from './match-default-export';
import matchNamedExport from './match-named-export';
import {isFunctionExpression} from '../../utils/function-type';
import ExportNamedDeclaration from '../../syntax/export-named-declaration';
import VariableDeclaration from '../../syntax/variable-declaration';

export default function (ast) {
  estraverse.replace(ast, {
    enter(node, parent) {
      let m;
      if ((m = matchDefaultExport(node)) && parent.type === 'Program') {
        return exportDefault(m);
      }
      else if ((m = matchNamedExport(node)) && parent.type === 'Program') {
        return exportNamed(m);
      }
    }
  });
}

function exportDefault({value}) {
  return {
    type: 'ExportDefaultDeclaration',
    declaration: value
  };
}

function exportNamed({id, value}) {
  if (isFunctionExpression(value)) {
    // Exclude functions with different name than the assigned property name
    if (compatibleIdentifiers(id, value.id)) {
      return new ExportNamedDeclaration({
        declaration: functionExpressionToDeclaration(value, id)
      });
    }
  }
  else if (value.type === 'ClassExpression') {
    // Exclude classes with different name than the assigned property name
    if (compatibleIdentifiers(id, value.id)) {
      return new ExportNamedDeclaration({
        declaration: classExpressionToDeclaration(value, id)
      });
    }
  }
  else if (value.type === 'Identifier') {
    return new ExportNamedDeclaration({
      specifiers: [
        {
          type: 'ExportSpecifier',
          exported: id,
          local: value
        }
      ]
    });
  }
  else {
    return new ExportNamedDeclaration({
      declaration: new VariableDeclaration('var', [
        {
          type: 'VariableDeclarator',
          id: id,
          init: value
        }
      ])
    });
  }
}

// True when one of the identifiers is null or their names are equal.
function compatibleIdentifiers(id1, id2) {
  return !id1 || !id2 || id1.name === id2.name;
}

function functionExpressionToDeclaration(func, id) {
  func.type = 'FunctionDeclaration';
  func.id = id;

  // Transform <expression> to { return <expression>; }
  if (func.body.type !== 'BlockStatement') {
    func.body = {
      type: 'BlockStatement',
      body: [
        {
          type: 'ReturnStatement',
          argument: func.body
        }
      ]
    };
  }

  return func;
}

function classExpressionToDeclaration(cls, id) {
  cls.type = 'ClassDeclaration';
  cls.id = id;
  return cls;
}
<|endoftext|>


<commit_before>import NumberFormatter from './numberFormatter';
import * as consts from './contractorCalculatorConstants';

export default class ContractTakeHomeCalculator {

  static necessaryDataIsProvidedToCalculateSavings(settings) {
    return settings.dayRate > 0
      && settings.weeks > 0
      && settings.wages > 0
      && settings.expenses >= 0;
  }

  static calculateExpenses(settings) {

    if (!settings.expenseDetail)
      return settings.expenses;

    let mileage = 0;
    if (settings.mileage > 0)
      mileage = ((settings.mileage * 0.45) * 5) * settings.weeks;

    let months = 0;
    if (settings.weeks > 4)
      months = (settings.weeks / 4);

    let accountancy = 0;
    if (settings.accountancyFees > 0)
      accountancy = settings.accountancyFees * months;

    let insurance = 0;

    if (settings.insurance > 0)
      insurance = settings.insurance * months;

    let other = 0;
    if (settings.other > 0)
      other = settings.other * months;

    return parseFloat(mileage) + parseFloat(insurance) + parseFloat(accountancy) + parseFloat(other);

  }

  static calculate(settings) {

    const {dayRate, weeks, wages, expenses} = settings;

    const days = 5;
    // const paLimit = 11000;
    // const divTaxFreeAllowance = 5000;
    // const taxRate = 0.20;
    //
    // const basic = 43000;
    // const higher = 150000;
    //
    // const basicTaxRate = 0.075;
    // const higherTaxRate = 0.325;

    // let dayrate = 400;
    // let weeks = 46;
    // let wages = 8040; //8060 no NI,, todo allow second salary
    // let expenses = 5000;

    let grossEarned = ((dayRate * days) * weeks);
    let profitBeforeTax = grossEarned - wages - expenses;//grossEarned  - (wages + expenses);
    let corporationTax = (profitBeforeTax * consts.CORPORATION_TAX);
    let profitAfterTax = profitBeforeTax - corporationTax;

    let personalAllowanceLeft = (consts.PERSONAL_ALLOWANCE_LIMIT - wages) + consts.DIVIDEND_TAX_FREE_ALLOWANCE;

    let basicTaxable = (consts.BASIC_TAX_THRESHOLD - personalAllowanceLeft - wages);
    let tax1 = consts.BASIC_TAX_RATE * basicTaxable;

    let higherTaxable = (profitAfterTax - basicTaxable - personalAllowanceLeft);
    let tax2 = consts.HIGHER_TAX_RATE * higherTaxable;

    let divToTax = tax1 + tax2;

    let takeHome = parseFloat(profitAfterTax) + parseFloat(wages);
    let takeHomeAfterPersonalTax = parseFloat(takeHome) - parseFloat(divToTax);

    let percTakeHome = (takeHomeAfterPersonalTax / grossEarned) * 100;

    return {
      grossEarned: NumberFormatter.getCurrencyFormattedNumber(grossEarned),
      // salary: wages,
      // expenses: expenses,

      profitBeforeTax: NumberFormatter.getCurrencyFormattedNumber(profitBeforeTax),
      corpTax: NumberFormatter.getCurrencyFormattedNumber(corporationTax),
      profitAfterTax: NumberFormatter.getCurrencyFormattedNumber(profitAfterTax),

      // basic: basic,
      personalAllowanceLeft: NumberFormatter.getCurrencyFormattedNumber(personalAllowanceLeft),
      basicTaxable: NumberFormatter.getCurrencyFormattedNumber(basicTaxable),
      tax1: NumberFormatter.getCurrencyFormattedNumber(tax1),

      // higher: higher,
      tax2: NumberFormatter.getCurrencyFormattedNumber(tax2),
      divToTax: NumberFormatter.getCurrencyFormattedNumber(divToTax),
      takeHome: NumberFormatter.getCurrencyFormattedNumber(takeHome),
      takeHomeAfterPersonalTax: NumberFormatter.getCurrencyFormattedNumber(takeHomeAfterPersonalTax),
      percTakeHome: `${percTakeHome.toFixed(0)}%`
    };
  }
}
<commit_msg>fix higher rate defect<commit_after>import NumberFormatter from './numberFormatter';
import * as consts from './contractorCalculatorConstants';

export default class ContractTakeHomeCalculator {

  static necessaryDataIsProvidedToCalculateSavings(settings) {
    return settings.dayRate > 0
      && settings.weeks > 0
      && settings.wages > 0
      && settings.expenses >= 0;
  }

  static calculateExpenses(settings) {

    if (!settings.expenseDetail)
      return settings.expenses;

    let mileage = 0;
    if (settings.mileage > 0)
      mileage = ((settings.mileage * 0.45) * 5) * settings.weeks;

    let months = 0;
    if (settings.weeks > 4)
      months = (settings.weeks / 4);

    let accountancy = 0;
    if (settings.accountancyFees > 0)
      accountancy = settings.accountancyFees * months;

    let insurance = 0;

    if (settings.insurance > 0)
      insurance = settings.insurance * months;

    let other = 0;
    if (settings.other > 0)
      other = settings.other * months;

    return parseFloat(mileage) + parseFloat(insurance) + parseFloat(accountancy) + parseFloat(other);

  }

  static calculate(settings) {

    const {dayRate, weeks, wages, expenses} = settings;

    const days = 5;
    // const paLimit = 11000;
    // const divTaxFreeAllowance = 5000;
    // const taxRate = 0.20;
    //
    // const basic = 43000;
    // const higher = 150000;
    //
    // const basicTaxRate = 0.075;
    // const higherTaxRate = 0.325;

    // let dayrate = 400;
    // let weeks = 46;
    // let wages = 8040; //8060 no NI,, todo allow second salary
    // let expenses = 5000;

    let grossEarned = ((dayRate * days) * weeks);
    let profitBeforeTax = grossEarned - wages - expenses;//grossEarned  - (wages + expenses);
    let corporationTax = (profitBeforeTax * consts.CORPORATION_TAX);
    let profitAfterTax = profitBeforeTax - corporationTax;

    let personalAllowanceLeft = (consts.PERSONAL_ALLOWANCE_LIMIT - wages) + consts.DIVIDEND_TAX_FREE_ALLOWANCE;

    let basicTaxable = (consts.BASIC_TAX_THRESHOLD - personalAllowanceLeft - wages);
    let tax1 = consts.BASIC_TAX_RATE * basicTaxable;

    let higherTaxable = (profitAfterTax - basicTaxable - personalAllowanceLeft);
    let tax2 = higherTaxable <0 ? 0 : consts.HIGHER_TAX_RATE * higherTaxable;

    let divToTax = tax1 + tax2;

    let takeHome = parseFloat(profitAfterTax) + parseFloat(wages);
    let takeHomeAfterPersonalTax = parseFloat(takeHome) - parseFloat(divToTax);

    let percTakeHome = (takeHomeAfterPersonalTax / grossEarned) * 100;

    return {
      grossEarned: NumberFormatter.getCurrencyFormattedNumber(grossEarned),
      // salary: wages,
      // expenses: expenses,

      profitBeforeTax: NumberFormatter.getCurrencyFormattedNumber(profitBeforeTax),
      corpTax: NumberFormatter.getCurrencyFormattedNumber(corporationTax),
      profitAfterTax: NumberFormatter.getCurrencyFormattedNumber(profitAfterTax),

      // basic: basic,
      personalAllowanceLeft: NumberFormatter.getCurrencyFormattedNumber(personalAllowanceLeft),
      basicTaxable: NumberFormatter.getCurrencyFormattedNumber(basicTaxable),
      tax1: NumberFormatter.getCurrencyFormattedNumber(tax1),

      // higher: higher,
      tax2: NumberFormatter.getCurrencyFormattedNumber(tax2),
      divToTax: NumberFormatter.getCurrencyFormattedNumber(divToTax),
      takeHome: NumberFormatter.getCurrencyFormattedNumber(takeHome),
      takeHomeAfterPersonalTax: NumberFormatter.getCurrencyFormattedNumber(takeHomeAfterPersonalTax),
      percTakeHome: `${percTakeHome.toFixed(0)}%`
    };
  }
}
<|endoftext|>


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

import {LitElement, html} from "lit";
import UtilsNew from "../../core/utilsNew.js";

export default class SimpleChart extends LitElement {

    constructor() {
        super();
        this.#init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            active: {
                type: Boolean,
            },
            type: {
                type: String,
            },
            title: {
                type: String,
            },
            subtitle: {
                type: String,
            },
            data: {
                type: Object,
            },
            xAxisTitle: {
                type: String,
            },
            yAxisTitle: {
                type: String,
            },
            showButtons: {
                type: Boolean,
            },
            config: {
                // This must be a valid Highcharts configuration object
                type: Object,
            },
        };
    }

    #init() {
        this._prefix = UtilsNew.randomString(8);

        // Initially we set the default config, this will be overridden if 'config' is passed
        this._config = this.getDefaultConfig();
        this.type = "column";
    }

    updated(changedProperties) {
        if (changedProperties.has("config")) {
            this._config = {
                ...this.getDefaultConfig(),
                ...this.config,
            };
        }

        if (changedProperties.has("type") || changedProperties.has("data") || changedProperties.has("config")) {
            if (this.type && this.data) {
                switch (this.type) {
                    case "column":
                        this.renderColumnChart();
                        break;
                    case "bar":
                        this.renderBarChart();
                        break;
                    case "pie":
                        this.renderPieChart();
                        break;
                    default:
                        throw new Error("Chart type not supported");
                }
            }
        }
    }

    renderColumnChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "column",
                ...this._config.chart
            },
            title: {
                text: this.title,
                ...this._config.title
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle
            },
            xAxis: {
                title: {
                    text: this.xAxisTitle || ""
                },
                label: {
                    enabled: true
                },
                crosshair: true,
                ...this._config.xAxis
            },
            yAxis: {
                title: {
                    text: this.yAxisTitle || ""
                },
                min: 0,
                ...this._config.yAxis
            },
            plotOptions: {
                column: {
                    pointPadding: 0.2,
                    borderWidth: 0
                },
                ...this._config.plotOptions
            },
            series: Object.entries(this.data).map(([name, data]) => ({
                name: name,
                // TODO Proper fix after facet-result-view refactor.
                // Most of the variant stats are Maps with a single datapoint, in other cases we need an array (facets)
                data: Array.isArray(data) ? data : [data],
            })),
            credits: {
                enabled: false
            },
        });
    }

    renderBarChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "bar",
                ...this._config.chart,
            },
            title: {
                text: this.title,
                ...this._config.title,
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle,
            },
            xAxis: {
                title: {
                    text: this.xAxisTitle || "",
                },
                label: {
                    enabled: true,
                },
                crosshair: true,
                // categories: Object.keys(this.data).map(elem => elem),
                ...this._config.xAxis,
            },
            yAxis: {
                title: {
                    text: this.yAxisTitle || "",
                },
                min: 0,
                ...this._config.yAxis,
            },
            plotOptions: {
                column: {
                    pointPadding: 0.2,
                    borderWidth: 0,
                },
                ...this._config.plotOptions,
            },
            series: Object.entries(this.data).map(([name, data]) => ({
                name: name,
                // TODO Proper fix after facet-result-view refactor.
                // Most of the variant stats are Maps with a single datapoint, in other cases we need an array (facets)
                data: Array.isArray(data) ? data : [data],
            })),
            credits: {
                enabled: false,
            },
        });
    }

    renderPieChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "pie",
                plotBackgroundColor: null,
                plotBorderWidth: null,
                plotShadow: false,
            },
            title: {
                text: this.title,
                ...this._config.title,
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle,
            },
            accessibility: {
                point: {
                    valueSuffix: "%",
                },
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: "pointer",
                    dataLabels: {
                        enabled: false,
                    },
                    showInLegend: true,
                },
                ...this._config.plotOptions,
            },
            series: [
                {
                    data: Object.entries(this.data).map(([name, data]) => ({name: name, y: data})),
                }
            ],
            credits: {
                enabled: false,
            },
        });
    }

    render() {
        return html`

        `;
    }

    getDefaultConfig() {
        return {
            chart: {
                backgroundColor: {
                    // linearGradient: [0, 0, 500, 500],
                    stops: [
                        [0, "rgb(255, 255, 255)"],
                        [1, "rgb(240, 240, 255)"]
                    ]
                },
                borderWidth: 0,
                // plotBackgroundColor: "rgba(255, 255, 255, .9)",
                plotShadow: true,
                plotBorderWidth: 1
            },
        };
    }

}

customElements.define("simple-chart", SimpleChart);
<commit_msg>wc: add colors property to simple-chart component #TASK-1096<commit_after>/*
 * Copyright 2015-2016 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import UtilsNew from "../../core/utilsNew.js";

export default class SimpleChart extends LitElement {

    constructor() {
        super();
        this.#init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            active: {
                type: Boolean,
            },
            type: {
                type: String,
            },
            title: {
                type: String,
            },
            subtitle: {
                type: String,
            },
            data: {
                type: Object,
            },
            xAxisTitle: {
                type: String,
            },
            yAxisTitle: {
                type: String,
            },
            showButtons: {
                type: Boolean,
            },
            colors: {
                type: Object,
            },
            config: {
                // This must be a valid Highcharts configuration object
                type: Object,
            },
        };
    }

    #init() {
        this._prefix = UtilsNew.randomString(8);

        // Initially we set the default config, this will be overridden if 'config' is passed
        this._config = this.getDefaultConfig();
        this.type = "column";
        this.colors = {};
    }

    updated(changedProperties) {
        if (changedProperties.has("config")) {
            this._config = {
                ...this.getDefaultConfig(),
                ...this.config,
            };
        }

        if (changedProperties.has("type") || changedProperties.has("data") || changedProperties.has("config")) {
            if (this.type && this.data) {
                switch (this.type) {
                    case "column":
                        this.renderColumnChart();
                        break;
                    case "bar":
                        this.renderBarChart();
                        break;
                    case "pie":
                        this.renderPieChart();
                        break;
                    default:
                        throw new Error("Chart type not supported");
                }
            }
        }
    }

    renderColumnChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "column",
                ...this._config.chart
            },
            title: {
                text: this.title,
                ...this._config.title
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle
            },
            xAxis: {
                title: {
                    text: this.xAxisTitle || ""
                },
                label: {
                    enabled: true
                },
                crosshair: true,
                ...this._config.xAxis
            },
            yAxis: {
                title: {
                    text: this.yAxisTitle || ""
                },
                min: 0,
                ...this._config.yAxis
            },
            plotOptions: {
                column: {
                    pointPadding: 0.2,
                    borderWidth: 0
                },
                ...this._config.plotOptions
            },
            series: Object.entries(this.data).map(([name, data]) => ({
                name: name,
                // TODO Proper fix after facet-result-view refactor.
                // Most of the variant stats are Maps with a single datapoint, in other cases we need an array (facets)
                data: Array.isArray(data) ? data : [data],
                color: this.colors[name] ? this.colors[name] : undefined,
            })),
            credits: {
                enabled: false
            },
        });
    }

    renderBarChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "bar",
                ...this._config.chart,
            },
            title: {
                text: this.title,
                ...this._config.title,
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle,
            },
            xAxis: {
                title: {
                    text: this.xAxisTitle || "",
                },
                label: {
                    enabled: true,
                },
                crosshair: true,
                // categories: Object.keys(this.data).map(elem => elem),
                ...this._config.xAxis,
            },
            yAxis: {
                title: {
                    text: this.yAxisTitle || "",
                },
                min: 0,
                ...this._config.yAxis,
            },
            plotOptions: {
                column: {
                    pointPadding: 0.2,
                    borderWidth: 0,
                },
                ...this._config.plotOptions,
            },
            series: Object.entries(this.data).map(([name, data]) => ({
                name: name,
                // TODO Proper fix after facet-result-view refactor.
                // Most of the variant stats are Maps with a single datapoint, in other cases we need an array (facets)
                data: Array.isArray(data) ? data : [data],
                color: this.colors[name] ? this.colors[name] : undefined,
            })),
            credits: {
                enabled: false,
            },
        });
    }

    renderPieChart() {
        Highcharts.chart(this._prefix + "chart", {
            chart: {
                type: "pie",
                plotBackgroundColor: null,
                plotBorderWidth: null,
                plotShadow: false,
            },
            title: {
                text: this.title,
                ...this._config.title,
            },
            subtitle: {
                text: this.subtitle,
                ...this._config.subtitle,
            },
            accessibility: {
                point: {
                    valueSuffix: "%",
                },
            },
            plotOptions: {
                pie: {
                    allowPointSelect: true,
                    cursor: "pointer",
                    dataLabels: {
                        enabled: false,
                    },
                    showInLegend: true,
                },
                ...this._config.plotOptions,
            },
            series: [
                {
                    data: Object.entries(this.data).map(([name, data]) => ({name: name, y: data})),
                }
            ],
            credits: {
                enabled: false,
            },
        });
    }

    render() {
        return html`

        `;
    }

    getDefaultConfig() {
        return {
            chart: {
                backgroundColor: {
                    // linearGradient: [0, 0, 500, 500],
                    stops: [
                        [0, "rgb(255, 255, 255)"],
                        [1, "rgb(240, 240, 255)"]
                    ]
                },
                borderWidth: 0,
                // plotBackgroundColor: "rgba(255, 255, 255, .9)",
                plotShadow: true,
                plotBorderWidth: 1
            },
        };
    }

}

customElements.define("simple-chart", SimpleChart);
<|endoftext|>


<commit_before>import handleInput from '../handleInput';
import MockDate from 'mockdate';

import {
  REASON_PREFIX,
  DOWNVOTE_PREFIX,
  UPVOTE_PREFIX,
  VIEW_ARTICLE_PREFIX,
  getArticleURL,
} from 'src/lib/sharedUtils';

import initState from '../handlers/initState';
import choosingArticle from '../handlers/choosingArticle';
import choosingReply from '../handlers/choosingReply';
import askingReplyFeedback from '../handlers/askingReplyFeedback';
import askingArticleSubmissionConsent from '../handlers/askingArticleSubmissionConsent';
import askingReplyRequestReason from '../handlers/askingReplyRequestReason';
import defaultState from '../handlers/defaultState';
import { ManipulationError } from '../handlers/utils';

jest.mock('../handlers/initState');
jest.mock('../handlers/choosingArticle');
jest.mock('../handlers/choosingReply');
jest.mock('../handlers/askingReplyFeedback');
jest.mock('../handlers/askingArticleSubmissionConsent');
jest.mock('../handlers/askingReplyRequestReason');
jest.mock('../handlers/defaultState');

// Original session ID in context
const FIXED_DATE = 612964800000;

// If session is renewed, sessionId will become this value
const NOW = 1561982400000;

beforeEach(() => {
  initState.mockClear();
  choosingArticle.mockClear();
  choosingReply.mockClear();
  askingReplyFeedback.mockClear();
  askingArticleSubmissionConsent.mockClear();
  askingReplyRequestReason.mockClear();
  defaultState.mockClear();
  MockDate.set(NOW);
});

afterEach(() => {
  MockDate.reset();
});

it('rejects undefined input', () => {
  const data = {};
  const event = {};

  return expect(handleInput(data, event)).rejects.toMatchInlineSnapshot(
    `[Error: input undefined]`
  );
});

it('invokes state handler specified by event.postbackHandlerState', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_REPLY',
    input: 'reply-id',
  };

  choosingReply.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(choosingReply).toHaveBeenCalledTimes(1);
});

it('shows article list when VIEW_ARTICLE_PREFIX is sent', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${VIEW_ARTICLE_PREFIX}${getArticleURL('article-id')}`,
  };

  choosingArticle.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'CHOOSING_REPLY',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "searchedText": "",
                    "sessionId": 1561982400000,
                  },
                  "state": "CHOOSING_REPLY",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(choosingArticle).toHaveBeenCalledTimes(1);
});

it('Resets session on free-form input, triggers fast-forward', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: 'Newly forwarded message',
  };

  initState.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: true,
      state: 'CHOOSING_ARTICLE',
    })
  );

  choosingArticle.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'CHOOSING_REPLY',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 1561982400000,
                  },
                  "state": "CHOOSING_REPLY",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(initState).toHaveBeenCalledTimes(1);
  expect(choosingArticle).toHaveBeenCalledTimes(1);
});

it('processes upvote', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${UPVOTE_PREFIX}My upvote reason`,
  };

  askingReplyFeedback.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).toHaveBeenCalledTimes(1);
});

it('processes downvote', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${DOWNVOTE_PREFIX}My downvote reason`,
  };

  askingReplyFeedback.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).toHaveBeenCalledTimes(1);
});

it('processes first article reason submission', async () => {
  const context = {
    state: 'ASKING_ARTICLE_SUBMISSION_CONSENT',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${REASON_PREFIX}My reason sending it to DB`,
  };

  askingArticleSubmissionConsent.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: '__INIT__',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "__INIT__",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingArticleSubmissionConsent).toHaveBeenCalledTimes(1);
});

it('processes not replied yet reply request submission', async () => {
  const context = {
    state: 'ASKING_REPLY_REQUEST_REASON',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${REASON_PREFIX}My reason adding reply request`,
  };

  askingReplyRequestReason.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: '__INIT__',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "__INIT__",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyRequestReason).toHaveBeenCalledTimes(1);
});

it('handles unimplemented state using defaultState', async () => {
  const context = {
    state: '__INIT__',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'NOT_IMPLEMENTED_YET',
    input: 'foo',
  };

  defaultState.mockImplementationOnce(params => ({
    ...params,
    isSkipUser: false,
    replies: 'Cannot understand reply',
    state: '__INIT__',
  }));

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
          Object {
            "context": Object {
              "data": Object {
                "sessionId": 612964800000,
              },
              "state": "__INIT__",
            },
            "replies": "Cannot understand reply",
          }
        `);

  expect(initState).not.toHaveBeenCalled();
  expect(defaultState).toHaveBeenCalledTimes(1);
});

it('handles ManipulationError fired in handlers', async () => {
  const context = {
    state: 'CHOOSING_ARTICLE',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_ARTICLE',
    input: `article-id`,
  };

  choosingArticle.mockImplementationOnce(() =>
    Promise.reject(new ManipulationError('Foo error'))
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "CHOOSING_ARTICLE",
                },
                "replies": Array [
                  Object {
                    "altText": "Error: Foo error",
                    "contents": Object {
                      "body": Object {
                        "contents": Array [
                          Object {
                            "text": "Foo error",
                            "type": "text",
                            "wrap": true,
                          },
                        ],
                        "layout": "vertical",
                        "type": "box",
                      },
                      "header": Object {
                        "contents": Array [
                          Object {
                            "color": "#ffb600",
                            "text": "⚠ Wrong usage",
                            "type": "text",
                            "weight": "bold",
                          },
                        ],
                        "layout": "vertical",
                        "type": "box",
                      },
                      "styles": Object {
                        "body": Object {
                          "separator": true,
                        },
                      },
                      "type": "bubble",
                    },
                    "type": "flex",
                  },
                ],
              }
          `);
});

it('throws on unknown error', async () => {
  const context = {
    state: 'CHOOSING_ARTICLE',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_ARTICLE',
    input: `article-id`,
  };

  choosingArticle.mockImplementationOnce(() =>
    Promise.reject(new Error('Unknown error'))
  );

  await expect(handleInput(context, event)).rejects.toMatchInlineSnapshot(
    `[Error: Unknown error]`
  );
});
<commit_msg>Cover defaultState handler<commit_after>import MockDate from 'mockdate';
import initState from '../handlers/initState';
import choosingArticle from '../handlers/choosingArticle';
import choosingReply from '../handlers/choosingReply';
import askingReplyFeedback from '../handlers/askingReplyFeedback';
import askingArticleSubmissionConsent from '../handlers/askingArticleSubmissionConsent';
import askingReplyRequestReason from '../handlers/askingReplyRequestReason';
import { ManipulationError } from '../handlers/utils';
import handleInput from '../handleInput';

import {
  REASON_PREFIX,
  DOWNVOTE_PREFIX,
  UPVOTE_PREFIX,
  VIEW_ARTICLE_PREFIX,
  getArticleURL,
} from 'src/lib/sharedUtils';

jest.mock('../handlers/initState');
jest.mock('../handlers/choosingArticle');
jest.mock('../handlers/choosingReply');
jest.mock('../handlers/askingReplyFeedback');
jest.mock('../handlers/askingArticleSubmissionConsent');
jest.mock('../handlers/askingReplyRequestReason');

// Original session ID in context
const FIXED_DATE = 612964800000;

// If session is renewed, sessionId will become this value
const NOW = 1561982400000;

beforeEach(() => {
  initState.mockClear();
  choosingArticle.mockClear();
  choosingReply.mockClear();
  askingReplyFeedback.mockClear();
  askingArticleSubmissionConsent.mockClear();
  askingReplyRequestReason.mockClear();
  MockDate.set(NOW);
});

afterEach(() => {
  MockDate.reset();
});

it('rejects undefined input', () => {
  const data = {};
  const event = {};

  return expect(handleInput(data, event)).rejects.toMatchInlineSnapshot(
    `[Error: input undefined]`
  );
});

it('invokes state handler specified by event.postbackHandlerState', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_REPLY',
    input: 'reply-id',
  };

  choosingReply.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(choosingReply).toHaveBeenCalledTimes(1);
});

it('shows article list when VIEW_ARTICLE_PREFIX is sent', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${VIEW_ARTICLE_PREFIX}${getArticleURL('article-id')}`,
  };

  choosingArticle.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'CHOOSING_REPLY',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "searchedText": "",
                    "sessionId": 1561982400000,
                  },
                  "state": "CHOOSING_REPLY",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(choosingArticle).toHaveBeenCalledTimes(1);
});

it('Resets session on free-form input, triggers fast-forward', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: 'Newly forwarded message',
  };

  initState.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: true,
      state: 'CHOOSING_ARTICLE',
    })
  );

  choosingArticle.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'CHOOSING_REPLY',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 1561982400000,
                  },
                  "state": "CHOOSING_REPLY",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).not.toHaveBeenCalled();
  expect(initState).toHaveBeenCalledTimes(1);
  expect(choosingArticle).toHaveBeenCalledTimes(1);
});

it('processes upvote', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${UPVOTE_PREFIX}My upvote reason`,
  };

  askingReplyFeedback.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).toHaveBeenCalledTimes(1);
});

it('processes downvote', async () => {
  const context = {
    state: 'ASKING_REPLY_FEEDBACK',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${DOWNVOTE_PREFIX}My downvote reason`,
  };

  askingReplyFeedback.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: 'ASKING_REPLY_FEEDBACK',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "ASKING_REPLY_FEEDBACK",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyFeedback).toHaveBeenCalledTimes(1);
});

it('processes first article reason submission', async () => {
  const context = {
    state: 'ASKING_ARTICLE_SUBMISSION_CONSENT',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${REASON_PREFIX}My reason sending it to DB`,
  };

  askingArticleSubmissionConsent.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: '__INIT__',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "__INIT__",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingArticleSubmissionConsent).toHaveBeenCalledTimes(1);
});

it('processes not replied yet reply request submission', async () => {
  const context = {
    state: 'ASKING_REPLY_REQUEST_REASON',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'message',
    input: `${REASON_PREFIX}My reason adding reply request`,
  };

  askingReplyRequestReason.mockImplementationOnce(params =>
    Promise.resolve({
      ...params,
      isSkipUser: false,
      state: '__INIT__',
      replies: 'Foo replies',
    })
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "__INIT__",
                },
                "replies": "Foo replies",
              }
          `);

  expect(askingReplyRequestReason).toHaveBeenCalledTimes(1);
});

it('handles unimplemented state using defaultState', async () => {
  const context = {
    state: '__INIT__',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'NOT_IMPLEMENTED_YET',
    input: 'foo',
  };

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
          Object {
            "context": Object {
              "data": Object {
                "sessionId": 612964800000,
              },
              "state": "__INIT__",
            },
            "replies": Array [
              Object {
                "text": "我們看不懂 QQ
          大俠請重新來過。",
                "type": "text",
              },
            ],
          }
        `);

  expect(initState).not.toHaveBeenCalled();
});

it('handles ManipulationError fired in handlers', async () => {
  const context = {
    state: 'CHOOSING_ARTICLE',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_ARTICLE',
    input: `article-id`,
  };

  choosingArticle.mockImplementationOnce(() =>
    Promise.reject(new ManipulationError('Foo error'))
  );

  await expect(handleInput(context, event)).resolves.toMatchInlineSnapshot(`
              Object {
                "context": Object {
                  "data": Object {
                    "sessionId": 612964800000,
                  },
                  "state": "CHOOSING_ARTICLE",
                },
                "replies": Array [
                  Object {
                    "altText": "Error: Foo error",
                    "contents": Object {
                      "body": Object {
                        "contents": Array [
                          Object {
                            "text": "Foo error",
                            "type": "text",
                            "wrap": true,
                          },
                        ],
                        "layout": "vertical",
                        "type": "box",
                      },
                      "header": Object {
                        "contents": Array [
                          Object {
                            "color": "#ffb600",
                            "text": "⚠ Wrong usage",
                            "type": "text",
                            "weight": "bold",
                          },
                        ],
                        "layout": "vertical",
                        "type": "box",
                      },
                      "styles": Object {
                        "body": Object {
                          "separator": true,
                        },
                      },
                      "type": "bubble",
                    },
                    "type": "flex",
                  },
                ],
              }
          `);
});

it('throws on unknown error', async () => {
  const context = {
    state: 'CHOOSING_ARTICLE',
    data: { sessionId: FIXED_DATE },
  };
  const event = {
    type: 'postback',
    postbackHandlerState: 'CHOOSING_ARTICLE',
    input: `article-id`,
  };

  choosingArticle.mockImplementationOnce(() =>
    Promise.reject(new Error('Unknown error'))
  );

  await expect(handleInput(context, event)).rejects.toMatchInlineSnapshot(
    `[Error: Unknown error]`
  );
});
<|endoftext|>


<commit_before>var _ = require('underscore');
var colorScales = [
  '#7F3C8D',
  '#11A579',
  '#3969AC',
  '#F2B701',
  '#E73F74'
]; // Demo colors

function getColorRange (styles) {

  var colorRange = styles && styles.definition &&
      styles.definition.color &&
      styles.definition.color.range;

  return colorRange || colorScales;
}

/**
 *  Class to set categories to each color
 *  - Right now, there is a relation 1 color to 1 category.
 *  - If that category is not available in the new data, that
 *    color will be freed.
 *
 */

function CategoryColors (styles) {
  this.updateColors(styles);
}

CategoryColors.prototype.updateColors = function (styles) {
  var colorRange = getColorRange(styles);
  this.colors = {};
  _.each(colorRange, function (c) {
    this.colors[c] = null;
  }, this);
};

CategoryColors.prototype.updateData = function (d) {
  // Remove categories from colors where they are not present anymore
  _.each(this.colors, function (value, key) {
    if (!_.contains(d, value)) {
      this.colors[key] = null;
    } else {
      d = _.without(d, value);
    }
  }, this);

  // Set colors by new categories
  _.each(d, function (category) {
    var nextFreeColor = this.getNextAvailableColor();
    if (nextFreeColor) {
      this.colors[nextFreeColor] = category;
    }
  }, this);
};

CategoryColors.prototype.getNextAvailableColor = function () {
  for (var i in this.colors) {
    if (this.colors[i] === null) {
      return i;
    }
  }
  return null;
};

CategoryColors.prototype.getColorByCategory = function (category) {
  for (var i in this.colors) {
    if (this.colors[i] === category) {
      return i;
    }
  }
  return '#A5AA99';
};

CategoryColors.prototype.getCategoryByColor = function (color) {
  return this.colors[color];
};

module.exports = CategoryColors;
<commit_msg>Fix lint error<commit_after>var _ = require('underscore');
var colorScales = [
  '#7F3C8D',
  '#11A579',
  '#3969AC',
  '#F2B701',
  '#E73F74'
]; // Demo colors

function getColorRange (styles) {
  var colorRange = styles && styles.definition &&
      styles.definition.color &&
      styles.definition.color.range;

  return colorRange || colorScales;
}

/**
 *  Class to set categories to each color
 *  - Right now, there is a relation 1 color to 1 category.
 *  - If that category is not available in the new data, that
 *    color will be freed.
 *
 */

function CategoryColors (styles) {
  this.updateColors(styles);
}

CategoryColors.prototype.updateColors = function (styles) {
  var colorRange = getColorRange(styles);
  this.colors = {};
  _.each(colorRange, function (c) {
    this.colors[c] = null;
  }, this);
};

CategoryColors.prototype.updateData = function (d) {
  // Remove categories from colors where they are not present anymore
  _.each(this.colors, function (value, key) {
    if (!_.contains(d, value)) {
      this.colors[key] = null;
    } else {
      d = _.without(d, value);
    }
  }, this);

  // Set colors by new categories
  _.each(d, function (category) {
    var nextFreeColor = this.getNextAvailableColor();
    if (nextFreeColor) {
      this.colors[nextFreeColor] = category;
    }
  }, this);
};

CategoryColors.prototype.getNextAvailableColor = function () {
  for (var i in this.colors) {
    if (this.colors[i] === null) {
      return i;
    }
  }
  return null;
};

CategoryColors.prototype.getColorByCategory = function (category) {
  for (var i in this.colors) {
    if (this.colors[i] === category) {
      return i;
    }
  }
  return '#A5AA99';
};

CategoryColors.prototype.getCategoryByColor = function (color) {
  return this.colors[color];
};

module.exports = CategoryColors;
<|endoftext|>


<commit_before>/* Shows the SEED functional category hierarchy as a
 * collapsable/expandable bar chart
 *
 * Found a collapsable hierarcy example from Mike Bostock to follow:
 * https://gist.github.com/mbostock/1093025
 *
 * will adapt this to work with the KBase SEED annotations
 */

 (function( $, undefined ) {
    $.KBWidget({
        name: "KBaseSEEDFunctions",
        parent: "kbaseWidget",
        version: "1.0.0",

        wsUrl:"https://kbase.us/services/ws",

        options: {
            objNameOrId: null,
            wsNameOrId: null,
            objVer: null,
            loadingImage: "assets/img/loading.gif",
            kbCache:{},
            width:900
        },

        SEEDTree:{ "count": 0, "children":[] },
        subsysToGeneMap:[],

        objName:"",
        wsName:"",

        /**
         * Initialize the widget.
         */

        init: function(options) {
            this._super(options);

            var SEEDTree = this.SEEDTree;
            var subsysToGeneMap = this.subsysToGeneMap;

            var obj = {"ref" : this.options.wsNameOrId + "/" + this.options.objNameOrId };

            var prom = this.options.kbCache.req('ws', 'get_objects', [obj]);

            $.when(prom).fail($.proxy(function(error) {
                //this.renderError(error); Need to define this function when I have time
                console.log(error);
            }, this));

            $.when(prom).done($.proxy(function(genome) {
                var genomeObj = genome[0].data;
                console.log("Num Features: " + genomeObj.features.length);

                /*
                    First I am going to iterate over the Genome Typed Object and
                    create a mapping of the assigned functional roles (by SEED) to
                    an array of genes with those roles.

                    subsysToGeneMap [ SEED Role ] = Array of Gene Ids
                */

                genomeObj.features.forEach( function(f){

                    // Each function can have multiple genes, creating mapping of function to list of gene ids
                    if (subsysToGeneMap[f["function"]] === undefined) {subsysToGeneMap[f["function"]] = [];}
                    subsysToGeneMap[f["function"]].push(f["id"]);

                    // Not sure if this is necessary, but I'm going to keep track of the number of genes with
                    // SEED assigned functions in this count variable.
                    SEEDTree.count++;
                });

                console.log("S: " + SEEDTree.count);
                this.loadSEEDHierarchy();

            }, this));

            this.render();

            return this;
        },

        /*
          I need to load the SEED subsystem ontology. I am going to use
          the "subsys.txt" file I found at:
                ftp.theseed.org/subsystems/subsys.txt

          Note that this file is updated weekly, but not versioned. It's
          possible that errors will arise because the subsystems assigned
          in the genome object are out of date relative to the current
          subsys.txt file.

          file format is:
          Level 1 \t Level 2 \t Level 3 \t Level 4\t Optional GO id \t Optional GO desc \n

          ontologyDepth is set to 4 for SEED

          SEED is not a strict heirarchy, some nodes have multiple parents
          I'm going to keep track of a nodes parents to map things right.

          loadSEEDHierarchy() function will parse file and populate the SEEDTree data structure
        */
        loadSEEDHierarchy: function() {

            var ontologyDepth = 4; //this should be moved up to the global variables
            var nodeMap = {};

            var SEEDTree = this.SEEDTree;
            var subsysToGeneMap = this.subsysToGeneMap;

            d3.text("assets/data/subsys.txt", function(text) {
                var data = d3.tsv.parseRows(text);
                console.log("Lines: " + data.length);

                for (i = 0; i < data.length; i++) {
                    var geneCount = 0;
                    var nodeHierarchy = "";
                    var parentHierarchy = "SEED";

                    if (subsysToGeneMap[data[i][3]] === undefined) {
                        // if barchart needs to only show the subsystems that have genes in this genome,
                        // uncomment the continue statement.
                        //continue;
                    } else {
                        geneCount = subsysToGeneMap[data[i][3]].length;
                    }

                    for (j = 0; j < ontologyDepth; j++) {

                        // some node names are an empty string "". I'm going to set these to
                        // a modified version of their parent node name
                        data[i][j] = (data[i][j] === "") ? "--- " + data[i][j-1] + " ---" : data[i][j];
                        nodeHierarchy = parentHierarchy + ":" + data[i][j];

                        // create new node for top level of hierarchy if it's not already defined.
                        if (j === 0) {
                            if (nodeMap[nodeHierarchy] === undefined) {
                                var node = { "name" : data[i][j], size : 0, "children" : [] };
                                SEEDTree.children.push(node);
                                nodeMap[nodeHierarchy] = node;
                            }
                        } else {
                            if (nodeMap[nodeHierarchy] === undefined) {
                                var node = { "name" : data[i][j], size : 0, "children" : [] };
                                nodeMap[parentHierarchy].children.push(node);
                                nodeMap[nodeHierarchy] = node;

                                if ( j === ontologyDepth - 1 && subsysToGeneMap[data[i][j]] !== undefined) {
                                    subsysToGeneMap[data[i][j]].forEach( function(f){
                                        var gene = { "name" : f, "size" : 0 };
                                        node.children.push( gene );
                                    });
                                }
                            }
                        }
                        nodeMap[nodeHierarchy].size += geneCount;
                        parentHierarchy = nodeHierarchy;
                    }
                }

            });

            SEEDTree.children.forEach(this.collapse);
        },

        collapse: function() {
            if (d.children) {
                d._children = d.children;
                d._children.forEach(collapse);
                d.children = null;
            }
        },

        getData: function() {
            return {title:"SEED Functional Categories :",id:this.objName, workspace:this.wsName};
        },

        render: function() {
            var margin = {top: 30, right: 20, bottom: 30, left: 20},
                width = 960 - margin.left - margin.right;

            this.$elem.append('
');

            var svg = d3.select("#mainview").append("svg")
                        .attr("width", width + margin.left + margin.right)
                        .append("g")
                        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        }

    });
})( jQuery );<commit_msg>Bar graph appears on screen, still having some scoping problem causing subcategories to not be visible`<commit_after>/* Shows the SEED functional category hierarchy as a
 * collapsable/expandable bar chart
 *
 * Found a collapsable hierarcy example from Mike Bostock to follow:
 * https://gist.github.com/mbostock/1093025
 *
 * will adapt this to work with the KBase SEED annotations
 */

 (function( $, undefined ) {
    $.KBWidget({
        name: "KBaseSEEDFunctions",
        parent: "kbaseWidget",
        version: "1.0.0",

        wsUrl:"https://kbase.us/services/ws",

        options: {
            objNameOrId: null,
            wsNameOrId: null,
            objVer: null,
            loadingImage: "assets/img/loading.gif",
            kbCache:{},
            width:900
        },

        SEEDTree:{ "name":"SEED", "count": 0, "children":[], "size":0, "x0":0, "y0":0 },
        subsysToGeneMap:[],

        margin:{top: 30, right: 20, bottom: 30, left: 20},
        width: 920,
        barHeight : 20,
        barWidth:400,
        svg:null,

        i : 0,
        duration : 400,
        root:null,

        tree:null,

        objName:"",
        wsName:"",

        /**
         * Initialize the widget.
         */

        init: function(options) {
            this._super(options);
            this.render();

            var SEEDTree = this.SEEDTree;
            var subsysToGeneMap = this.subsysToGeneMap;

            var obj = {"ref" : this.options.wsNameOrId + "/" + this.options.objNameOrId };

            var prom = this.options.kbCache.req('ws', 'get_objects', [obj]);

            $.when(prom).fail($.proxy(function(error) {
                //this.renderError(error); Need to define this function when I have time
                console.log(error);
            }, this));

            $.when(prom).done($.proxy(function(genome) {
                var genomeObj = genome[0].data;
                console.log("Num Features: " + genomeObj.features.length);

                /*
                    First I am going to iterate over the Genome Typed Object and
                    create a mapping of the assigned functional roles (by SEED) to
                    an array of genes with those roles.

                    subsysToGeneMap [ SEED Role ] = Array of Gene Ids
                */

                genomeObj.features.forEach( function(f){

                    // Each function can have multiple genes, creating mapping of function to list of gene ids
                    if (subsysToGeneMap[f["function"]] === undefined) {subsysToGeneMap[f["function"]] = [];}
                    subsysToGeneMap[f["function"]].push(f["id"]);

                    // Not sure if this is necessary, but I'm going to keep track of the number of genes with
                    // SEED assigned functions in this count variable.
                    SEEDTree.count++;
                });

                console.log("S: " + SEEDTree.count);
                this.loadSEEDHierarchy();

            }, this));

            return this;
        },

        /*
          I need to load the SEED subsystem ontology. I am going to use
          the "subsys.txt" file I found at:
                ftp.theseed.org/subsystems/subsys.txt

          Note that this file is updated weekly, but not versioned. It's
          possible that errors will arise because the subsystems assigned
          in the genome object are out of date relative to the current
          subsys.txt file.

          file format is:
          Level 1 \t Level 2 \t Level 3 \t Level 4\t Optional GO id \t Optional GO desc \n

          ontologyDepth is set to 4 for SEED

          SEED is not a strict heirarchy, some nodes have multiple parents
          I'm going to keep track of a nodes parents to map things right.

          loadSEEDHierarchy() function will parse file and populate the SEEDTree data structure
        */
        loadSEEDHierarchy: function() {
            var self = this;
            var ontologyDepth = 4; //this should be moved up to the global variables
            var nodeMap = {};

            var SEEDTree = self.SEEDTree;
            var subsysToGeneMap = self.subsysToGeneMap;

            d3.text("assets/data/subsys.txt", function(text) {
                var data = d3.tsv.parseRows(text);
                console.log("Lines: " + data.length);

                for (i = 0; i < data.length; i++) {
                    var geneCount = 0;
                    var nodeHierarchy = "";
                    var parentHierarchy = "SEED";

                    if (subsysToGeneMap[data[i][3]] === undefined) {
                        // if barchart needs to only show the subsystems that have genes in this genome,
                        // uncomment the continue statement.
                        //continue;
                    } else {
                        geneCount = subsysToGeneMap[data[i][3]].length;
                    }

                    for (j = 0; j < ontologyDepth; j++) {

                        // some node names are an empty string "". I'm going to set these to
                        // a modified version of their parent node name
                        data[i][j] = (data[i][j] === "") ? "--- " + data[i][j-1] + " ---" : data[i][j];
                        nodeHierarchy = parentHierarchy + ":" + data[i][j];

                        // create new node for top level of hierarchy if it's not already defined.
                        if (j === 0) {
                            if (nodeMap[nodeHierarchy] === undefined) {
                                var node = { "name" : data[i][j], size : 0, "children" : [] };
                                SEEDTree.children.push(node);
                                nodeMap[nodeHierarchy] = node;
                            }
                        } else {
                            if (nodeMap[nodeHierarchy] === undefined) {
                                var node = { "name" : data[i][j], size : 0, "children" : [] };
                                nodeMap[parentHierarchy].children.push(node);
                                nodeMap[nodeHierarchy] = node;

                                if ( j === ontologyDepth - 1 && subsysToGeneMap[data[i][j]] !== undefined) {
                                    subsysToGeneMap[data[i][j]].forEach( function(f){
                                        var gene = { "name" : f, "size" : 0 };
                                        node.children.push( gene );
                                    });
                                }
                            }
                        }
                        nodeMap[nodeHierarchy].size += geneCount;
                        parentHierarchy = nodeHierarchy;
                    }
                }

            console.log("something " + self.SEEDTree.children.length);
            $.when(
                self.SEEDTree.children.forEach(function(d) {
                    self.collapse(d)})
                ).done(
                    self.update(self.SEEDTree)
                );
            //this.update(this.root = SEEDTree);
            }); //).done( $.when( SEEDTree.children.forEach(self.collapse) ).done( self.update(self.SEEDTree)) );
            //this.update(this.root = SEEDTree);
        },

        update: function(source) {

            //if (this.tree === null) { this.tree = d3.layout.tree().nodeSize([0, 4]);}
            var self = this;
            var tree = d3.layout.tree().nodeSize([0, 4]);
            var nodes = tree.nodes(self.SEEDTree);
            console.log("Z: " + this.SEEDTree.children.length);
            var scale = d3.scale.linear().domain([0,400]).range([0,290]);
            var height = Math.max(500, nodes.length * this.barHeight + this.margin.top + this.margin.bottom);
            var i = self.i;
            d3.select("svg").transition()
                .duration(this.duration)
                .attr("height", height);

            d3.select(self.frameElement).transition()
                .duration(this.duration)
                .style("height", height + "px");

            // Compute the "layout".
            nodes.forEach(function(n, i) {
                n.x = i * self.barHeight;
            });

            // Update the nodes…
            var node = this.svg.selectAll("g.node")
                .data(nodes, function(d) { return d.id || (d.id = ++i); });

            var nodeEnter = node.enter().append("g")
                .attr("class", "node")
                .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
                .style("opacity", 1e-6);

            // Enter any new nodes at the parent's previous position.
            nodeEnter.append("rect")
                .attr("y", -this.barHeight / 2)
                .attr("x", 300)
                .attr("height", self.barHeight)
                .attr("width", self.barWidth)
                .style("fill", self.color)
                .on("click", $.proxy(function(d) {self.click(d)}, self));

            nodeEnter.append("text")
                .attr("dy", 3.5)
                .attr("dx", 300 + 5.5)
                .text(function(d) { return d.name; });

            nodeEnter.append("rect")
                .attr("y", -self.barHeight / 2)
                //.attr("x", function (d) { return 0 - d.depth * 4;} )
                .attr("x", function (d) { return 0 + 295 - scale(d.size) - d.depth * 4;} )
                .attr("height", self.barHeight)
                .attr("width", function (d) { return scale(d.size); })
                .style("fill", self.color);

            nodeEnter.append("text")
                .attr("dy", 3.5)
                .attr("dx", 5.5)
                .text(function(d) { return d.size; });

            // Transition nodes to their new position.
            nodeEnter.transition()
                .duration(this.duration)
                .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
                .style("opacity", 1);

            node.transition()
                .duration(this.duration)
                .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
                .style("opacity", 1)
                .select("rect")
                .style("fill", self.color);

            // Transition exiting nodes to the parent's new position.
            node.exit().transition()
                .duration(this.duration)
                .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
                .style("opacity", 1e-6)
                .remove();

            // Stash the old positions for transition.
            nodes.forEach(function(d) {
                d.x0 = d.x;
                d.y0 = d.y;
            });

        },

        // Toggle children on click.
        click: function(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }
            console.log(this);
            this.update(d);
        },

        color: function(d) {
            return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
        },

        collapse: function(d) {
            var self = this;
            if (d.children) {
                d._children = d.children;
                //for(var i = 0; i < d._children.length; i++) {
                //    self.collapse(d._children[i]);
                //}
                d._children.forEach( function(n) {self.collapse(n)});
                d.children = null;
            }
        },

        getData: function() {
            return {title:"SEED Functional Categories :",id:this.objName, workspace:this.wsName};
        },

        render: function() {
            var margin =  this.margin,
                width = this.width;
                //svg = this.svg;

            this.$elem.append('
');

            this.svg = d3.select("#mainview").append("svg")
                    .attr("width", width + margin.left + margin.right)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        }

    });
})( jQuery );<|endoftext|>


<commit_before>/**
 * Just a simple example widget - makes a div with "Hello world!"
 * in a user-defined color (must be a css color - 'red' or 'yellow' or '#FF0000')
 */
(function( $, undefined ) {
    $.KBWidget({
        name: "kbaseSimulationSet",
        parent: "kbaseWidget",
        version: "1.0.0",
        options: {
            color: "black",
        },

init: function(options) {
            this._super(options);
            var self = this;
            var data = options.data
            data = $.extend(data,{});
            console.log (data[0])
            //this.$elem.append(JSON.stringify(data))
            console.log(data[0].data.id, data[0].info[7]);
            var simu = data[0].data;

/*
                var table = $('
');

                	for (var i = 0; i < data[0].data.phenotypeSimulations.length; i++) {
                    	 var pheno_id = data[0].data.phenotypeSimulations[i].id;
                    	  var pheno_class = data[0].data.phenotypeSimulations[i].class;

                    	table.append('
'+pheno_id+'	'+
')

                	}

                this.$elem.append (table)
*/
                var tableSettings = {
					 "sPaginationType": "bootstrap",
					 "iDisplayLength": 10,
					 "aaData": simu.phenotypeSimulations,
					 "aaSorting": [[ 3, "desc" ]],
					 "aoColumns": [
					   { "sTitle": "Name", 'mData': 'id'},
					   { "sTitle": "phenoclass", 'mData': function(d) {
						 return d.phenoclass;
					   }},
					   { "sTitle": "Simulated Growth", 'mData': function(d) {
						 return d.simulatedGrowth
					   }},
					   { "sTitle": "Simulated Growth Fraction", 'mData': function(d) {
						 return d.simulatedGrowthFraction
					   }},
					  ],

				}

				var simu_table = $('
')
				this.$elem.append(simu_table)
				simu_table.dataTable(tableSettings);

/*

        // Code to displaying phenotype overview data
				var keys = [
					{key: 'wsid'},
					{key: 'ws'},
					{key: 'kbid'},
					{key: 'phenoclass'},
					{key: 'simgrowth'},
					{key: 'simgrowthfrac'},
					{key: 'date'}
				];
				var phenooverdata = {
					wsid: data[0].info[1],
					ws: data[0].info[7],
					kbid: data[0].data.id,
					phenoclass: data[0].data.phenoclass,
					simgrowth: data[0].data.simulatedGrowth,
					simgrowfrac: data[0].data.simulatedGrowthFraction,
					//errors: data[0].data.importErrors,
					//owner: data[0].creator,
					date: data[0].created,
				};

				//var labels = ['Name','Workspace','KBID','Phenotype','Simulated Growth','Simulated Growth Fraction','Creation date'];
				//var table = kb.ui.objTable('overview-table',phenooverdata,keys,labels);
				//container.find('#overview-table').append(table.find('tbody'));
				//Code for loading the phenotype list table
				simu = data[0].data;
				var tableSettings = {
					 "sPaginationType": "bootstrap",
					 "iDisplayLength": 10,
					 "aaData": simu.simulations,
					 "aaSorting": [[ 3, "desc" ]],
					 "aoColumns": [
					   //{ "sTitle": "Name", 'mData': 'name'},
					   { "sTitle": "Media", 'mData': function(d) {
						 return d.id;
					   }},
					   { "sTitle": "Growth Analysis", 'mData': function(d) {
						 return d.phenoclass;
					   }},
					   { "sTitle": "Simulated Growth", 'mData': function(d) {
						 return d.simulatedGrowth
					   }},
					   { "sTitle": "Simulated Growth Fraction", 'mData': function(d) {
						 return d.simulatedGrowthFraction
					   }},
					  ],

				}
				//var table = $('#phenotypes-table').dataTable(tableSettings);

*/

            return this;

        }

/*
		translaterefs: function(reflist,refhash) {
			var obj_refs = []
        	for (var i in reflist) {
                obj_refs.push({ref: reflist[i]})
            }
        	var prom = kb.ws.get_object_info(obj_refs)
			$.when(prom).done(function(refinfo) {
        	    for (var i=0; i<refinfo.length; i++) {
        	    	var item = refinfo[i];
        	    	var full_type = item[2];
        	    	var module = full_type.split('.')[0];
					var type = full_type.slice(full_type.indexOf('.')+1);
					var kind = type.split('-')[0];
        	    	var label = item[7]+"/"+item[1];
        	    	switch (kind) {
						case 'FBA':
							route = 'ws.fbas';
							break;
						case 'FBAModel':
							route = 'ws.mv.model';
							break;
						case 'Media':
							route = 'ws.media';
							break;
						case 'Genome':
							route = 'ws.genome';
							break;
						case 'MetabolicMap':
							route = 'ws.maps';
							break;
						case 'PhenotypeSet':
							route = 'ws.phenotype';
							break;
					}
					var url = route+"({ws:'"+item[7]+"', id:'"+item[1]+"'})";
        	    	var link = ''+label+''
        	    	refhash[reflist[i]] = link;
        		}
			})
			return prom;
		},

        init: function(options) {
            this._super(options);
            var self = this;
            var data = options.data
            data = $.extend(data,{})
        	var reflist = data[0].refs;
        	reflist.push(data[0].data.genome_ref);
        	var refhash = {};
        	var prom = this.translaterefs(reflist,refhash);
        	var container = this.$elem;
        	$.when(prom).done(function() {
				var tables = ['Overview', 'Phenotypes'];
				var tableIds = ['overview', 'phenotypes'];
				// build tabs
				var tabs = $('
 \

 \
								'+tables[0]+' \

');
				for (var i=1; i<tableIds.length; i++) {
					tabs.append('
'+tables[i]+'
');
				}
				// add tabs
				container.append(tabs);

				var tab_pane = $('
')
				// add table views (don't hide first one)
				tab_pane.append('
 \

\

');

				for (var i=1; i<tableIds.length; i++) {
					var tableDiv = $('
 ');
					var table = $('
');
					tableDiv.append(table);
					tab_pane.append(tableDiv);
				}

				container.append(tab_pane)

				// event for showing tabs
				$('#table-tabs a').click(function (e) {
					e.preventDefault();
					$(this).tab('show');
				})

				// Code to displaying phenotype overview data
				var keys = [
					{key: 'wsid'},
					{key: 'ws'},
					{key: 'kbid'},
					{key: 'source'},
					{key: 'genome'},
					{key: 'type'},
					{key: 'errors'},
					{key: 'owner'},
					{key: 'date'}
				];
				var phenooverdata = {
					wsid: data[0].info[1],
					ws: data[0].info[7],
					kbid: data[0].data.id,
					source: data[0].data.source,
					genome: refhash[data[0].data.genome_ref],
					type: data[0].data.type,
					errors: data[0].data.importErrors,
					owner: data[0].creator,
					date: data[0].created,
				};
				var labels = ['Name','Workspace','KBID','Source','Genome','Type','Errors','Owner','Creation date'];
				var table = kb.ui.objTable('overview-table',phenooverdata,keys,labels);
				container.find('#overview-table').append(table.find('tbody'));
				//Code for loading the phenotype list table
				pheno = data[0].data;
				var tableSettings = {
					 "sPaginationType": "bootstrap",
					 "iDisplayLength": 10,
					 "aaData": pheno.phenotypes,
					 "aaSorting": [[ 3, "desc" ]],
					 "aoColumns": [
					   { "sTitle": "Name", 'mData': 'name'},
					   { "sTitle": "Media", 'mData': function(d) {
						 return refhash[d.media_ref];
					   }},
					   { "sTitle": "Gene KO", 'mData': function(d) {
						 return d.geneko_refs.join("
")
					   }},
					   { "sTitle": "Additional compounds", 'mData': function(d) {
						 return d.additionalcompound_refs.join("
")
					   }},
					   { "sTitle": "Growth", 'mData': 'normalizedGrowth'},
					 ],
					 "oLanguage": {
						 "sEmptyTable": "No objects in workspace",
						 "sSearch": "Search: "
					 },
					 //'fnDrawCallback': events
				}
				var table = $('#phenotypes-table').dataTable(tableSettings);
			})
            return this;
        }
*/

    });
})( jQuery )<commit_msg>changes to kbaseSimulationSet.js<commit_after>/**
 * Just a simple example widget - makes a div with "Hello world!"
 * in a user-defined color (must be a css color - 'red' or 'yellow' or '#FF0000')
 */
(function( $, undefined ) {
    $.KBWidget({
        name: "kbaseSimulationSet",
        parent: "kbaseWidget",
        version: "1.0.0",
        options: {
            color: "black",
        },

init: function(options) {
            this._super(options);
            var self = this;
            var data = options.data
            data = $.extend(data,{});
            console.log (data[0])
            //this.$elem.append(JSON.stringify(data))
            console.log(data[0].data.id, data[0].info[7]);
            var simu = data[0].data;

/*
                var table = $('
');

                	for (var i = 0; i < data[0].data.phenotypeSimulations.length; i++) {
                    	 var pheno_id = data[0].data.phenotypeSimulations[i].id;
                    	  var pheno_class = data[0].data.phenotypeSimulations[i].class;

                    	table.append('
'+pheno_id+'	'+
')

                	}

                this.$elem.append (table)
*/
                var tableSettings = {
					 "sPaginationType": "bootstrap",
					 "iDisplayLength": 10,
					 "aaData": simu.phenotypeSimulations,
					 "aaSorting": [[ 3, "desc" ]],
					 "aoColumns": [
					   { "sTitle": "Name", 'mData': 'id'},
					   { "sTitle": "phenoclass", 'mData': function(d) {
						 return d.phenoclass;
					   }},
					   { "sTitle": "Simulated Growth", 'mData': function(d) {
						 return d.simulatedGrowth
					   }},
					   { "sTitle": "Simulated Growth Fraction", 'mData': function(d) {
						 return d.simulatedGrowthFraction
					   }},
					  ],

				}

				var simu_table = $('
')
				this.$elem.append(simu_table)
				simu_table.dataTable(tableSettings);

            return this;

        }

    });
})( jQuery )<|endoftext|>