<commit_before>import R from 'ramda';
import { Maybe } from 'ramda-fantasy';

import * as Project from './project';
import * as Patch from './patch';
import * as Node from './node';
import * as Link from './link';
import * as Pin from './pin';

// ================================================
//
// Utility functions
//
// ================================================

// :: Maybe Function -> a -> Maybe a
const apOrSkip = R.curry((maybeFn, val) => {
  const maybeVal = Maybe(val);
  const result = maybeFn.ap(maybeVal);
  return Maybe.isJust(result) ? result : maybeVal;
});
// :: a -> Maybe a
const maybeEmpty = R.ifElse(
  R.isEmpty,
  Maybe.Nothing,
  Maybe.of
);

// :: a -> [Function fn] -> a
// fn :: a -> Applicative a
// For example,
//   const t = key => R.compose(Maybe, R.assoc(key, true));
//   reduceChainOver({}, [t('a'), t('b')]);
//   { 'a': true, 'b': true }
const reduceChainOver = R.curry((overObj, fnList) =>
  R.compose(
    R.unnest,
    R.reduce(R.flip(R.chain), Maybe.of(overObj))
  )(fnList)
);

// :: String -> Object -> [a]
const propValues = key => R.compose(R.values, R.propOr({}, key));

// :: [Function] -> Function fn
// fn :: a -> a
const composeA = R.ifElse(
  R.isEmpty,
  R.always(R.identity),
  R.apply(R.compose)
);

// ================================================
//
// Getters from old bundle
//
// ================================================

// :: Project -> [Patch ^ NodeType]
export const mergePatchesAndNodeTypes = R.converge(
  R.mergeWith(R.merge),
  [
    R.prop('patches'),
    R.prop('nodeTypes'),
  ]
);

// :: {} -> String
const getLabel = R.prop('label');
// :: {} -> [a]
const getNodes = propValues('nodes');
// :: {} -> [a]
const getLinks = propValues('links');

// ================================================
//
// Conversion functions
//
// ================================================

// :: Path -> Patch -> Project -> Project
const assocPatchUnsafe = R.curry(
  (path, patch, project) => R.assocPath(['patches', path], patch, project)
);

// :: Patch -> Maybe fn
// fn :: Patch -> Patch
const addLabel = oldPatch => Maybe(getLabel(oldPatch)).map(Patch.setPatchLabel);

// :: String -> String
export const convertPinType = R.cond([
  [R.equals('bool'), R.always('boolean')],
  [R.T, R.identity],
]);

// :: Node -> Node
const updateNodeIdMap = R.curry((nodeIdMap, oldNode, newNode) => {
  nodeIdMap[oldNode.id] = newNode.id; // eslint-disable-line
  return newNode;
});

// :: NodeIdMap -> String -> String
const getLinkNodeId = R.flip(R.prop);

// :: NodeIdMap -> String -> String
const getLinkPin = R.ifElse(
  R.flip(R.has),
  getLinkNodeId,
  R.nthArg(1)
);

// :: NodeOld -> Function fn
// fn :: Node -> Node
const convertNodePins = R.compose(
  composeA,
  R.values,
  R.mapObjIndexed((pin, key) => {
    const setCurryPin = Maybe(pin.injected).map(Node.curryPin(key));
    const setPinValue = Maybe(pin.value).map(Node.setPinCurriedValue(key));
    return R.compose(
      R.chain(R.identity),
      R.chain(apOrSkip(setPinValue)),
      apOrSkip(setCurryPin)
    );
  }),
  R.prop('pins')
);

// :: NodeIdMap -> PatchOld -> Function fn
// fn :: Patch -> Patch
const convertNodes = R.curry((nodeIdMap, patchOld) =>
  R.compose(
    composeA,
    R.map(oldNode => Node.createNode(oldNode.position, oldNode.typeId)
      .map(updateNodeIdMap(nodeIdMap, oldNode))
      .map(convertNodePins(oldNode))
      .chain(Patch.assocNode)
    ),
    getNodes
  )(patchOld)
);

// :: NodeIdMap -> PatchOld -> Function fn
// fn :: Patch -> Patch
const convertLinks = nodeIdMap => R.compose(
  R.map(R.compose(
    Patch.assocLink,
    R.converge(
      Link.createLink,
      [
        R.compose(getLinkPin(nodeIdMap), R.prop('pinKey'), R.head),
        R.compose(getLinkNodeId(nodeIdMap), R.prop('nodeId'), R.head),
        R.compose(getLinkPin(nodeIdMap), R.prop('pinKey'), R.last),
        R.compose(getLinkNodeId(nodeIdMap), R.prop('nodeId'), R.last),
      ]
    ),
    R.prop('pins')
  )),
  getLinks
);

// :: Project -> PatchOld -> Patch
const getPatchByOldPatch = project => R.compose(
  R.unnest,
  Project.getPatchByPath(R.__, project),
  R.prop('id')
);

// :: Project -> [PatchOld] -> [Patch]
const getPatchesWithLinks = R.curry(
  (nodeIdMap, project) => R.map(R.converge(
    reduceChainOver,
    [
      getPatchByOldPatch(project),
      convertLinks(nodeIdMap),
    ]
  ))
);

// :: NodeIdMap -> ProjectOld -> Project -> Project
const convertLinksInPatches = R.curry(
  (nodeIdMap, projectOld, project) => R.compose(
    reduceChainOver(project),
    R.map(R.apply(Project.assocPatch)),
    R.converge(
      R.zip,
      [
        R.map(R.prop('id')),
        getPatchesWithLinks(nodeIdMap, project),
      ]
    ),
    R.values,
    mergePatchesAndNodeTypes
  )(projectOld)
);

// :: PatchOld -> Function fn
// fn :: Patch -> Patch
const copyImpls = R.compose(
  R.map(R.assoc('impls')),
  maybeEmpty,
  R.prop('impl')
);

// :: ProjectOld -> Maybe Function fn
// fn :: Project -> Project
const appendAuthor = R.compose(
  R.map(R.compose(
    R.over(R.lensProp('authors')),
    R.append
  )),
  Maybe,
  R.path(['meta', 'author'])
);

// :: Patch -> Pin[]
const getCustomPinsOnly = R.compose(
  R.reject(R.has('nodeId')),
  propValues('pins')
);

// :: PatchOld -> Function fn
// fn :: Patch -> Patch
const convertPatchPins = R.compose(
  R.flip(reduceChainOver),
  R.when(
    Maybe.isNothing,
    R.always([R.identity])
  ),
  R.chain(R.compose(
    R.map(R.compose(
      R.chain(Patch.assocPin),
      R.converge(
        Pin.createPin,
        [
          R.prop('key'),
          R.compose(convertPinType, R.prop('type')),
          R.prop('direction'),
        ]
      )
    )),
    R.values
  )),
  maybeEmpty,
  getCustomPinsOnly
);

// :: NodeIdMap -> ProjectOld -> Function fn
// fn :: Project -> Project
const convertPatches = R.curry((nodeIdMap, projectOld) =>
  R.compose(
    composeA,
    R.map(oldPatch => Maybe.of(Patch.createPatch())
      .chain(apOrSkip(addLabel(oldPatch)))
      .map(convertPatchPins(oldPatch))
      .map(convertNodes(nodeIdMap, oldPatch))
      .chain(apOrSkip(copyImpls(oldPatch)))
      .chain(assocPatchUnsafe(oldPatch.id))
    ),
    R.values,
    mergePatchesAndNodeTypes
  )(projectOld)
);

/**
 * Transforms old project bundle (v1) into new (v2)
 * @param {object} bundle
 * @returns {Project}
 */
export const toV2 = (bundle) => {
  const nodeIdMap = {};

  return Maybe.of(Project.createProject())
    .chain(apOrSkip(appendAuthor(bundle)))
    .map(convertPatches(nodeIdMap, bundle))
    .map(convertLinksInPatches(nodeIdMap, bundle))
    .chain(R.identity);
};
<commit_msg>refactor(xod-project): get rid of side effects with wrapping data into Tuple monad<commit_after>import R from 'ramda';
import { Tuple, Maybe } from 'ramda-fantasy';

import * as Project from './project';
import * as Patch from './patch';
import * as Node from './node';
import * as Link from './link';
import * as Pin from './pin';

// ================================================
//
// Utility functions
//
// ================================================

// :: Maybe Function -> a -> Maybe a
const apOrSkip = R.curry((maybeFn, val) => {
  const maybeVal = Maybe(val);
  const result = maybeFn.ap(maybeVal);
  return Maybe.isJust(result) ? result : maybeVal;
});
// :: a -> Maybe a
const maybeEmpty = R.ifElse(
  R.isEmpty,
  Maybe.Nothing,
  Maybe.of
);

// :: [Function fn] -> a -> a
// fn :: a -> Applicative a
// For example,
//   const t = key => R.compose(Maybe, R.assoc(key, true));
//   reduceChainOver([t('a'), t('b')], {});
//   { 'a': true, 'b': true }
const reduceChainOver = R.curry((fnList, overObj) =>
  R.compose(
    R.unnest,
    R.reduce(R.flip(R.chain), Maybe.of(overObj))
  )(fnList)
);

// :: String -> Object -> [a]
const propValues = key => R.compose(R.values, R.propOr({}, key));

// :: [Function] -> Function fn
// fn :: a -> a
const composeA = R.ifElse(
  R.isEmpty,
  R.always(R.identity),
  R.apply(R.compose)
);

// :: Tuple a (b -> b) -> Tuple a b
const composeT = R.curry((tuple, b) => tuple.map(fn => fn(b)));

// ================================================
//
// Getters from old bundle
//
// ================================================

// :: Project -> [Patch ^ NodeType]
export const mergePatchesAndNodeTypes = R.converge(
  R.mergeWith(R.merge),
  [
    R.prop('patches'),
    R.prop('nodeTypes'),
  ]
);

// :: {} -> String
const getLabel = R.prop('label');
// :: {} -> [a]
const getNodes = propValues('nodes');
// :: {} -> [a]
const getLinks = propValues('links');

// ================================================
//
// Conversion functions
//
// ================================================

// :: Path -> Patch -> Project -> Project
const assocPatchUnsafe = R.curry(
  (path, patch, project) => R.assocPath(['patches', path], patch, project)
);

// :: Patch -> Maybe fn
// fn :: Patch -> Patch
const addLabel = oldPatch => Maybe(getLabel(oldPatch)).map(Patch.setPatchLabel);

// :: String -> String
export const convertPinType = R.cond([
  [R.equals('bool'), R.always('boolean')],
  [R.T, R.identity],
]);

// :: NodeIdMap -> String -> String
const getLinkNodeId = R.flip(R.prop);

// :: NodeIdMap -> String -> String
const getLinkPin = R.ifElse(
  R.flip(R.has),
  getLinkNodeId,
  R.nthArg(1)
);

// :: NodeOld -> Function fn
// fn :: Node -> Node
const convertNodePins = R.compose(
  composeA,
  R.values,
  R.mapObjIndexed((pin, key) => {
    const setCurryPin = Maybe(pin.injected).map(Node.curryPin(key));
    const setPinValue = Maybe(pin.value).map(Node.setPinCurriedValue(key));
    return R.compose(
      R.chain(R.identity),
      R.chain(apOrSkip(setPinValue)),
      apOrSkip(setCurryPin)
    );
  }),
  R.prop('pins')
);

const mapNodeId = R.curry((oldNode, newNode) => ({ [oldNode.id]: newNode.id }));

// :: PatchOld -> { NodeIdMap, Function fn }
// fn :: Patch -> Tuple NodeIdMap Patch
const convertNodes = R.compose(
  composeT,
  R.converge(
    Tuple,
    [
      R.compose(R.mergeAll, R.pluck('nodeIdMap')),
      R.compose(composeA, R.pluck('node')),
    ]
  ),
  R.map(oldNode => Node.createNode(oldNode.position, oldNode.typeId)
    .map(node => ({ nodeIdMap: mapNodeId(oldNode, node), node }))
    .map(R.evolve({ node: convertNodePins(oldNode) }))
    .chain(R.evolve({ node: Patch.assocNode }))
  ),
  getNodes
);

// :: NodeIdMap -> PatchOld -> Function fn
// fn :: Patch -> Patch
const convertLinks = nodeIdMap => R.compose(
  R.map(R.compose(
    Patch.assocLink,
    R.converge(
      Link.createLink,
      [
        R.compose(getLinkPin(nodeIdMap), R.prop('pinKey'), R.head),
        R.compose(getLinkNodeId(nodeIdMap), R.prop('nodeId'), R.head),
        R.compose(getLinkPin(nodeIdMap), R.prop('pinKey'), R.last),
        R.compose(getLinkNodeId(nodeIdMap), R.prop('nodeId'), R.last),
      ]
    ),
    R.prop('pins')
  )),
  getLinks
);

// :: Project -> PatchOld -> Patch
const getPatchByOldPatch = project => R.compose(
  R.unnest,
  Project.getPatchByPath(R.__, project),
  R.prop('id')
);

// :: Tuple NodeIdMap Project -> [PatchOld] -> [Patch]
const getPatchesWithLinks = R.compose(
  R.map,
  R.converge(reduceChainOver),
  R.juxt([
    R.compose(convertLinks, Tuple.fst),
    R.compose(getPatchByOldPatch, Tuple.snd),
  ])
);

// :: ProjectOld -> Tuple NodeIdMap Project -> Project
const convertLinksInPatches = R.curry(
  (projectOld, tuple) => R.compose(
      reduceChainOver(R.__, Tuple.snd(tuple)),
      R.map(R.apply(Project.assocPatch)),
      R.converge(
        R.zip,
        [
          R.map(R.prop('id')),
          getPatchesWithLinks(tuple),
        ]
      ),
      R.values,
      mergePatchesAndNodeTypes
    )(projectOld)
);

// :: PatchOld -> Function fn
// fn :: Patch -> Patch
const copyImpls = R.compose(
  R.map(R.assoc('impls')),
  maybeEmpty,
  R.prop('impl')
);

// :: ProjectOld -> Maybe Function fn
// fn :: Project -> Project
const appendAuthor = R.compose(
  R.map(R.compose(
    R.over(R.lensProp('authors')),
    R.append
  )),
  Maybe,
  R.path(['meta', 'author'])
);

// :: Patch -> Pin[]
const getCustomPinsOnly = R.compose(
  R.reject(R.has('nodeId')),
  propValues('pins')
);

// :: PatchOld -> Function fn
// fn :: Patch -> Patch
const convertPatchPins = R.curry((patchOld, patch) => R.compose(
  R.ifElse(
    Maybe.isNothing,
    R.nthArg(1), // TODO: Why we get second argument here, not just Nothing?
    reduceChainOver
  ),
  R.chain(R.compose(
    R.map(R.compose(
      R.chain(Patch.assocPin),
      R.converge(
        Pin.createPin,
        [
          R.prop('key'),
          R.compose(convertPinType, R.prop('type')),
          R.prop('direction'),
        ]
      )
    )),
    R.values
  )),
  maybeEmpty,
  getCustomPinsOnly
)(patchOld)(patch));

// :: ProjectOld -> Function fn
// fn :: Project -> Tuple NodeIdMap Project
const convertPatches = R.compose(
  composeT,
  R.converge(
    Tuple,
    [
      R.compose(R.mergeAll, R.pluck(0)),
      R.compose(composeA, R.pluck(1)),
    ]
  ),
  R.map(oldPatch => Maybe.of(Patch.createPatch())
    .chain(apOrSkip(addLabel(oldPatch)))
    .chain(apOrSkip(copyImpls(oldPatch)))
    .map(convertPatchPins(oldPatch))
    .chain(convertNodes(oldPatch))
    .map(assocPatchUnsafe(oldPatch.id))
  ),
  R.values,
  mergePatchesAndNodeTypes
);

/**
 * Transforms old project bundle (v1) into new (v2)
 * @param {object} bundle
 * @returns {Project}
 */
export const toV2 = (bundle) =>
  Maybe.of(Project.createProject())
    .chain(apOrSkip(appendAuthor(bundle)))
    .map(convertPatches(bundle))
    .map(convertLinksInPatches(bundle))
    .chain(R.identity);
<|endoftext|>


<commit_before>import Head from 'next/head';
import { makeStyles } from '@material-ui/core/styles';
import { c } from 'ttag';
import cx from 'clsx';
import Link from 'next/link';

import AppLayout from 'components/AppLayout';
import { TutorialHeader, LineTutorial } from 'components/Tutorial';

import withData from 'lib/apollo';

import immediateImg from 'components/Tutorial/images/immediate.png';
import freedomImg from 'components/Tutorial/images/freedom.png';
import mediaImg from 'components/Tutorial/images/media.png';
import discussImg from 'components/Tutorial/images/discuss.png';
import fakeMessageImg from 'components/Tutorial/images/fakeMessage.png';
import parentImg from 'components/Tutorial/images/parent.png';
import researchImg from 'components/Tutorial/images/research.png';

const useStyles = makeStyles(theme => ({
  root: {
    '& h3': {
      fontSize: 34,
      fontWeight: 500,
      lineHeight: 1.45,
      letterSpacing: 0.25,
      textAlign: 'center',
      margin: 0,

      [theme.breakpoints.down('md')]: {
        fontSize: 18,
        letterSpacing: 0.15,
      },
    },
    '& p': {
      fontSize: 18,
      lineHeight: 1.56,
      letterSpacing: 0.5,
      maxWidth: 870,
      margin: '0 auto',

      [theme.breakpoints.down('md')]: {
        fontSize: 12,
        letterSpacing: 0.25,
      },

      '&:not(:last-child)': {
        marginBottom: 36,

        [theme.breakpoints.down('md')]: {
          marginBottom: 24,
        },
      },
    },
  },
  row: {
    display: 'flex',
    width: '100%',
    maxWidth: 1024,
    margin: '0 auto',
  },
  col: {
    display: 'flex',
    width: '100%',
    flexDirection: 'column',
    alignItems: 'center',
  },
  colImage: {
    width: '100%',
  },
  colTitle: {
    fontSize: 24,
    lineHeight: 1.45,
    marginBottom: 16,

    [theme.breakpoints.down('md')]: {
      fontSize: 14,
      marginBottom: 10,
    },
  },
  colContent: {
    fontSize: 18,
    lineHeight: 1.55,
    letterSpacing: 0.5,
    whiteSpace: 'pre-line',

    [theme.breakpoints.down('md')]: {
      fontSize: 10,
      letterSpacing: 0.25,
    },
  },
  info: {
    padding: '0 40px',
    margin: '56px auto 0',

    [theme.breakpoints.down('md')]: {
      margin: '24px auto 0',
    },
  },
  how: {
    paddingTop: 40,

    '& > h3': {
      marginBottom: 32,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },

    [theme.breakpoints.down('md')]: {
      paddingTop: 24,
      paddingBottom: 20,
    },
  },
  when: {
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
    margin: '50px 0 45px',

    [theme.breakpoints.down('md')]: {
      maxWidth: 600,
      margin: '36px auto 38px',
    },

    '&::before': {
      background: 'white',
    },

    '& > h3': {
      marginBottom: 54,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },
  },
  whenItem: {
    padding: '0 45px',

    [theme.breakpoints.down('md')]: {
      padding: '0',
    },
  },
  whenItemTitle: {
    textAlign: 'center',
  },
  howToUseButton: {
    fontSize: 24,
    fontWeight: 500,
    lineHeight: 1,
    color: 'white',
    background: theme.palette.primary.main,
    borderRadius: 65,
    textDecoration: 'none',
    padding: '12px 48px',
    margin: '25px auto 0',

    [theme.breakpoints.down('md')]: {
      fontSize: 18,
      padding: '10px 32px',
      margin: '18px auto 0',
    },
  },
  coreValue: {
    padding: '50px 20px 68px',
    background: theme.palette.secondary[800],

    [theme.breakpoints.down('md')]: {
      padding: '36px 9px 50px',
    },

    '& > h3': {
      color: 'white',
      marginBottom: 20,

      [theme.breakpoints.down('md')]: {
        marginBottom: 14,
      },
    },
  },
  coreValueItem: {
    padding: '0 36px',
    color: 'white',

    [theme.breakpoints.down('md')]: {
      padding: '0 9px',
    },
  },
  birth: {
    marginTop: 50,
    padding: '0 40px',

    [theme.breakpoints.down('md')]: {
      marginTop: 36,
    },

    '& > h3': {
      marginBottom: 36,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },
  },
  whoWeAre: {
    marginTop: 50,
    marginBottom: 70,
    padding: '0 40px',

    [theme.breakpoints.down('md')]: {
      marginTop: 36,
      marginBottom: 40,
    },

    '& > h3': {
      marginBottom: 36,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },
  },
}));

const TutorialPage = () => {
  const classes = useStyles();

  return (
    <AppLayout container={false}>

        <title>{c('tutorial').t`what is cofacts`}</title>

        <TutorialHeader />

          {/* TODO: translate */}

            Cofacts 真的假的
            是一個透過群眾協作以及聊天機器人來做事實查核的可疑訊息查證平台，
            讓每一個不確定真假的訊息，都能透過群眾的共同努力，被好好檢視跟查核討論。
            我們透過聊天機器人能 24 小時，不間斷地回應使用者疑惑的可疑訊息；
            另一方面，我們的網站平台呈現所有被收錄的訊息，
            每一位無私貢獻的志工都能查核表達自己的看法。

            Cofacts 是一個公民發起的平台，不隸屬於任何政黨或是政治人物，
            成果開放由所有人一起共享，讓所有人一起努力，貢獻各自的技能，
            讓機器人可以為您查證不實訊息的方法。

          {/* TODO: translate */}

可疑訊息怎麼被查核的？

          <LineTutorial />

          {/* TODO: translate */}

Cofacts 在什麼時候能幫上忙？

                {`想知道看起來很可疑的 \n 訊息是真是假？`}

                想做不實訊息的研究？

                {`想幫助不會查證訊息的 \n 長輩或朋友？`}

                {`想看看最近大家都在 \n 討論什麼話題？`}

          <Link href="/how-to-use">

              快來了解 Cofacts 怎麼使用吧

          </Link>

          {/* TODO: translate */}

Cofacts 的核心價值是？

即時工具

                提供方便工具幫助第一線查核可疑訊息，幫助相關媒體、教育人士，專注發揮所長，識別假訊息讓謠言止於智者。

自由開放

                從平台的每一行程式碼，到查核回復都是公開平等給大眾，每一個人都可以貢獻，每一個人的查核意見也不受它限制。

媒體識讀

                帶動媒體識讀教育與事實查核的能力，鼓勵人人做研究，一同討論各方意見，正面面對不實訊息。

          {/* TODO: translate */}

Cofacts 怎麼誕生的？

            這個專案最核心的想法，就是希望讓「不同的聲音」更容易被看見。
            網路謠言相對應的闢謠文章、以及個人意見與相對應得「回覆」文，都是一種「不同的聲音」。
            一個人會去相信網路謠言，或許是因為他沒有接觸過不同的聲音，或不願意去接觸不同的聲音。
            只是，即使一個人樂意接觸不同的聲音，其實門檻還是很高的。

            對於已經習慣於查證網路消息的人們而言。若在收到每一種轉傳來的 LINE
            訊息之後，都要自己花時間一筆一筆查證，會非常地花時間。
            協作型闢謠資料庫就像是個共筆，大家查到什麼就寫成回應放進去，
            之後收到同一訊息的人如果也想要查證，就不用從零開始。
            因此，對於已經習慣也願意查證、擁抱「不同的聲音」的人，
            協作型闢謠資料庫能幫這些人少走很多冤枉路。

          {/* TODO: translate */}

我們是誰？

            Cofacts 是台灣公民科技社群 g0v
            的其中一個專案，鼓勵開放原始碼、群眾協作。
            團隊成員，包含發起人在內，都是在正職工作之餘參加這個專案，
            憑藉著大家自主熱心的貢獻，有人寫文案、有人寫程式、
            有人查證謠言、有人製圖設計，慢慢開發出的成果。
            每個人都是自發性地盡一己之力，貢獻到公眾領域。

    </AppLayout>
  );
};

export default withData(TutorialPage);
<commit_msg>[Tutorial] revise background of what-is-cofacts page<commit_after>import Head from 'next/head';
import { makeStyles } from '@material-ui/core/styles';
import { c } from 'ttag';
import cx from 'clsx';
import Link from 'next/link';

import AppLayout from 'components/AppLayout';
import { TutorialHeader, LineTutorial } from 'components/Tutorial';

import withData from 'lib/apollo';

import immediateImg from 'components/Tutorial/images/immediate.png';
import freedomImg from 'components/Tutorial/images/freedom.png';
import mediaImg from 'components/Tutorial/images/media.png';
import discussImg from 'components/Tutorial/images/discuss.png';
import fakeMessageImg from 'components/Tutorial/images/fakeMessage.png';
import parentImg from 'components/Tutorial/images/parent.png';
import researchImg from 'components/Tutorial/images/research.png';

const useStyles = makeStyles(theme => ({
  root: {
    '& h3': {
      fontSize: 34,
      fontWeight: 500,
      lineHeight: 1.45,
      letterSpacing: 0.25,
      textAlign: 'center',
      margin: 0,

      [theme.breakpoints.down('md')]: {
        fontSize: 18,
        letterSpacing: 0.15,
      },
    },
    '& p': {
      fontSize: 18,
      lineHeight: 1.56,
      letterSpacing: 0.5,
      maxWidth: 870,
      margin: '0 auto',

      [theme.breakpoints.down('md')]: {
        fontSize: 12,
        letterSpacing: 0.25,
      },

      '&:not(:last-child)': {
        marginBottom: 36,

        [theme.breakpoints.down('md')]: {
          marginBottom: 24,
        },
      },
    },
  },
  row: {
    display: 'flex',
    width: '100%',
    maxWidth: 1024,
    margin: '0 auto',
  },
  col: {
    display: 'flex',
    width: '100%',
    flexDirection: 'column',
    alignItems: 'center',
  },
  colImage: {
    width: '100%',
  },
  colTitle: {
    fontSize: 24,
    lineHeight: 1.45,
    marginBottom: 16,

    [theme.breakpoints.down('md')]: {
      fontSize: 14,
      marginBottom: 10,
    },
  },
  colContent: {
    fontSize: 18,
    lineHeight: 1.55,
    letterSpacing: 0.5,
    whiteSpace: 'pre-line',

    [theme.breakpoints.down('md')]: {
      fontSize: 10,
      letterSpacing: 0.25,
    },
  },
  info: {
    padding: '0 40px',
    margin: '56px auto 0',

    [theme.breakpoints.down('md')]: {
      margin: '24px auto 0',
    },
  },
  how: {
    paddingTop: 40,

    '& > h3': {
      marginBottom: 32,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },

    [theme.breakpoints.down('md')]: {
      paddingTop: 24,
    },

    [theme.breakpoints.down('sm')]: {
      paddingBottom: 20,
    },
  },
  when: {
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'center',
    padding: '50px 0 45px',
    background: 'white',

    '& > h3': {
      marginBottom: 54,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },

    '& > div': {
      [theme.breakpoints.down('md')]: {
        maxWidth: 600,
        margin: '0 auto',
      },
    },
  },
  whenItem: {
    padding: '0 45px',

    [theme.breakpoints.down('md')]: {
      padding: '0',
    },
  },
  whenItemTitle: {
    textAlign: 'center',
  },
  howToUseButton: {
    fontSize: 24,
    fontWeight: 500,
    lineHeight: 1,
    color: 'white',
    background: theme.palette.primary.main,
    borderRadius: 65,
    textDecoration: 'none',
    padding: '12px 48px',
    margin: '25px auto 0',

    [theme.breakpoints.down('md')]: {
      fontSize: 18,
      padding: '10px 32px',
      margin: '18px auto 0',
    },
  },
  coreValue: {
    padding: '50px 20px 68px',
    background: theme.palette.secondary[800],

    [theme.breakpoints.down('md')]: {
      padding: '36px 9px 50px',
    },

    '& > h3': {
      color: 'white',
      marginBottom: 20,

      [theme.breakpoints.down('md')]: {
        marginBottom: 14,
      },
    },
  },
  coreValueItem: {
    padding: '0 36px',
    color: 'white',

    [theme.breakpoints.down('md')]: {
      padding: '0 9px',
    },
  },
  birth: {
    marginTop: 50,
    padding: '0 40px',

    [theme.breakpoints.down('md')]: {
      marginTop: 36,
    },

    '& > h3': {
      marginBottom: 36,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },
  },
  whoWeAre: {
    marginTop: 50,
    marginBottom: 70,
    padding: '0 40px',

    [theme.breakpoints.down('md')]: {
      marginTop: 36,
      marginBottom: 40,
    },

    '& > h3': {
      marginBottom: 36,

      [theme.breakpoints.down('md')]: {
        marginBottom: 24,
      },
    },
  },
}));

const TutorialPage = () => {
  const classes = useStyles();

  return (
    <AppLayout container={false}>

        <title>{c('tutorial').t`what is cofacts`}</title>

        <TutorialHeader />

          {/* TODO: translate */}

            Cofacts 真的假的
            是一個透過群眾協作以及聊天機器人來做事實查核的可疑訊息查證平台，
            讓每一個不確定真假的訊息，都能透過群眾的共同努力，被好好檢視跟查核討論。
            我們透過聊天機器人能 24 小時，不間斷地回應使用者疑惑的可疑訊息；
            另一方面，我們的網站平台呈現所有被收錄的訊息，
            每一位無私貢獻的志工都能查核表達自己的看法。

            Cofacts 是一個公民發起的平台，不隸屬於任何政黨或是政治人物，
            成果開放由所有人一起共享，讓所有人一起努力，貢獻各自的技能，
            讓機器人可以為您查證不實訊息的方法。

          {/* TODO: translate */}

可疑訊息怎麼被查核的？

          <LineTutorial />

          {/* TODO: translate */}

Cofacts 在什麼時候能幫上忙？

                {`想知道看起來很可疑的 \n 訊息是真是假？`}

                想做不實訊息的研究？

                {`想幫助不會查證訊息的 \n 長輩或朋友？`}

                {`想看看最近大家都在 \n 討論什麼話題？`}

          <Link href="/how-to-use">

              快來了解 Cofacts 怎麼使用吧

          </Link>

          {/* TODO: translate */}

Cofacts 的核心價值是？

即時工具

                提供方便工具幫助第一線查核可疑訊息，幫助相關媒體、教育人士，專注發揮所長，識別假訊息讓謠言止於智者。

自由開放

                從平台的每一行程式碼，到查核回復都是公開平等給大眾，每一個人都可以貢獻，每一個人的查核意見也不受它限制。

媒體識讀

                帶動媒體識讀教育與事實查核的能力，鼓勵人人做研究，一同討論各方意見，正面面對不實訊息。

          {/* TODO: translate */}

Cofacts 怎麼誕生的？

            這個專案最核心的想法，就是希望讓「不同的聲音」更容易被看見。
            網路謠言相對應的闢謠文章、以及個人意見與相對應得「回覆」文，都是一種「不同的聲音」。
            一個人會去相信網路謠言，或許是因為他沒有接觸過不同的聲音，或不願意去接觸不同的聲音。
            只是，即使一個人樂意接觸不同的聲音，其實門檻還是很高的。

            對於已經習慣於查證網路消息的人們而言。若在收到每一種轉傳來的 LINE
            訊息之後，都要自己花時間一筆一筆查證，會非常地花時間。
            協作型闢謠資料庫就像是個共筆，大家查到什麼就寫成回應放進去，
            之後收到同一訊息的人如果也想要查證，就不用從零開始。
            因此，對於已經習慣也願意查證、擁抱「不同的聲音」的人，
            協作型闢謠資料庫能幫這些人少走很多冤枉路。

          {/* TODO: translate */}

我們是誰？

            Cofacts 是台灣公民科技社群 g0v
            的其中一個專案，鼓勵開放原始碼、群眾協作。
            團隊成員，包含發起人在內，都是在正職工作之餘參加這個專案，
            憑藉著大家自主熱心的貢獻，有人寫文案、有人寫程式、
            有人查證謠言、有人製圖設計，慢慢開發出的成果。
            每個人都是自發性地盡一己之力，貢獻到公眾領域。

    </AppLayout>
  );
};

export default withData(TutorialPage);
<|endoftext|>


<commit_before>const { platform } = require("os");
const { format } = require("url");
const { BrowserWindow } = require("electron");
const isDev = require("electron-is-dev");
const { resolve } = require("app-root-path");
const windowStateKeeper = require("electron-window-state");
const touchBar = require("./../touchbar");

const isWindows = platform() === "win32";

const createMainWindow = () => {
  const windowState = windowStateKeeper({
    defaultWidth: 360,
    defaultHeight: 440,
  });

  const mainWindow = new BrowserWindow({
    width: windowState.width,
    height: windowState.height,
    x: windowState.x,
    y: windowState.y,
    title: "Caption",
    minWidth: 300,
    minHeight: 300,
    vibrancy: "sidebar",
    titleBarStyle: "hidden-inset",
    show: false,
    center: true,
    autoHideMenuBar: true,
    acceptFirstMouse: false,
    opacity: isWindows ? 0.8 : 1,
    transparent: isWindows,
    webPreferences: {
      backgroundThrottling: false,
      webSecurity: false,
    },
  });

  windowState.manage(mainWindow);

  const devPath = "http://localhost:8000/start";
  const prodPath = format({
    pathname: resolve("renderer/out/start/index.html"),
    protocol: "file:",
    slashes: true,
  });
  const url = isDev ? devPath : prodPath;
  mainWindow.loadURL(url);

  mainWindow.webContents.on("did-finish-load", () => {
    mainWindow.show();
    mainWindow.focus();
  });

  // Add Touchbar support for MacOS
  if (process.platform === "darwin") {
    mainWindow.setTouchBar(touchBar);
  }

  return mainWindow;
};

module.exports = { createMainWindow };
<commit_msg>Accept first mouse again<commit_after>const { platform } = require("os");
const { format } = require("url");
const { BrowserWindow } = require("electron");
const isDev = require("electron-is-dev");
const { resolve } = require("app-root-path");
const windowStateKeeper = require("electron-window-state");
const touchBar = require("./../touchbar");

const isWindows = platform() === "win32";

const createMainWindow = () => {
  const windowState = windowStateKeeper({
    defaultWidth: 360,
    defaultHeight: 440,
  });

  const mainWindow = new BrowserWindow({
    width: windowState.width,
    height: windowState.height,
    x: windowState.x,
    y: windowState.y,
    title: "Caption",
    minWidth: 300,
    minHeight: 300,
    vibrancy: "sidebar",
    titleBarStyle: "hidden-inset",
    show: false,
    center: true,
    autoHideMenuBar: true,
    acceptFirstMouse: true,
    opacity: isWindows ? 0.8 : 1,
    transparent: isWindows,
    webPreferences: {
      backgroundThrottling: false,
      webSecurity: false,
    },
  });

  windowState.manage(mainWindow);

  const devPath = "http://localhost:8000/start";
  const prodPath = format({
    pathname: resolve("renderer/out/start/index.html"),
    protocol: "file:",
    slashes: true,
  });
  const url = isDev ? devPath : prodPath;
  mainWindow.loadURL(url);

  mainWindow.webContents.on("did-finish-load", () => {
    mainWindow.show();
    mainWindow.focus();
  });

  // Add Touchbar support for MacOS
  if (process.platform === "darwin") {
    mainWindow.setTouchBar(touchBar);
  }

  return mainWindow;
};

module.exports = { createMainWindow };
<|endoftext|>


<commit_before>// ==UserScript==
// @name        Spade World Boundary
// @description This adds a world boundary overlay to the Overviewer map of the Spade server.
// @namespace   http://ermarian.net/resources/minecraft/spade/
// @include     http://te.calref.net/~minecraft/spade/
// @version     1
// @grant       none
// ==/UserScript==

circle = function (point, r, tileSet, opts, vertices) {
  opts.paths = [];
  for (var i = 0; i <= vertices; i++) {
    var angle = i / vertices * 2 * Math.PI;
    console.log(point[0] + r*Math.cos(angle), point[1], point[2] + r*Math.cos(angle));
    var p = overviewer.util.fromWorldToLatLng(
      point[0] + r*Math.cos(angle), point[1], point[2] + r*Math.cos(angle), tileSet
    );
    opts.paths.push(p);
  }
  return new google.maps.Polygon(opts);
}

overviewer.views.WorldBoundaryView = Backbone.View.extend({
  render: function () {
    if (overviewer.collections.spawnMarker) {
      var curTileSet = overviewer.mapView.options.currentTileSet;
      var spawn = curTileSet.get("spawn");
      var c = circle(spawn, 1500, curTileSet, {
        'map' : overviewer.map,
        strokeColor: '#FF0000',
        strokeWeight: 1,
        strokeOpacity: 0.5,
        fillColor: '#00FF00',
        fillOpacity: 0.05
      }, 100);
      console.log(c);
    }
  }
});

overviewer.util.ready(function() {
    var boundary = new overviewer.views.WorldBoundaryView();
    boundary.render();
});

<commit_msg>learn to trigonometry.<commit_after>// ==UserScript==
// @name        Spade World Boundary
// @description This adds a world boundary overlay to the Overviewer map of the Spade server.
// @namespace   http://ermarian.net/resources/minecraft/spade/
// @include     http://te.calref.net/~minecraft/spade/
// @version     1
// @grant       none
// ==/UserScript==

circle = function (point, r, tileSet, opts, vertices) {
  opts.paths = [];
  for (var i = 0; i <= vertices; i++) {
    var angle = i / vertices * 2 * Math.PI;
    var p = overviewer.util.fromWorldToLatLng(point[0] + r * Math.cos(angle), point[1], point[2] + r * Math.sin(angle), tileSet);
    opts.paths.push(p);
  }
  return new google.maps.Polygon(opts);
}

overviewer.views.WorldBoundaryView = Backbone.View.extend({
  render: function () {
    if (overviewer.collections.spawnMarker) {
      var curTileSet = overviewer.mapView.options.currentTileSet;
      var spawn = curTileSet.get("spawn");
      var c = circle(spawn, 1500, curTileSet, {
        'map' : overviewer.map,
        strokeColor: '#FF0000',
        strokeWeight: 1,
        strokeOpacity: 0.5,
        fillColor: '#00FF00',
        fillOpacity: 0.05
      }, 100);
      console.log(c);
    }
  }
});

overviewer.util.ready(function() {
    var boundary = new overviewer.views.WorldBoundaryView();
    boundary.render();
});

<|endoftext|>


<commit_before>/* ========================================================================
 * Bootstrap: modal.js v3.2.0
 * http://getbootstrap.com/javascript/#modals
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // MODAL CLASS DEFINITION
  // ======================

  var Modal = function (element, options) {
    this.options        = options
    this.$body          = $(document.body)
    this.$element       = $(element)
    this.$backdrop      =
    this.isShown        = null
    this.scrollbarWidth = 0

    if (this.options.remote) {
      this.$element
        .find('.modal-content')
        .load(this.options.remote, $.proxy(function () {
          this.$element.trigger('loaded.bs.modal')
        }, this))
    }
  }

  Modal.VERSION  = '3.2.0'

  Modal.DEFAULTS = {
    backdrop: true,
    keyboard: true,
    show: true
  }

  Modal.prototype.toggle = function (_relatedTarget) {
    return this.isShown ? this.hide() : this.show(_relatedTarget)
  }

  Modal.prototype.show = function (_relatedTarget) {
    var that = this
    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })

    this.$element.trigger(e)

    if (this.isShown || e.isDefaultPrevented()) return

    this.isShown = true

    this.checkScrollbar()
    this.$body.addClass('modal-open')

    this.setScrollbar()
    this.escape()

    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))

    this.backdrop(function () {
      var transition = $.support.transition && that.$element.hasClass('fade')

      if (!that.$element.parent().length) {
        that.$element.appendTo(that.$body) // don't move modals dom position
      }

      that.$element
        .show()
        .scrollTop(0)

      if (transition) {
        that.$element[0].offsetWidth // force reflow
      }

      that.$element
        .addClass('in')
        .attr('aria-hidden', false)

      that.enforceFocus()

      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })

      transition ?
        that.$element.find('.modal-dialog') // wait for modal to slide in
          .one('bsTransitionEnd', function () {
            that.$element.trigger('focus').trigger(e)
          })
          .emulateTransitionEnd(300) :
        that.$element.trigger('focus').trigger(e)
    })
  }

  Modal.prototype.hide = function (e) {
    if (e) e.preventDefault()

    e = $.Event('hide.bs.modal')

    this.$element.trigger(e)

    if (!this.isShown || e.isDefaultPrevented()) return

    this.isShown = false

    this.$body.removeClass('modal-open')

    this.resetScrollbar()
    this.escape()

    $(document).off('focusin.bs.modal')

    this.$element
      .removeClass('in')
      .attr('aria-hidden', true)
      .off('click.dismiss.bs.modal')

    $.support.transition && this.$element.hasClass('fade') ?
      this.$element
        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
        .emulateTransitionEnd(300) :
      this.hideModal()
  }

  Modal.prototype.enforceFocus = function () {
    $(document)
      .off('focusin.bs.modal') // guard against infinite focus loop
      .on('focusin.bs.modal', $.proxy(function (e) {
        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
          this.$element.trigger('focus')
        }
      }, this))
  }

  Modal.prototype.escape = function () {
    if (this.isShown && this.options.keyboard) {
      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
        e.which == 27 && this.hide()
      }, this))
    } else if (!this.isShown) {
      this.$element.off('keyup.dismiss.bs.modal')
    }
  }

  Modal.prototype.hideModal = function () {
    var that = this
    this.$element.hide()
    this.backdrop(function () {
      that.$element.trigger('hidden.bs.modal')
    })
  }

  Modal.prototype.removeBackdrop = function () {
    this.$backdrop && this.$backdrop.remove()
    this.$backdrop = null
  }

  Modal.prototype.backdrop = function (callback) {
    var that = this
    var animate = this.$element.hasClass('fade') ? 'fade' : ''

    if (this.isShown && this.options.backdrop) {
      var doAnimate = $.support.transition && animate

      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
        .appendTo(this.$body)

      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
        if (e.target !== e.currentTarget) return
        this.options.backdrop == 'static'
          ? this.$element[0].focus.call(this.$element[0])
          : this.hide.call(this)
      }, this))

      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow

      this.$backdrop.addClass('in')

      if (!callback) return

      doAnimate ?
        this.$backdrop
          .one('bsTransitionEnd', callback)
          .emulateTransitionEnd(150) :
        callback()

    } else if (!this.isShown && this.$backdrop) {
      this.$backdrop.removeClass('in')

      var callbackRemove = function () {
        that.removeBackdrop()
        callback && callback()
      }
      $.support.transition && this.$element.hasClass('fade') ?
        this.$backdrop
          .one('bsTransitionEnd', callbackRemove)
          .emulateTransitionEnd(150) :
        callbackRemove()

    } else if (callback) {
      callback()
    }
  }

  Modal.prototype.checkScrollbar = function () {
    if (document.body.clientWidth >= window.innerWidth) return
    this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar()
  }

  Modal.prototype.setScrollbar = function () {
    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
    if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
  }

  Modal.prototype.resetScrollbar = function () {
    this.$body.css('padding-right', '')
  }

  Modal.prototype.measureScrollbar = function () { // thx walsh
    var scrollDiv = document.createElement('div')
    scrollDiv.className = 'modal-scrollbar-measure'
    this.$body.append(scrollDiv)
    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
    this.$body[0].removeChild(scrollDiv)
    return scrollbarWidth
  }

  // MODAL PLUGIN DEFINITION
  // =======================

  function Plugin(option, _relatedTarget) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.modal')
      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)

      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
      if (typeof option == 'string') data[option](_relatedTarget)
      else if (options.show) data.show(_relatedTarget)
    })
  }

  var old = $.fn.modal

  $.fn.modal             = Plugin
  $.fn.modal.Constructor = Modal

  // MODAL NO CONFLICT
  // =================

  $.fn.modal.noConflict = function () {
    $.fn.modal = old
    return this
  }

  // MODAL DATA-API
  // ==============

  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
    var $this   = $(this)
    var href    = $this.attr('href')
    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())

    if ($this.is('a')) e.preventDefault()

    $target.one('show.bs.modal', function (showEvent) {
      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
      $target.one('hidden.bs.modal', function () {
        $this.is(':visible') && $this.trigger('focus')
      })
    })
    Plugin.call($target, option, this)
  })

}(jQuery);
<commit_msg>Remove modal.js<commit_after><|endoftext|>


<commit_before><commit_msg>Reset index on flush<commit_after><|endoftext|>


<commit_before>'use strict';

function ZZTWorld() {}
function ZZTBoard() {}

function ZZTWorldLoader()
{
}

ZZTWorldLoader.prototype.init = function(url, callback)
{
   var self = this;
   var request = new XMLHttpRequest();
   request.open("GET", url, true);
   request.responseType = 'arraybuffer';
   request.onload = function(e)
   {
      var world = null;
      if (this.status == 200)
      {
         var stream = new ZZTFileStream(this.response);
         world = self.parseWorldData(stream);
      }
      callback(world);
   }
   request.send();
}

ZZTWorldLoader.prototype.parseWorldData = function(stream)
{
   var world = new ZZTWorld();

   world.worldType = stream.getInt16();
   world.numBoards = stream.getInt16();
   world.playerAmmo = stream.getInt16();
   world.playerGems = stream.getInt16();
   world.playerKeys = new Array(7);
   for (var i = 0; i < 7; ++i)
      world.playerKeys[i] = stream.getBoolean();
   world.playerHealth = stream.getInt16();
   world.playerBoard = stream.getInt16();

   world.playerTorches = stream.getInt16();
   world.torchCycles = stream.getInt16();
   world.energyCycles = stream.getInt16();
   stream.position += 2; /* unused */
   world.playerScore = stream.getInt16();

   world.worldName = stream.getFixedPascalString(20);
   world.flag = new Array(10);
   for (var i = 0; i < 10; ++i)
      world.flag[i] = stream.getFixedPascalString(20);

   world.timeLeft = stream.getInt16();
   stream.position += 2; /* playerdata pointer */
   world.locked = stream.getBoolean();
   world.board = [];

   /* board information then starts at offset 512 */
   stream.position = 512;

   for (var i = 0; i < world.numBoards; ++i)
      world.board.push(this.parseZZTBoard(stream));

   return world;
}

ZZTWorldLoader.prototype.parseZZTBoard = function(stream)
{
   var boardOffset = stream.position;
   var boardSize = stream.getInt16();

   var board = new ZZTBoard;
   board.name = stream.getFixedPascalString(50);

   board.width = 60;
   board.height = 25;
   board.player = null;

   var tiles = [];
   /* what follows now is RLE data, encoding 1500 tiles */
   while (tiles.length < (board.width * board.height))
   {
      var count = stream.getUint8();
      var typeid = stream.getUint8();
      var color = stream.getUint8();

      /* A count of zero actually means 256 tiles. The built-in editor
         never encodes like this, but some other editors do. */
      if (count == 0) count = 256;

      for (var i = 0; i < count; ++i)
      {
         var tileIndex = tiles.length;
         var obj = makeBoardObject(typeid, color);

         /* TODO: should each object also maintain its own x/y? */
         /* I feel like that'll complicate things... */
         obj.x = (tileIndex % board.width);
         obj.y = Math.floor(tileIndex / board.width);
         tiles.push(obj);

         if (obj.name == "player" && board.player == null)
         {
            board.player = obj;
         }
      }
   }
   board.tiles = tiles;
   board.tilesBelow = new Array(tiles.length);

   /* following the RLE data, we then have... */
   board.maxPlayerShots = stream.getUint8();
   board.isDark = stream.getUint8();
   board.exitNorth = stream.getUint8();
   board.exitSouth = stream.getUint8();
   board.exitWest = stream.getUint8();
   board.exitEast = stream.getUint8();
   board.restartOnZap = stream.getUint8();
   board.onScreenMessage = stream.getFixedPascalString(58); /* never used? */
   board.messageTimer = 0;
   board.playerEnterX = stream.getUint8();
   board.playerEnterY = stream.getUint8();
   board.timeLimit = stream.getInt16();
   stream.position += 16; /* unused */
   var statusElementCount = stream.getInt16();

   var statusElement = [];
   for (var i = 0; i < statusElementCount; ++i)
      statusElement.push(this.parseStatusElement(stream));

   /* for objects with code pointers referring to a different object, link them. */
   for (var i = 0; i < statusElementCount; ++i)
   {
      if (statusElement[i].codeLength < 0)
         statusElement[i].code = this.statusElement[-this.statusElement[i].codeLength].code;
   }

   /* now, inject the information from the status elements into the appropriate tiles. */

   /* now turn these into objects */
   for (var i = 0; i < statusElementCount; ++i)
   {
      /* town.zzt's board 19 contains an element at (-1,-1)? */
      if (statusElement[i].y < 0 || statusElement[i].x < 0)
         continue;
      var tileIndex = statusElement[i].y * 60 + statusElement[i].x;
      var obj = board.tiles[tileIndex];

      if (obj.setParams)
         obj.setParams(statusElement[i]);

      /* handle all the 'below' layers too */
      if (status.underType)
      {
         var underObj = makeBoardObject(status.underType, status.underColor);
         underObj.x = statusElement[i].x;
         underObj.y = statusElement[i].y;
         board.tilesBelow[tileIndex] = underObj;
      }
   }

   /* jump to next board */
   stream.position = boardOffset + boardSize + 2;

   return board;
}

ZZTWorldLoader.prototype.parseStatusElement = function(stream)
{
   var status = {};

   /* x and y coordinates are 1-based for some reason */
   status.x = stream.getUint8() - 1;
   status.y = stream.getUint8() - 1;

   status.xStep = stream.getInt16();
   status.yStep = stream.getInt16();
   status.cycle = stream.getInt16();

   status.param1 = stream.getUint8();
   status.param2 = stream.getUint8();
   status.param3 = stream.getUint8();

   status.follower = stream.getInt16();
   status.leader = stream.getInt16();
   status.underType = stream.getUint8();
   status.underColor = stream.getUint8();
   stream.position += 4; /* pointer is not used when loading */
   status.currentInstruction = stream.getInt16();
   status.codeLength = stream.getInt16();

   /* for ZZT and not Super ZZT, eight bytes of padding follow */
   stream.position += 8;

   /* if status.codeLength is positive, there is that much ZZT-OOP code following */
   if (status.codeLength > 0)
   {
      status.code = stream.getFixedString(status.codeLength);
   }
   else
   {
      /* it's negative, which means that we'll need to look at a different
         object in order to use it's code instead; we'll do that later. */
      status.code = null;
   }

   return status;
}

ZZTBoard.prototype.get = function(x, y)
{
   return this.tiles[y * this.width + x];
}

ZZTBoard.prototype.set = function(x, y, obj)
{
  this.tiles[y * this.width + x] = obj;
}

ZZTBoard.prototype.update = function()
{
   var self = this;

   /* Make sure everybody's where they think they are. */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);
         if (tile.x != x || tile.y != y)
         {
            console.log("object at [" + x + ", " + y +
               "] thinks it's at [" + tile.x + ", " + y + "], fixing.");
            tile.x = x;
            tile.y = y;
         }
      }
   }

   /* Now, update them. */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);

         if (tile.update && tile.hasUpdated == false)
         {
            tile.update(this);
            tile.hasUpdated = true;
         }
      }
   }

   /* clear the update flag */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);
         tile.hasUpdated = false;
      }
   }
}

ZZTBoard.prototype.draw = function(textconsole)
{
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);

         var inf = getTileRenderInfo(tile);
         textconsole.set(x, y, inf.glyph, inf.color);
      }
   }

   if (this.messageTimer > 0)
   {
      /* TODO: actually work out how to make this multiline */
      textconsole.setString(
         Math.floor((this.width / 2) - (this.onScreenMessage.length / 2)),
         24,
         this.onScreenMessage,
         (this.messageTimer % 6) + VGA.ATTR_FG_BBLUE);
      --this.messageTimer;
   }
}

ZZTBoard.prototype.setMessage = function(msg)
{
   /* TODO: actually work out how to make this multiline */
   if (msg.length >= (this.width - 2))
   {
      msg = msg.substr(0, (this.width - 2))
   }
   this.onScreenMessage = " " + msg + " ";
   this.messageTimer = 24;
}
<commit_msg>Lines!<commit_after>'use strict';

function ZZTWorld() {}
function ZZTBoard() {}

function ZZTWorldLoader()
{
}

ZZTWorldLoader.prototype.init = function(url, callback)
{
   var self = this;
   var request = new XMLHttpRequest();
   request.open("GET", url, true);
   request.responseType = 'arraybuffer';
   request.onload = function(e)
   {
      var world = null;
      if (this.status == 200)
      {
         var stream = new ZZTFileStream(this.response);
         world = self.parseWorldData(stream);
      }
      callback(world);
   }
   request.send();
}

ZZTWorldLoader.prototype.parseWorldData = function(stream)
{
   var world = new ZZTWorld();

   world.worldType = stream.getInt16();
   world.numBoards = stream.getInt16();
   world.playerAmmo = stream.getInt16();
   world.playerGems = stream.getInt16();
   world.playerKeys = new Array(7);
   for (var i = 0; i < 7; ++i)
      world.playerKeys[i] = stream.getBoolean();
   world.playerHealth = stream.getInt16();
   world.playerBoard = stream.getInt16();

   world.playerTorches = stream.getInt16();
   world.torchCycles = stream.getInt16();
   world.energyCycles = stream.getInt16();
   stream.position += 2; /* unused */
   world.playerScore = stream.getInt16();

   world.worldName = stream.getFixedPascalString(20);
   world.flag = new Array(10);
   for (var i = 0; i < 10; ++i)
      world.flag[i] = stream.getFixedPascalString(20);

   world.timeLeft = stream.getInt16();
   stream.position += 2; /* playerdata pointer */
   world.locked = stream.getBoolean();
   world.board = [];

   /* board information then starts at offset 512 */
   stream.position = 512;

   for (var i = 0; i < world.numBoards; ++i)
      world.board.push(this.parseZZTBoard(stream));

   return world;
}

ZZTWorldLoader.prototype.parseZZTBoard = function(stream)
{
   var boardOffset = stream.position;
   var boardSize = stream.getInt16();

   var board = new ZZTBoard;
   board.name = stream.getFixedPascalString(50);

   board.width = 60;
   board.height = 25;
   board.player = null;

   var tiles = [];
   /* what follows now is RLE data, encoding 1500 tiles */
   while (tiles.length < (board.width * board.height))
   {
      var count = stream.getUint8();
      var typeid = stream.getUint8();
      var color = stream.getUint8();

      /* A count of zero actually means 256 tiles. The built-in editor
         never encodes like this, but some other editors do. */
      if (count == 0) count = 256;

      for (var i = 0; i < count; ++i)
      {
         var tileIndex = tiles.length;
         var obj = makeBoardObject(typeid, color);

         /* TODO: should each object also maintain its own x/y? */
         /* I feel like that'll complicate things... */
         obj.x = (tileIndex % board.width);
         obj.y = Math.floor(tileIndex / board.width);
         tiles.push(obj);

         if (obj.name == "player" && board.player == null)
         {
            board.player = obj;
         }
      }
   }
   board.tiles = tiles;
   board.tilesBelow = new Array(tiles.length);

   /* following the RLE data, we then have... */
   board.maxPlayerShots = stream.getUint8();
   board.isDark = stream.getUint8();
   board.exitNorth = stream.getUint8();
   board.exitSouth = stream.getUint8();
   board.exitWest = stream.getUint8();
   board.exitEast = stream.getUint8();
   board.restartOnZap = stream.getUint8();
   board.onScreenMessage = stream.getFixedPascalString(58); /* never used? */
   board.messageTimer = 0;
   board.playerEnterX = stream.getUint8();
   board.playerEnterY = stream.getUint8();
   board.timeLimit = stream.getInt16();
   stream.position += 16; /* unused */
   var statusElementCount = stream.getInt16();

   var statusElement = [];
   for (var i = 0; i < statusElementCount; ++i)
      statusElement.push(this.parseStatusElement(stream));

   /* for objects with code pointers referring to a different object, link them. */
   for (var i = 0; i < statusElementCount; ++i)
   {
      if (statusElement[i].codeLength < 0)
         statusElement[i].code = this.statusElement[-this.statusElement[i].codeLength].code;
   }

   /* now, inject the information from the status elements into the appropriate tiles. */

   /* now turn these into objects */
   for (var i = 0; i < statusElementCount; ++i)
   {
      /* town.zzt's board 19 contains an element at (-1,-1)? */
      if (statusElement[i].y < 0 || statusElement[i].x < 0)
         continue;
      var tileIndex = statusElement[i].y * 60 + statusElement[i].x;
      var obj = board.tiles[tileIndex];

      if (obj.setParams)
         obj.setParams(statusElement[i]);

      /* handle all the 'below' layers too */
      if (status.underType)
      {
         var underObj = makeBoardObject(status.underType, status.underColor);
         underObj.x = statusElement[i].x;
         underObj.y = statusElement[i].y;
         board.tilesBelow[tileIndex] = underObj;
      }
   }

   /* update all the line characters */
   board.updateLines();

   /* jump to next board */
   stream.position = boardOffset + boardSize + 2;

   return board;
}

ZZTWorldLoader.prototype.parseStatusElement = function(stream)
{
   var status = {};

   /* x and y coordinates are 1-based for some reason */
   status.x = stream.getUint8() - 1;
   status.y = stream.getUint8() - 1;

   status.xStep = stream.getInt16();
   status.yStep = stream.getInt16();
   status.cycle = stream.getInt16();

   status.param1 = stream.getUint8();
   status.param2 = stream.getUint8();
   status.param3 = stream.getUint8();

   status.follower = stream.getInt16();
   status.leader = stream.getInt16();
   status.underType = stream.getUint8();
   status.underColor = stream.getUint8();
   stream.position += 4; /* pointer is not used when loading */
   status.currentInstruction = stream.getInt16();
   status.codeLength = stream.getInt16();

   /* for ZZT and not Super ZZT, eight bytes of padding follow */
   stream.position += 8;

   /* if status.codeLength is positive, there is that much ZZT-OOP code following */
   if (status.codeLength > 0)
   {
      status.code = stream.getFixedString(status.codeLength);
   }
   else
   {
      /* it's negative, which means that we'll need to look at a different
         object in order to use it's code instead; we'll do that later. */
      status.code = null;
   }

   return status;
}

ZZTBoard.prototype.get = function(x, y)
{
   return this.tiles[y * this.width + x];
}

ZZTBoard.prototype.set = function(x, y, obj)
{
  this.tiles[y * this.width + x] = obj;
}

ZZTBoard.prototype.update = function()
{
   var self = this;

   /* Make sure everybody's where they think they are. */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);
         if (tile.x != x || tile.y != y)
         {
            console.log("object at [" + x + ", " + y +
               "] thinks it's at [" + tile.x + ", " + y + "], fixing.");
            tile.x = x;
            tile.y = y;
         }
      }
   }

   /* Now, update them. */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);

         if (tile.update && tile.hasUpdated == false)
         {
            tile.update(this);
            tile.hasUpdated = true;
         }
      }
   }

   /* clear the update flag */
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);
         tile.hasUpdated = false;
      }
   }
}

ZZTBoard.prototype.draw = function(textconsole)
{
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);

         var inf = getTileRenderInfo(tile);
         textconsole.set(x, y, inf.glyph, inf.color);
      }
   }

   if (this.messageTimer > 0)
   {
      /* TODO: actually work out how to make this multiline */
      textconsole.setString(
         Math.floor((this.width / 2) - (this.onScreenMessage.length / 2)),
         24,
         this.onScreenMessage,
         (this.messageTimer % 6) + VGA.ATTR_FG_BBLUE);
      --this.messageTimer;
   }
}

ZZTBoard.prototype.setMessage = function(msg)
{
   /* TODO: actually work out how to make this multiline */
   if (msg.length >= (this.width - 2))
   {
      msg = msg.substr(0, (this.width - 2))
   }
   this.onScreenMessage = " " + msg + " ";
   this.messageTimer = 24;
}

var _ZZTBoard_LineGlyphs =
[
   /* NESW */
   /* 0000 */ 249,
   /* 0001 */ 181,
   /* 0010 */ 210,
   /* 0011 */ 187,
   /* 0100 */ 198,
   /* 0101 */ 205,
   /* 0110 */ 201,
   /* 0111 */ 203,
   /* 1000 */ 208,
   /* 1001 */ 188,
   /* 1010 */ 186,
   /* 1011 */ 185,
   /* 1100 */ 200,
   /* 1101 */ 202,
   /* 1110 */ 204,
   /* 1111 */ 206
];

/* Update the glyphs of all line characters on the board.

   We only need to do this whenever one of them changes. */
ZZTBoard.prototype.updateLines = function()
{
   for (var y = 0; y < this.height; ++y)
   {
      for (var x = 0; x < this.width; ++x)
      {
         var tile = this.get(x, y);
         if (tile.name == "line")
         {
            var glyphIndex = 0;

            if ((y == 0) || (this.get(x, y-1).name == "line"))
               glyphIndex += 8;

            if ((x == this.width-1) || (this.get(x+1, y).name == "line"))
               glyphIndex += 4;

            if ((y == this.height-1) || (this.get(x, y+1).name == "line"))
               glyphIndex += 2;

            if ((x == 0) || (this.get(x-1, y).name == "line"))
               glyphIndex += 1;

            tile.glyph = _ZZTBoard_LineGlyphs[glyphIndex];
         }
      }
   }
}
<|endoftext|>


<commit_before>var treeMapBuilder = (function() {

    // initialize all variables
    var treemap, root, formatNumber, rname, margin, theight, width, height, transitioning, x, y, svg, grandparent, maxDepth, defaults
	var refreshing = false;

    // initialize the entire treemap up till displaying
    function initializeTheTree(root) {
        initialize(root, width, height);
        accumulateValue(root);
        accumulateWarnings(root);
        layout(root, treemap);
    }

    // initialize the root of the treemap
    function initialize(root, width, height) {
        root.x = root.y = 0;
        root.dx = width;
        root.dy = height;
        root.depth = 0;
    }
    // Aggregate the values for internal nodes. This is normally done by the
    // treemap layout, but not here because of our custom implementation.
    // We also take a snapshot of the original children (_children) to avoid
    // the children being overwritten when when layout is computed.
    function accumulateValue(d) {
        return (d._children = d.values) ?
            d.value = d.values.reduce(function(p, v) {
                return p + accumulateValue(v);
            }, 0) :
            d.value;
    }

    function accumulateWarnings(d) {
        return (d._children = d.values) ?
            d.warnings = d.values.reduce(function(p, v) {
                return p + accumulateWarnings(v);
            }, 0) :
            d.warnings;
    }

    // Compute the treemap layout recursively such that each group of siblings
    // uses the same size (1×1) rather than the dimensions of the parent cell.
    // This optimizes the layout for the current zoom state. Note that a wrapper
    // object is created for the parent node for each group of siblings so that
    // the parent’s dimensions are not discarded as we recurse. Since each group
    // of sibling was laid out in 1×1, we must rescale to fit using absolute
    // coordinates. This lets us use a viewport to zoom.
    function layout(d, treemap) {
        if (d._children) {
            treemap.nodes({
                _children: d._children
            });
            d._children.forEach(function(c) {
                c.x = d.x + c.x * d.dx;
                c.y = d.y + c.y * d.dy;
                c.dx *= d.dx;
                c.dy *= d.dy;
                c.parent = d;
                layout(c, treemap);
            });
        }
    }
    //render the chart with given depth and children
    function display(d) {
        // On click top bar to go back
        grandparent
            .datum(d.parent)
            .on("click", transition)
            .select("text")
            .text(name(d));

        var g1 = svg.insert("g", ".grandparent")
            .datum(d)
            .attr("class", "depth");

        var g = g1.selectAll("g")
            .data(d._children)
            .enter().append("g");

        //on click square to go more in depth
        if (d.depth < maxDepth) {
            g.filter(function(d) {
                    return d._children;
                })
                .classed("children", true)
                .on("click", transition);
        }

		// all the updateContent class will trigger this refresh of data
		// so that the input of the user (checkboxes/radiobuttons) will update the content of
        $(".updateContent").off("click");
        $(".updateContent").on('click', function(view) {
            if (document.getElementById('treemapButton').checked && !refreshing) {
				refreshing = true;
                handleClickTreeMapTypeSat(view.target.value, view.target.checked)
                reloadContent();
                var newNode = findNode(d, root);
                g.filter(function(newNode) {
                    return newNode;
                });
                transition(newNode);
				var millisecondsToWait = 200;
				setTimeout(function() {
    				refreshing = false;
				}, millisecondsToWait);

            }
        });

        function findNode(d, root) {
            if (root.fileName != null && root.values != null) {
                if (root.fileName == d.fileName) {
                    return (root);
                } else {
                    var arr = root.values;
                    for (var i = 0; i < arr.length; i++) {
                        var finalNode = findNode(d, arr[i]);
                        if (finalNode != null) {
                            return finalNode;
                        }
                    }
                }
            } else {
                var err = null
                return err;
            }
        }

        function reloadContent() {
            root.values = getFilteredJSON();
            initialize(root, width, height);
            accumulateValue(root);
            accumulateWarnings(root);
            layout(root, treemap);
            display(root);
        }

        // on click child to go to source code
        if (d.depth == maxDepth) {
            g.filter(function(d) {
                    return d;
                })
                .classed("child", true)
                .on("click", toSourceCode);
        }

        var children = g.selectAll(".child")
            .data(function(d) {
                return d._children || [d];
            })
            .enter().append("g");

        children.append("rect")
            .attr("class", "child")
            .call(rect)
            .append("title")
            .text(function(d) {
                return d.fileName + " (" + formatNumber(d.warnings) + ")";
            });
        children.append("text")
            .attr("class", "ctext")
            .text(function(d) {
                return d.fileName;
            })
            .call(textBottomRight);

        g.append("rect")
            .attr("class", "parent")
            .call(rect);

        var t = g.append("text")
            .attr("class", "ptext")
            .attr("dy", ".75em")

        t.append("tspan")
            .text(function(d) {
                return d.fileName;
            });

        //title of the squares
        t.append("tspan")
            .attr("dy", "1.0em")
            .text(function(d) {
                return getSatWarningsPrint(d);
            });
        t.call(text);

		function getSatWarningsPrint(d){
			output = ""
			for (var i = 0; i < acceptedTypes.length; i++){
				output += formatNumber(d.warnings) + acceptedTypes[i].substring(0,1) + " ";
			}
			return formatNumber(d.warnings);
		}
        // set the color of the squares based on warnings / line
        g.selectAll("rect")
            .style("fill", function(d) {
                var ratio = 200 * d.warnings / d.value;
                // if statement for when there are more warnings then lines
                return colorScale.getColor(ratio);
            });

        function toSourceCode(d) {
            console.log(d.filePath)
        }

        function transition(d) {
            if (transitioning || !d) return;
            transitioning = true;

            var g2 = display(d),
                t1 = g1.transition().duration(100),
                t2 = g2.transition().duration(100);

            // Update the domain only after entering new elements.
            x.domain([d.x, d.x + d.dx]);
            y.domain([d.y, d.y + d.dy]);

            // Enable anti-aliasing during the transition.
            svg.style("shape-rendering", null);

            // Draw child nodes on top of parent nodes.
            svg.selectAll(".depth").sort(function(a, b) {
                return a.depth - b.depth;
            });

            // Fade-in entering text.
            g2.selectAll("text").style("fill-opacity", 0);

            // Transition to the new view.
            t1.selectAll(".ptext").call(text).style("fill-opacity", 0);
            t1.selectAll(".ctext").call(textBottomRight).style("fill-opacity", 0);
            t2.selectAll(".ptext").call(text).style("fill-opacity", 1);
            t2.selectAll(".ctext").call(textBottomRight).style("fill-opacity", 1);
            t1.selectAll("rect").call(rect);
            t2.selectAll("rect").call(rect);

            // Remove the old node when the transition is finished.
            t1.remove().each("end", function() {
                svg.style("shape-rendering", "crispEdges");
                transitioning = false;
            });
        }

        return g;
    }

    function text(text) {
        text.selectAll("tspan")
            .attr("x", function(d) {
                return x(d.x) + 6;
            })
        text.attr("x", function(d) {
                return x(d.x) + 6;
            })
            .attr("y", function(d) {
                return y(d.y) + 6;
            })
            .style("opacity", function(d) {
                return this.getComputedTextLength() < x(d.x + d.dx) - x(d.x) ? 1 : 0;
            });
    }
    function textBottomRight(text) {
        text.attr("x", function(d) {
                return x(d.x + d.dx) - this.getComputedTextLength() - 6;
            })
            .attr("y", function(d) {
                return y(d.y + d.dy) - 6;
            })
            .style("opacity", function(d) {
                return this.getComputedTextLength() < x(d.x + d.dx) - x(d.x) ? 1 : 0;
            });
    }

    function rect(rect) {
        rect.attr("x", function(d) {
                return x(d.x);
            })
            .attr("y", function(d) {
                return y(d.y);
            })
            .attr("width", function(d) {
                return x(d.x + d.dx) - x(d.x);
            })
            .attr("height", function(d) {
                return y(d.y + d.dy) - y(d.y);
            });
    }
    //title above the chart
    function name(d) {
        return d.parent ?
            name(d.parent) + " / " + d.fileName + " (" + formatNumber(d.warnings) + ")" :
            d.fileName + " (" + formatNumber(d.warnings) + ")";
    }

    function setTheVariables(o, data) {
        // hard coded the depth where the click should go to source code (no zoom)
        maxDepth = 2
        defaults = {
            margin: {
                top: 30,
                right: 0,
                bottom: 0,
                left: 0
            },
            rootname: "TOP",
            format: ",d",
            title: "",
            width: window.innerWidth - 350,
            height: window.innerHeight - 175
        };
        // Remove the chart if there is already one.
        removeChart();
        // Setting up some values about number format(rounding) and marigns
        opts = $.extend(true, {}, defaults, o);
        formatNumber = d3.format(opts.format);
        rname = opts.rootname;
        margin = opts.margin;
        theight = 36 + 16;

        // size of the chart
        $('#chart').width(opts.width).height(opts.height);
        width = opts.width - margin.left - margin.right;
        height = opts.height - margin.top - margin.bottom;

        // Uses a range of 100 values between green and red
        // The closer the value is to 0, the more green it will use
        // The closer the value is to 100, the more red it will use

        x = d3.scale.linear()
            .domain([0, width])
            .range([0, width]);

        y = d3.scale.linear()
            .domain([0, height])
            .range([0, height]);

        // Create the d3 treemap from the library
        treemap = d3.layout.treemap()
            .children(function(d, depth) {
                return depth ? null : d._children;
            })
            .sort(function(a, b) {
                return a.value - b.value;
            })
            .ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
            .round(false);

        // creating the chart and appending it to views
        svg = d3.select("#chart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.bottom + margin.top)
            .style("margin-left", -margin.left + "px")
            .style("margin.right", -margin.right + "px")
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
            .style("shape-rendering", "crispEdges");

        grandparent = svg.append("g")
            .attr("class", "grandparent");

        grandparent.append("rect")
            .attr("y", -margin.top)
            .attr("width", width)
            .attr("height", margin.top);

        grandparent.append("text")
            .attr("x", 6)
            .attr("y", 6 - margin.top)
            .attr("dy", ".75em")

        if (data instanceof Array) {
            root = {
                fileName: rname,
                values: data
            };
        } else {
            root = data;
        }
    }

    return {

        // The main method which is called to create the treeMap.
        // This calls all the methods needed like initialize.
        createTreeMap: function(o, data) {
            // First we create all variables that are needed for this treemap.
            setTheVariables(o, data);
            // After cresating the variables we can start initializing and displaying the tree.
            initializeTheTree(root);
            display(root);
        }

    };

}());<commit_msg>extra commaents for readability<commit_after>var treeMapBuilder = (function() {

    // initialize all variables
    var treemap, root, formatNumber, rname, margin, theight, width, height, transitioning, x, y, svg, grandparent, maxDepth, defaults
	var refreshing = false;

    // initialize the entire treemap up till displaying
    function initializeTheTree(root) {
        initialize(root, width, height);
        accumulateValue(root);
        accumulateWarnings(root);
        layout(root, treemap);
    }

    // initialize the root of the treemap
    function initialize(root, width, height) {
        root.x = root.y = 0;
        root.dx = width;
        root.dy = height;
        root.depth = 0;
    }
    // Aggregate the values for internal nodes. This is normally done by the
    // treemap layout, but not here because of our custom implementation.
    // We also take a snapshot of the original children (_children) to avoid
    // the children being overwritten when when layout is computed.
    function accumulateValue(d) {
        return (d._children = d.values) ?
            d.value = d.values.reduce(function(p, v) {
                return p + accumulateValue(v);
            }, 0) :
            d.value;
    }

    function accumulateWarnings(d) {
        return (d._children = d.values) ?
            d.warnings = d.values.reduce(function(p, v) {
                return p + accumulateWarnings(v);
            }, 0) :
            d.warnings;
    }

    // Compute the treemap layout recursively such that each group of siblings
    // uses the same size (1×1) rather than the dimensions of the parent cell.
    // This optimizes the layout for the current zoom state. Note that a wrapper
    // object is created for the parent node for each group of siblings so that
    // the parent’s dimensions are not discarded as we recurse. Since each group
    // of sibling was laid out in 1×1, we must rescale to fit using absolute
    // coordinates. This lets us use a viewport to zoom.
    function layout(d, treemap) {
        if (d._children) {
            treemap.nodes({
                _children: d._children
            });
            d._children.forEach(function(c) {
                c.x = d.x + c.x * d.dx;
                c.y = d.y + c.y * d.dy;
                c.dx *= d.dx;
                c.dy *= d.dy;
                c.parent = d;
                layout(c, treemap);
            });
        }
    }
    //render the chart with given depth and children
    function display(d) {
        // On click top bar to go back
        grandparent
            .datum(d.parent)
            .on("click", transition)
            .select("text")
            .text(name(d));

        var g1 = svg.insert("g", ".grandparent")
            .datum(d)
            .attr("class", "depth");

        var g = g1.selectAll("g")
            .data(d._children)
            .enter().append("g");

        //on click square to go more in depth
        if (d.depth < maxDepth) {
            g.filter(function(d) {
                    return d._children;
                })
                .classed("children", true)
                .on("click", transition);
        }

		// all the updateContent class will trigger this refresh of data
		// so that the input of the user (checkboxes/radiobuttons) will update the content of
        $(".updateContent").off("click");
        $(".updateContent").on('click', function(view) {
            if (document.getElementById('treemapButton').checked && !refreshing) {
				refreshing = true;
                handleClickTreeMapTypeSat(view.target.value, view.target.checked)
                reloadContent();
                var newNode = findNode(d, root);
                g.filter(function(newNode) {
                    return newNode;
                });
                transition(newNode);
				var millisecondsToWait = 200;
				setTimeout(function() {
    				refreshing = false;
				}, millisecondsToWait);

            }
        });

        function findNode(d, root) {
            if (root.fileName != null && root.values != null) {
                if (root.fileName == d.fileName) {
                    return (root);
                } else {
                    var arr = root.values;
                    for (var i = 0; i < arr.length; i++) {
                        var finalNode = findNode(d, arr[i]);
                        if (finalNode != null) {
                            return finalNode;
                        }
                    }
                }
            } else {
                var err = null
                return err;
            }
        }

        function reloadContent() {
            root.values = getFilteredJSON();
            initialize(root, width, height);
            accumulateValue(root);
            accumulateWarnings(root);
            layout(root, treemap);
            display(root);
        }

        // on click child to go to source code
        if (d.depth == maxDepth) {
            g.filter(function(d) {
                    return d;
                })
                .classed("child", true)
                .on("click", toSourceCode);
        }

        var children = g.selectAll(".child")
            .data(function(d) {
                return d._children || [d];
            })
            .enter().append("g");

        children.append("rect")
            .attr("class", "child")
            .call(rect)
            .append("title")
            .text(function(d) {
                return d.fileName + " (" + formatNumber(d.warnings) + ")";
            });
        children.append("text")
            .attr("class", "ctext")
            .text(function(d) {
                return d.fileName;
            })
            .call(textBottomRight);

        g.append("rect")
            .attr("class", "parent")
            .call(rect);

        var t = g.append("text")
            .attr("class", "ptext")
            .attr("dy", ".75em")

        t.append("tspan")
            .text(function(d) {
                return d.fileName;
            });

        //title of the squares
        t.append("tspan")
            .attr("dy", "1.0em")
            .text(function(d) {
                return getSatWarningsPrint(d);
            });
        t.call(text);

		// future method for more advance warning count
		function getSatWarningsPrint(d){
			output = ""
			for (var i = 0; i < acceptedTypes.length; i++){
				output += formatNumber(d.warnings) + acceptedTypes[i].substring(0,1) + " ";
			}
			return formatNumber(d.warnings);
		}
        // set the color of the squares based on warnings / line
        g.selectAll("rect")
            .style("fill", function(d) {
                var ratio = 200 * d.warnings / d.value;
                // if statement for when there are more warnings then lines
                return colorScale.getColor(ratio);
            });

        function toSourceCode(d) {
            console.log(d.filePath)
        }

        function transition(d) {
            if (transitioning || !d) return;
            transitioning = true;

            var g2 = display(d),
                t1 = g1.transition().duration(100),
                t2 = g2.transition().duration(100);

            // Update the domain only after entering new elements.
            x.domain([d.x, d.x + d.dx]);
            y.domain([d.y, d.y + d.dy]);

            // Enable anti-aliasing during the transition.
            svg.style("shape-rendering", null);

            // Draw child nodes on top of parent nodes.
            svg.selectAll(".depth").sort(function(a, b) {
                return a.depth - b.depth;
            });

            // Fade-in entering text.
            g2.selectAll("text").style("fill-opacity", 0);

            // Transition to the new view.
            t1.selectAll(".ptext").call(text).style("fill-opacity", 0);
            t1.selectAll(".ctext").call(textBottomRight).style("fill-opacity", 0);
            t2.selectAll(".ptext").call(text).style("fill-opacity", 1);
            t2.selectAll(".ctext").call(textBottomRight).style("fill-opacity", 1);
            t1.selectAll("rect").call(rect);
            t2.selectAll("rect").call(rect);

            // Remove the old node when the transition is finished.
            t1.remove().each("end", function() {
                svg.style("shape-rendering", "crispEdges");
                transitioning = false;
            });
        }

        return g;
    }

    function text(text) {
        text.selectAll("tspan")
            .attr("x", function(d) {
                return x(d.x) + 6;
            })
        text.attr("x", function(d) {
                return x(d.x) + 6;
            })
            .attr("y", function(d) {
                return y(d.y) + 6;
            })
            .style("opacity", function(d) {
                return this.getComputedTextLength() < x(d.x + d.dx) - x(d.x) ? 1 : 0;
            });
    }
    function textBottomRight(text) {
        text.attr("x", function(d) {
                return x(d.x + d.dx) - this.getComputedTextLength() - 6;
            })
            .attr("y", function(d) {
                return y(d.y + d.dy) - 6;
            })
            .style("opacity", function(d) {
                return this.getComputedTextLength() < x(d.x + d.dx) - x(d.x) ? 1 : 0;
            });
    }

    function rect(rect) {
        rect.attr("x", function(d) {
                return x(d.x);
            })
            .attr("y", function(d) {
                return y(d.y);
            })
            .attr("width", function(d) {
                return x(d.x + d.dx) - x(d.x);
            })
            .attr("height", function(d) {
                return y(d.y + d.dy) - y(d.y);
            });
    }
    //title above the chart
    function name(d) {
        return d.parent ?
            name(d.parent) + " / " + d.fileName + " (" + formatNumber(d.warnings) + ")" :
            d.fileName + " (" + formatNumber(d.warnings) + ")";
    }

    function setTheVariables(o, data) {
        // hard coded the depth where the click should go to source code (no zoom)
        maxDepth = 2
        defaults = {
            margin: {
                top: 30,
                right: 0,
                bottom: 0,
                left: 0
            },
            rootname: "TOP",
            format: ",d",
            title: "",
            width: window.innerWidth - 350,
            height: window.innerHeight - 175
        };
        // Remove the chart if there is already one.
        removeChart();
        // Setting up some values about number format(rounding) and marigns
        opts = $.extend(true, {}, defaults, o);
        formatNumber = d3.format(opts.format);
        rname = opts.rootname;
        margin = opts.margin;
        theight = 36 + 16;

        // size of the chart
        $('#chart').width(opts.width).height(opts.height);
        width = opts.width - margin.left - margin.right;
        height = opts.height - margin.top - margin.bottom;

        // Uses a range of 100 values between green and red
        // The closer the value is to 0, the more green it will use
        // The closer the value is to 100, the more red it will use

        x = d3.scale.linear()
            .domain([0, width])
            .range([0, width]);

        y = d3.scale.linear()
            .domain([0, height])
            .range([0, height]);

        // Create the d3 treemap from the library
        treemap = d3.layout.treemap()
            .children(function(d, depth) {
                return depth ? null : d._children;
            })
            .sort(function(a, b) {
                return a.value - b.value;
            })
            .ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
            .round(false);

        // creating the chart and appending it to views
        svg = d3.select("#chart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.bottom + margin.top)
            .style("margin-left", -margin.left + "px")
            .style("margin.right", -margin.right + "px")
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
            .style("shape-rendering", "crispEdges");

        grandparent = svg.append("g")
            .attr("class", "grandparent");

        grandparent.append("rect")
            .attr("y", -margin.top)
            .attr("width", width)
            .attr("height", margin.top);

        grandparent.append("text")
            .attr("x", 6)
            .attr("y", 6 - margin.top)
            .attr("dy", ".75em")

        if (data instanceof Array) {
            root = {
                fileName: rname,
                values: data
            };
        } else {
            root = data;
        }
    }

    return {

        // The main method which is called to create the treeMap.
        // This calls all the methods needed like initialize.
        createTreeMap: function(o, data) {
            // First we create all variables that are needed for this treemap.
            setTheVariables(o, data);
            // After cresating the variables we can start initializing and displaying the tree.
            initializeTheTree(root);
            display(root);
        }

    };

}());<|endoftext|>


<commit_before><commit_msg>Allow a feed to flood more than 3 items so long as the flood is less than 20% of the feed's total item count.<commit_after><|endoftext|>


<commit_before><commit_msg>Change Background for PM hours in Clock activity<commit_after><|endoftext|>


<commit_before>/**
 * @todo don't show last/first week twice across two consecutive months
 * @todo smart-zoom (1 mo, 3 mo, 6mo, > 6mo)
 * @todo if there are 4 months => display 6 months. figure out how to display those 2 extra months
 */

import Ember from 'ember';
import layout from '../templates/components/calendar-month-multi';

const {
  computed,
  on
} = Ember;

const Month = Ember.Object.extend({
  month: null,
  year: null,

  /**
   * Show last partial week
   * If current month ends on a Tuesday, don't show this month on this calendar, but rather the next month
   * This should be true for the last month
   * @todo verify this works
   * @type {Boolean}
   */
  showLastPartialWeek: false
})

export default Ember.Component.extend({
  layout,
  classNames: ['calendar-month-multi'],

  // canonical
  startDate: null,
  endDate: null,

  context: null,

  /**
   * Max months to show
   * @type {Number}
   */
  maxMonthsToShow: 6,

  _onDidReceiveAttrs: on('didReceiveAttrs', function() {
    // validate starDate and endDate are valid moment objects
    if (!moment.isMoment(this.get('startDate')) || !moment.isMoment(this.get('endDate'))) {
      this.setProperties({
        startDate: moment(this.get('startDate')),
        endDate: moment(this.get('endDate'))
      });
    }

    // validate startDate is before endDate
    if (this.get('endDate').isBefore(this.get('startDate'))) {
      throw new Error('Start date must be before end date', this.get('startDate'), this.get('endDate'));
    }
  }),

  /**
   * Override this method in your controller
   * @param {object} date - moment date
   * @return {string} class name to be applied to this date
   */
  customClassFunction(date) {
    return false;
  },

  /**
   * Don't show duplicate end of last month/beginning of this month
   * If endDate - startDate = 2 months, still display 3 months (pad one on end)
   * If endDate - startDate > 6 mo, display "next" and "previous"
   * @type {array} array of months
   */
  displayMonths: computed('startDate', 'endDate', 'numberOfMonthsToDisplay', function() {
    var months = [];
    let current = moment(this.get('startDate'));

    for (let i = 0; i < this.get('numberOfMonthsToDisplay'); i++) {
        months.addObject(Month.create({
          // current.month() is 0-indexed, but calendar-month expects 1-indexed month
          month: current.month(),
          year: current.year(),
          showLastPartialWeek: (i === this.get('numberOfMonthsToDisplay') - 1)
        }));
        current.add(1, 'month');
    }

    return months;
  }),

  /**
   * The number of months to display
   * @type {number}
   */
  numberOfMonthsToDisplay: computed('startDate', 'endDate', 'maxMonthsToShow', function() {
    var startDate = this.get('startDate');
    var endDate = this.get('endDate');
    const maxMonthsToShow = this.get('maxMonthsToShow');

    if (startDate.month() === endDate.month()) {
      return 1;
    }

    var monthsDiff = endDate.diff(startDate, 'months');

    if (monthsDiff > maxMonthsToShow) {
      return maxMonthsToShow;
    }

    return monthsDiff + 1;
  })
});
<commit_msg>fix issue where last week of month not being rendered<commit_after>/**
 * @todo don't show last/first week twice across two consecutive months
 * @todo smart-zoom (1 mo, 3 mo, 6mo, > 6mo)
 * @todo if there are 4 months => display 6 months. figure out how to display those 2 extra months
 */

import Ember from 'ember';
import layout from '../templates/components/calendar-month-multi';

const {
  computed,
  on
} = Ember;

const Month = Ember.Object.extend({
  month: null,
  year: null,

  /**
   * Show last partial week
   * If current month ends on a Tuesday, don't show this month on this calendar, but rather the next month
   * This should be true for the last month
   * @todo verify this works
   * @type {Boolean}
   */
  showLastPartialWeek: false
})

export default Ember.Component.extend({
  layout,
  classNames: ['calendar-month-multi'],

  // canonical
  startDate: null,
  endDate: null,

  context: null,

  /**
   * Max months to show
   * @type {Number}
   */
  maxMonthsToShow: 6,

  _onDidReceiveAttrs: on('didReceiveAttrs', function() {
    // validate starDate and endDate are valid moment objects
    if (!moment.isMoment(this.get('startDate')) || !moment.isMoment(this.get('endDate'))) {
      this.setProperties({
        startDate: moment(this.get('startDate')),
        endDate: moment(this.get('endDate'))
      });
    }

    // validate startDate is before endDate
    if (this.get('endDate').isBefore(this.get('startDate'))) {
      throw new Error('Start date must be before end date', this.get('startDate'), this.get('endDate'));
    }
  }),

  /**
   * Override this method in your controller
   * @param {object} date - moment date
   * @return {string} class name to be applied to this date
   */
  customClassFunction(date) {
    return false;
  },

  /**
   * Don't show duplicate end of last month/beginning of this month
   * If endDate - startDate = 2 months, still display 3 months (pad one on end)
   * If endDate - startDate > 6 mo, display "next" and "previous"
   * @type {array} array of months
   */
  displayMonths: computed('startDate', 'endDate', 'numberOfMonthsToDisplay', function() {
    var months = [];
    let current = moment(this.get('startDate'));

    // if I enter 7/31/2017 (a Monday, so start of week) as startDate, that will be rendered in August since 8/1 is _not_ start of week. I shouldn't show July
    const firstWeekOfNextMonth = current.clone().add(1, 'month').startOf('month');
    if (current.isSame(firstWeekOfNextMonth, 'week')) {
      current.add(1, 'week');
    }

    for (let i = 0; i < this.get('numberOfMonthsToDisplay'); i++) {
        months.addObject(Month.create({
          // current.month() is 0-indexed, but calendar-month expects 1-indexed month
          month: current.month(),
          year: current.year(),
          showLastPartialWeek: (i === this.get('numberOfMonthsToDisplay') - 1)
        }));
        current.add(1, 'month');
    }

    return months;
  }),

  /**
   * The number of months to display
   * @type {number}
   */
  numberOfMonthsToDisplay: computed('startDate', 'endDate', 'maxMonthsToShow', function() {
    var startDate = this.get('startDate');
    var endDate = this.get('endDate');
    const maxMonthsToShow = this.get('maxMonthsToShow');

    if (startDate.month() === endDate.month()) {
      return 1;
    }

    var monthsDiff = endDate.diff(startDate, 'months');

    if (monthsDiff > maxMonthsToShow) {
      return maxMonthsToShow;
    }

    return monthsDiff + 1;
  })
});
<|endoftext|>