<commit_before>import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { toFixed } from 'common/math';
import { useBackend } from '../backend';
import { Button, LabeledList, NumberInput, ProgressBar, Section, Stack, Box } from '../components';
import { getGasColor, getGasLabel } from '../constants';
import { formatSiBaseTenUnit, formatSiUnit } from '../format';
import { Window } from '../layouts';

export const Hypertorus = (props, context) => {
  const { act, data } = useBackend(context);
  const filterTypes = data.filter_types || [];
  const selectedFuels = data.selected_fuel || [];
  const {
    energy_level,
    core_temperature,
    internal_power,
    power_output,
    heat_limiter_modifier,
    heat_output,
    heat_output_bool,
    heating_conductor,
    magnetic_constrictor,
    fuel_injection_rate,
    moderator_injection_rate,
    current_damper,
    power_level,
    iron_content,
    integrity,
    start_power,
    start_cooling,
    start_fuel,
    internal_fusion_temperature,
    moderator_internal_temperature,
    internal_output_temperature,
    internal_coolant_temperature,
    waste_remove,
    selected,
    product_gases,
  } = data;
  const fusion_gases = flow([
    filter(gas => gas.amount >= 0.01),
    sortBy(gas => -gas.amount),
  ])(data.fusion_gases || []);
  const moderator_gases = flow([
    filter(gas => gas.amount >= 0.01),
    sortBy(gas => -gas.amount),
  ])(data.moderator_gases || []);
  const fusionMax = Math.max(1, ...fusion_gases.map(gas => gas.amount));
  const moderatorMax = Math.max(1, ...moderator_gases.map(gas => gas.amount));
  return (
    <Window
      title="Fusion Reactor"
      width={500}
      height={600}>
      <Window.Content scrollable>

          <Stack>
            <Stack.Item color="label">
              {'Start power: '}
              <Button
                disabled={data.power_level > 0}
                icon={data.start_power ? 'power-off' : 'times'}
                content={data.start_power ? 'On' : 'Off'}
                selected={data.start_power}
                onClick={() => act('start_power')} />
            </Stack.Item>
            <Stack.Item color="label">
              {'Start cooling: '}
              <Button
                disabled={start_fuel === 1
                  || start_power === 0
                  || (start_cooling && data.power_level > 0)}
                icon={data.start_cooling ? 'power-off' : 'times'}
                content={data.start_cooling ? 'On' : 'Off'}
                selected={data.start_cooling}
                onClick={() => act('start_cooling')} />
            </Stack.Item>
            <Stack.Item color="label">
              {'Start fuel injection: '}
              <Button
                disabled={start_power === 0
                  || start_cooling === 0}
                icon={data.start_fuel ? 'power-off' : 'times'}
                content={data.start_fuel ? 'On' : 'Off'}
                selected={data.start_fuel}
                onClick={() => act('start_fuel')} />
            </Stack.Item>
          </Stack>

          <LabeledList>
            <LabeledList.Item label="Fuel">
              {selectedFuels.map(recipe => (
                <Button
                  disabled={data.power_level > 0}
                  key={recipe.id}
                  selected={recipe.id === selected}
                  content={recipe.name}
                  onClick={() => act('fuel', {
                    mode: recipe.id,
                  })} />
              ))}
            </LabeledList.Item>
            <LabeledList.Item label="Gases">
              <Box m={1} preserveWhitespace>
                {product_gases}
              </Box>
            </LabeledList.Item>
          </LabeledList>

          <LabeledList>
            {fusion_gases.map(gas => (
              <LabeledList.Item
                key={gas.name}
                label={getGasLabel(gas.name)}>
                <ProgressBar
                  color={getGasColor(gas.name)}
                  value={gas.amount}
                  minValue={0}
                  maxValue={fusionMax}>
                  {toFixed(gas.amount, 2) + ' moles'}
                </ProgressBar>
              </LabeledList.Item>
            ))}
          </LabeledList>

          <LabeledList>
            {moderator_gases.map(gas => (
              <LabeledList.Item
                key={gas.name}
                label={getGasLabel(gas.name)}>
                <ProgressBar
                  color={getGasColor(gas.name)}
                  value={gas.amount}
                  minValue={0}
                  maxValue={moderatorMax}>
                  {toFixed(gas.amount, 2) + ' moles'}
                </ProgressBar>
              </LabeledList.Item>
            ))}
          </LabeledList>

          <LabeledList>
            <LabeledList.Item label="Power Level">
              <ProgressBar
                value={power_level}
                ranges={{
                  good: [0, 2],
                  average: [2, 4],
                  bad: [4, 6],
                }} />
            </LabeledList.Item>
            <LabeledList.Item label="Integrity">
              <ProgressBar
                value={integrity / 100}
                ranges={{
                  good: [0.90, Infinity],
                  average: [0.5, 0.90],
                  bad: [-Infinity, 0.5],
                }} />
            </LabeledList.Item>
            <LabeledList.Item label="Iron Content">
              <ProgressBar
                value={iron_content}
                ranges={{
                  good: [-Infinity, 3],
                  average: [3, 6],
                  bad: [6, Infinity],
                }} />
            </LabeledList.Item>
            <LabeledList.Item label="Energy Levels">
              <ProgressBar
                color={'yellow'}
                value={energy_level}
                minValue={0}
                maxValue={1e35}>
                {formatSiUnit(energy_level, 1, 'J')}
              </ProgressBar>
            </LabeledList.Item>
            <LabeledList.Item label="Heat Limiter Modifier">
              <ProgressBar
                color={'blue'}
                value={heat_limiter_modifier}
                minValue={-1e40}
                maxValue={1e30}>
                {formatSiBaseTenUnit(heat_limiter_modifier * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
            <LabeledList.Item label="Heat Output">
              <ProgressBar
                color={'grey'}
                value={heat_output}
                minValue={-1e40}
                maxValue={1e30}>
                {heat_output_bool + formatSiBaseTenUnit(heat_output * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
          </LabeledList>

          <LabeledList>
            <LabeledList.Item label="Fusion gas temperature">
              <ProgressBar
                color={'yellow'}
                value={internal_fusion_temperature}
                minValue={0}
                maxValue={1e30}>
                {formatSiBaseTenUnit(internal_fusion_temperature * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
            <LabeledList.Item label="Moderator gas temperature">
              <ProgressBar
                color={'red'}
                value={moderator_internal_temperature}
                minValue={0}
                maxValue={1e30}>
                {formatSiBaseTenUnit(moderator_internal_temperature * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
            <LabeledList.Item label="Output gas temperature">
              <ProgressBar
                color={'pink'}
                value={internal_output_temperature}
                minValue={0}
                maxValue={1e30}>
                {formatSiBaseTenUnit(internal_output_temperature * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
            <LabeledList.Item label="Coolant output temperature">
              <ProgressBar
                color={'green'}
                value={internal_coolant_temperature}
                minValue={0}
                maxValue={1e30}>
                {formatSiBaseTenUnit(internal_coolant_temperature * 1000, 1, 'K')}
              </ProgressBar>
            </LabeledList.Item>
          </LabeledList>

          <LabeledList>
            <LabeledList.Item label="Heating Conductor">
              <NumberInput
                animated
                value={parseFloat(data.heating_conductor)}
                width="63px"
                unit="J/cm"
                minValue={50}
                maxValue={500}
                onDrag={(e, value) => act('heating_conductor', {
                  heating_conductor: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Cooling Volume">
              <NumberInput
                animated
                value={parseFloat(data.cooling_volume)}
                width="63px"
                unit="L"
                minValue={50}
                maxValue={2000}
                onDrag={(e, value) => act('cooling_volume', {
                  cooling_volume: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Magnetic Constrictor">
              <NumberInput
                animated
                value={parseFloat(data.magnetic_constrictor)}
                width="63px"
                unit="m^3/B"
                minValue={50}
                maxValue={1000}
                onDrag={(e, value) => act('magnetic_constrictor', {
                  magnetic_constrictor: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Fuel Injection Rate">
              <NumberInput
                animated
                value={parseFloat(data.fuel_injection_rate)}
                width="63px"
                unit="g/s"
                minValue={5}
                maxValue={1500}
                onDrag={(e, value) => act('fuel_injection_rate', {
                  fuel_injection_rate: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Moderator Injection Rate">
              <NumberInput
                animated
                value={parseFloat(data.moderator_injection_rate)}
                width="63px"
                unit="g/s"
                minValue={5}
                maxValue={1500}
                onDrag={(e, value) => act('moderator_injection_rate', {
                  moderator_injection_rate: value,
                })} />
            </LabeledList.Item>
            <LabeledList.Item label="Current Damper">
              <NumberInput
                animated
                value={parseFloat(data.current_damper)}
                width="63px"
                unit="W"
                minValue={0}
                maxValue={1000}
                onDrag={(e, value) => act('current_damper', {
                  current_damper: value,
                })} />
            </LabeledList.Item>
          </LabeledList>

          <LabeledList>
            <LabeledList.Item label="Waste remove">
              <Button
                disabled={data.power_level > 5}
                icon={data.waste_remove ? 'power-off' : 'times'}
                content={data.waste_remove ? 'On' : 'Off'}
                selected={data.waste_remove}
                onClick={() => act('waste_remove')} />
            </LabeledList.Item>
            <LabeledList.Item label="Filter from moderator mix">
              {filterTypes.map(filter => (
                <Button
                  key={filter.id}
                  selected={filter.selected}
                  content={getGasLabel(filter.id, filter.name)}
                  onClick={() => act('filter', {
                    mode: filter.id,
                  })} />
              ))}
            </LabeledList.Item>
          </LabeledList>

      </Window.Content>
    </Window>
  );
};
<commit_msg>HFR better UI (#61237)<commit_after>import { filter, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { toFixed } from 'common/math';
import { useBackend, useLocalState } from '../backend';
import { Button, LabeledList, NumberInput, ProgressBar, Section, Stack, Box, Tabs } from '../components';
import { getGasColor, getGasLabel } from '../constants';
import { formatSiBaseTenUnit, formatSiUnit } from '../format';
import { Window } from '../layouts';

const HypertorusMainControls = (props, context) => {
  const { act, data } = useBackend(context);
  const selectedFuels = data.selected_fuel || [];
  return (
    <>

        <Stack>
          <Stack.Item color="label">
            {'Start power: '}
            <Button
              disabled={data.power_level > 0}
              icon={data.start_power ? 'power-off' : 'times'}
              content={data.start_power ? 'On' : 'Off'}
              selected={data.start_power}
              onClick={() => act('start_power')} />
          </Stack.Item>
          <Stack.Item color="label">
            {'Start cooling: '}
            <Button
              disabled={data.start_fuel === 1
                || data.start_power === 0
                || (data.start_cooling && data.power_level > 0)}
              icon={data.start_cooling ? 'power-off' : 'times'}
              content={data.start_cooling ? 'On' : 'Off'}
              selected={data.start_cooling}
              onClick={() => act('start_cooling')} />
          </Stack.Item>
          <Stack.Item color="label">
            {'Start fuel injection: '}
            <Button
              disabled={data.start_power === 0
                || data.start_cooling === 0}
              icon={data.start_fuel ? 'power-off' : 'times'}
              content={data.start_fuel ? 'On' : 'Off'}
              selected={data.start_fuel}
              onClick={() => act('start_fuel')} />
          </Stack.Item>
        </Stack>

        <LabeledList>
          <LabeledList.Item label="Fuel">
            {selectedFuels.map(recipe => (
              <Button
                disabled={data.power_level > 0}
                key={recipe.id}
                selected={recipe.id === data.selected}
                content={recipe.name}
                onClick={() => act('fuel', {
                  mode: recipe.id,
                })} />
            ))}
          </LabeledList.Item>
          <LabeledList.Item label="Gases">
            <Box m={1} preserveWhitespace>
              {data.product_gases}
            </Box>
          </LabeledList.Item>
        </LabeledList>

    </>
  );
};

const HypertorusSecondaryControls = (props, context) => {
  const { act, data } = useBackend(context);
  const filterTypes = data.filter_types || [];
  return (
    <>

        <LabeledList>
          <LabeledList.Item label="Heating Conductor">
            <NumberInput
              animated
              value={parseFloat(data.heating_conductor)}
              width="63px"
              unit="J/cm"
              minValue={50}
              maxValue={500}
              onDrag={(e, value) => act('heating_conductor', {
                heating_conductor: value,
              })} />
          </LabeledList.Item>
          <LabeledList.Item label="Cooling Volume">
            <NumberInput
              animated
              value={parseFloat(data.cooling_volume)}
              width="63px"
              unit="L"
              minValue={50}
              maxValue={2000}
              onDrag={(e, value) => act('cooling_volume', {
                cooling_volume: value,
              })} />
          </LabeledList.Item>
          <LabeledList.Item label="Magnetic Constrictor">
            <NumberInput
              animated
              value={parseFloat(data.magnetic_constrictor)}
              width="63px"
              unit="m^3/B"
              minValue={50}
              maxValue={1000}
              onDrag={(e, value) => act('magnetic_constrictor', {
                magnetic_constrictor: value,
              })} />
          </LabeledList.Item>
          <LabeledList.Item label="Fuel Injection Rate">
            <NumberInput
              animated
              value={parseFloat(data.fuel_injection_rate)}
              width="63px"
              unit="g/s"
              minValue={5}
              maxValue={1500}
              onDrag={(e, value) => act('fuel_injection_rate', {
                fuel_injection_rate: value,
              })} />
          </LabeledList.Item>
          <LabeledList.Item label="Moderator Injection Rate">
            <NumberInput
              animated
              value={parseFloat(data.moderator_injection_rate)}
              width="63px"
              unit="g/s"
              minValue={5}
              maxValue={1500}
              onDrag={(e, value) => act('moderator_injection_rate', {
                moderator_injection_rate: value,
              })} />
          </LabeledList.Item>
          <LabeledList.Item label="Current Damper">
            <NumberInput
              animated
              value={parseFloat(data.current_damper)}
              width="63px"
              unit="W"
              minValue={0}
              maxValue={1000}
              onDrag={(e, value) => act('current_damper', {
                current_damper: value,
              })} />
          </LabeledList.Item>
        </LabeledList>

        <LabeledList>
          <LabeledList.Item label="Waste remove">
            <Button
              disabled={data.power_level > 5}
              icon={data.waste_remove ? 'power-off' : 'times'}
              content={data.waste_remove ? 'On' : 'Off'}
              selected={data.waste_remove}
              onClick={() => act('waste_remove')} />
          </LabeledList.Item>
          <LabeledList.Item label="Filter from moderator mix">
            {filterTypes.map(filter => (
              <Button
                key={filter.id}
                selected={filter.selected}
                content={getGasLabel(filter.id, filter.name)}
                onClick={() => act('filter', {
                  mode: filter.id,
                })} />
            ))}
          </LabeledList.Item>
        </LabeledList>

    </>
  );
};

const HypertorusGases = (props, context) => {
  const { act, data } = useBackend(context);
  const fusion_gases = flow([
    filter(gas => gas.amount >= 0.01),
    sortBy(gas => -gas.amount),
  ])(data.fusion_gases || []);
  const moderator_gases = flow([
    filter(gas => gas.amount >= 0.01),
    sortBy(gas => -gas.amount),
  ])(data.moderator_gases || []);
  const fusionMax = Math.max(1, ...fusion_gases.map(gas => gas.amount));
  const moderatorMax = Math.max(1, ...moderator_gases.map(gas => gas.amount));
  return (
    <>

        <LabeledList>
          {fusion_gases.map(gas => (
            <LabeledList.Item
              key={gas.name}
              label={getGasLabel(gas.name)}>
              <ProgressBar
                color={getGasColor(gas.name)}
                value={gas.amount}
                minValue={0}
                maxValue={fusionMax}>
                {toFixed(gas.amount, 2) + ' moles'}
              </ProgressBar>
            </LabeledList.Item>
          ))}
        </LabeledList>

        <LabeledList>
          {moderator_gases.map(gas => (
            <LabeledList.Item
              key={gas.name}
              label={getGasLabel(gas.name)}>
              <ProgressBar
                color={getGasColor(gas.name)}
                value={gas.amount}
                minValue={0}
                maxValue={moderatorMax}>
                {toFixed(gas.amount, 2) + ' moles'}
              </ProgressBar>
            </LabeledList.Item>
          ))}
        </LabeledList>

    </>
  );
};

const HypertorusParameters = (props, context) => {
  const { act, data } = useBackend(context);
  const {
    energy_level,
    heat_limiter_modifier,
    heat_output,
    heat_output_bool,
    power_level,
    iron_content,
    integrity,
    internal_fusion_temperature,
    moderator_internal_temperature,
    internal_output_temperature,
    internal_coolant_temperature,
  } = data;
  return (
    <>

        <LabeledList>
          <LabeledList.Item label="Power Level">
            <ProgressBar
              value={power_level}
              ranges={{
                good: [0, 2],
                average: [2, 4],
                bad: [4, 6],
              }} />
          </LabeledList.Item>
          <LabeledList.Item label="Integrity">
            <ProgressBar
              value={integrity / 100}
              ranges={{
                good: [0.90, Infinity],
                average: [0.5, 0.90],
                bad: [-Infinity, 0.5],
              }} />
          </LabeledList.Item>
          <LabeledList.Item label="Iron Content">
            <ProgressBar
              value={iron_content}
              ranges={{
                good: [-Infinity, 3],
                average: [3, 6],
                bad: [6, Infinity],
              }} />
          </LabeledList.Item>
          <LabeledList.Item label="Energy Levels">
            <ProgressBar
              color={'yellow'}
              value={energy_level}
              minValue={0}
              maxValue={1e35}>
              {formatSiUnit(energy_level, 1, 'J')}
            </ProgressBar>
          </LabeledList.Item>
          <LabeledList.Item label="Heat Limiter Modifier">
            <ProgressBar
              color={'blue'}
              value={heat_limiter_modifier}
              minValue={-1e40}
              maxValue={1e30}>
              {formatSiBaseTenUnit(heat_limiter_modifier * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
          <LabeledList.Item label="Heat Output">
            <ProgressBar
              color={'grey'}
              value={heat_output}
              minValue={-1e40}
              maxValue={1e30}>
              {heat_output_bool + formatSiBaseTenUnit(heat_output * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
        </LabeledList>

        <LabeledList>
          <LabeledList.Item label="Fusion gas temperature">
            <ProgressBar
              color={'yellow'}
              value={internal_fusion_temperature}
              minValue={0}
              maxValue={1e30}>
              {formatSiBaseTenUnit(internal_fusion_temperature * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
          <LabeledList.Item label="Moderator gas temperature">
            <ProgressBar
              color={'red'}
              value={moderator_internal_temperature}
              minValue={0}
              maxValue={1e30}>
              {formatSiBaseTenUnit(moderator_internal_temperature * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
          <LabeledList.Item label="Output gas temperature">
            <ProgressBar
              color={'pink'}
              value={internal_output_temperature}
              minValue={0}
              maxValue={1e30}>
              {formatSiBaseTenUnit(internal_output_temperature * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
          <LabeledList.Item label="Coolant output temperature">
            <ProgressBar
              color={'green'}
              value={internal_coolant_temperature}
              minValue={0}
              maxValue={1e30}>
              {formatSiBaseTenUnit(internal_coolant_temperature * 1000, 1, 'K')}
            </ProgressBar>
          </LabeledList.Item>
        </LabeledList>

    </>
  );
};

const HypertorusTabs = (props, context) => {
  const { act, data } = useBackend(context);
  const [
    tabIndex,
    setTabIndex,
  ] = useLocalState(context, 'tab-index', 1);
  return (
    <>
      <Tabs>
        <Tabs.Tab
          selected={tabIndex === 1}
          onClick={() => {
            setTabIndex(1);
          }}>
          HFR Main controls
        </Tabs.Tab>
        <Tabs.Tab
          selected={tabIndex === 2}
          onClick={() => {
            setTabIndex(2);
          }}>
          HFR Secondary controls
        </Tabs.Tab>
        <Tabs.Tab
          selected={tabIndex === 3}
          onClick={() => {
            setTabIndex(3);
          }}>
          HFR internal gases
        </Tabs.Tab>
        <Tabs.Tab
          selected={tabIndex === 4}
          onClick={() => {
            setTabIndex(4);
          }}>
          HFR internal parameters
        </Tabs.Tab>
      </Tabs>
      {tabIndex === 1 && (
        <HypertorusMainControls />
      )}
      {tabIndex === 2 && (
        <HypertorusSecondaryControls />
      )}
      {tabIndex === 3 && (
        <HypertorusGases />
      )}
      {tabIndex === 4 && (
        <HypertorusParameters />
      )}
    </>
  );
};

export const Hypertorus = (props, context) => {
  const { act, data } = useBackend(context);
  return (
    <Window
      title="Hypertorus Fusion Reactor control panel"
      width={500}
      height={600}>
      <Window.Content scrollable>
        <HypertorusTabs />
      </Window.Content>
    </Window>
  );
};
<|endoftext|>


<commit_before>var Component = require('./component')
var $ = window.jQuery

class LogoSwitcher extends Component {

	get listeners() {
		return {
			'click a[href^="#"]': 'handleClick'
		}
	}

	handleClick(e, self) {
	e.preventDefault();

		$('html, body').animate({
			scrollTop: $($.attr(e.currentTarget, 'href')).offset().top
		}, 500);
	}
}

module.exports = LogoSwitcher
<commit_msg>Fix scrolltoacnchor module name<commit_after>var Component = require('./component')
var $ = window.jQuery

class ScrollToAnchor extends Component {

	get listeners() {
		return {
			'click a[href^="#"]': 'handleClick'
		}
	}

	handleClick(e, self) {
	e.preventDefault();

		$('html, body').animate({
			scrollTop: $($.attr(e.currentTarget, 'href')).offset().top
		}, 500);
	}
}

module.exports = ScrollToAnchor
<|endoftext|>


<commit_before>'use strict';

var assign = require('object-assign');

var ALLOWED_HTML_ATTRIBUTES = 'accept acceptCharset accessKey action allowFullScreen allowTransparency alt async autoComplete autoFocus autoPlay cellPadding cellSpacing charSet checked classID className colSpan cols content contentEditable contextMenu controls coords crossOrigin data dateTime defer dir disabled download draggable encType form formAction formEncType formMethod formNoValidate formTarget frameBorder headers height hidden high href hrefLang htmlFor httpEquiv icon id label lang list loop low manifest marginHeight marginWidth max maxLength media mediaGroup method min multiple muted name noValidate open optimum pattern placeholder poster preload radioGroup readOnly rel required role rowSpan rows sandbox scope scoped scrolling seamless selected shape size sizes span spellCheck src srcDoc srcSet start step style tabIndex target title type useMap value width wmode'.split(' ');
var ALLOWED_SVG_ATTRIBUTES = 'clipPath cx cy d dx dy fill fillOpacity fontFamily fillRule fontSize fx fy gradientTransform gradientUnits markerEnd markerMid markerStart offset opacity patternContentUnits patternUnits points preserveAspectRatio r rx ry spreadMethod stopColor stopOpacity stroke strokeDasharray strokeLinecap strokeOpacity strokeWidth textAnchor transform vectorEffect version viewBox xmlns x1 x2 x y1 y2 y xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'.split(' ');

var ALLOWED_ATTRIBUTES = ALLOWED_HTML_ATTRIBUTES.concat(ALLOWED_SVG_ATTRIBUTES);
var ALLOWED_TAGS = 'circle clipPath defs ellipse g image line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan title'.split(' ');

var DATA_ATTRIBUTE = /^data-/i;

exports.cssProperty = function(string) {
    var unprefixed = string.replace(/^-ms/, 'ms');

    return exports.camelCase(unprefixed);
};

exports.camelCase = function(string) {
    return string.replace(/(?:-|_)([a-z])/g, function(g) { return g[1].toUpperCase(); });
};

exports.processAttributeName = function(name) {
    return DATA_ATTRIBUTE.test(name) ? name : exports.camelCase(name);
};

exports.unnamespaceAttributeName = function(name) {
    return name.replace(/(\w+):(\w)/i, function(match, namespace, char) {
        return namespace + char.toUpperCase();
    });
};

exports.sanitizeAttributes = function(attributes) {
    if (!attributes) return null;

    if (attributes.class) {
        attributes.className = attributes.class;
        delete attributes.class;
    }

    var allowed = ALLOWED_ATTRIBUTES.reduce(function(hash, name) {
        if (name in attributes) {
            var unnamespacedName = exports.unnamespaceAttributeName(name);

            hash[unnamespacedName] = attributes[name];
        }

        return hash;
    }, {});

    var custom = Object.keys(attributes).filter(function(name) {
        return DATA_ATTRIBUTE.test(name);
    }).reduce(function(data, name) {
        data[name] = attributes[name];

        return data;
    }, {});

    return assign(allowed, custom);
};

exports.sanitizeChildren = function(children) {
    if (!children) return null;

    return children.filter(function isTagAllowed(child) {
        return ALLOWED_TAGS.indexOf(child.tagName) !== -1;
    });
};

exports.styleAttribute = function(string) {
    var object = string.split(/\s*;\s*/g).reduce(function(hash, keyValue) {
        var split = keyValue.split(/\s*\:\s*/);
        var key = exports.camelCase((split[0] || '').trim());
        var value = (split[1] || '').trim();

        hash[key] = value;

        return hash;
    }, {});

    return JSON.stringify(object);
};

exports.forEach = function(element, callback) {
    element.children && element.children.forEach(function(child) {
        exports.forEach(child, callback);
    });

    callback(element);
};

exports.filter = function(element, test) {
    var filtered = [];

    exports.forEach(element, function(child) {
        if (test(child)) filtered.push(child);
    });

    return filtered;
};

exports.findById = function(element, id) {
    return exports.filter(element, function(node) {
        return node.attributes.id === id;
    }).shift() || null;
};

exports.supportsAllAttributes = function(element) {
    var hasHyphen = element.tagName.indexOf('-') !== -1;
    var hasIsAttribute = 'is' in element.attributes;

    return hasHyphen || hasIsAttribute;
};
<commit_msg>Added mask to allowed_attributes<commit_after>'use strict';

var assign = require('object-assign');

var ALLOWED_HTML_ATTRIBUTES = 'accept acceptCharset accessKey action allowFullScreen allowTransparency alt async autoComplete autoFocus autoPlay cellPadding cellSpacing charSet checked classID className colSpan cols content contentEditable contextMenu controls coords crossOrigin data dateTime defer dir disabled download draggable encType form formAction formEncType formMethod formNoValidate formTarget frameBorder headers height hidden high href hrefLang htmlFor httpEquiv icon id label lang list loop low manifest marginHeight marginWidth max maxLength media mediaGroup method min multiple muted name noValidate open optimum pattern placeholder poster preload radioGroup readOnly rel required role rowSpan rows sandbox scope scoped scrolling seamless selected shape size sizes span spellCheck src srcDoc srcSet start step style tabIndex target title type useMap value width wmode'.split(' ');
var ALLOWED_SVG_ATTRIBUTES = 'clipPath cx cy d dx dy fill fillOpacity fontFamily fillRule fontSize fx fy gradientTransform gradientUnits markerEnd markerMid markerStart offset opacity patternContentUnits patternUnits points preserveAspectRatio r rx ry spreadMethod stopColor stopOpacity stroke strokeDasharray strokeLinecap strokeOpacity strokeWidth textAnchor transform vectorEffect version viewBox xmlns x1 x2 x y1 y2 y xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space mask'.split(' ');

var ALLOWED_ATTRIBUTES = ALLOWED_HTML_ATTRIBUTES.concat(ALLOWED_SVG_ATTRIBUTES);
var ALLOWED_TAGS = 'circle clipPath defs ellipse g image line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan title'.split(' ');

var DATA_ATTRIBUTE = /^data-/i;

exports.cssProperty = function(string) {
    var unprefixed = string.replace(/^-ms/, 'ms');

    return exports.camelCase(unprefixed);
};

exports.camelCase = function(string) {
    return string.replace(/(?:-|_)([a-z])/g, function(g) { return g[1].toUpperCase(); });
};

exports.processAttributeName = function(name) {
    return DATA_ATTRIBUTE.test(name) ? name : exports.camelCase(name);
};

exports.unnamespaceAttributeName = function(name) {
    return name.replace(/(\w+):(\w)/i, function(match, namespace, char) {
        return namespace + char.toUpperCase();
    });
};

exports.sanitizeAttributes = function(attributes) {
    if (!attributes) return null;

    if (attributes.class) {
        attributes.className = attributes.class;
        delete attributes.class;
    }

    var allowed = ALLOWED_ATTRIBUTES.reduce(function(hash, name) {
        if (name in attributes) {
            var unnamespacedName = exports.unnamespaceAttributeName(name);

            hash[unnamespacedName] = attributes[name];
        }

        return hash;
    }, {});

    var custom = Object.keys(attributes).filter(function(name) {
        return DATA_ATTRIBUTE.test(name);
    }).reduce(function(data, name) {
        data[name] = attributes[name];

        return data;
    }, {});

    return assign(allowed, custom);
};

exports.sanitizeChildren = function(children) {
    if (!children) return null;

    return children.filter(function isTagAllowed(child) {
        return ALLOWED_TAGS.indexOf(child.tagName) !== -1;
    });
};

exports.styleAttribute = function(string) {
    var object = string.split(/\s*;\s*/g).reduce(function(hash, keyValue) {
        var split = keyValue.split(/\s*\:\s*/);
        var key = exports.camelCase((split[0] || '').trim());
        var value = (split[1] || '').trim();

        hash[key] = value;

        return hash;
    }, {});

    return JSON.stringify(object);
};

exports.forEach = function(element, callback) {
    element.children && element.children.forEach(function(child) {
        exports.forEach(child, callback);
    });

    callback(element);
};

exports.filter = function(element, test) {
    var filtered = [];

    exports.forEach(element, function(child) {
        if (test(child)) filtered.push(child);
    });

    return filtered;
};

exports.findById = function(element, id) {
    return exports.filter(element, function(node) {
        return node.attributes.id === id;
    }).shift() || null;
};

exports.supportsAllAttributes = function(element) {
    var hasHyphen = element.tagName.indexOf('-') !== -1;
    var hasIsAttribute = 'is' in element.attributes;

    return hasHyphen || hasIsAttribute;
};
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs');

class MyBrowserWindow extends global.electron.BrowserWindow {
  constructor (opts) {
    let size = global.mainWindow.getSize();
    super(Object.assign({
      width: size[0],
      height: size[1],
      webPreferences: {
        enableRemoteModule: true,
        nativeWindowOpen: true,
        preload: global.preloadPath
      },
    }, opts));
  }
}

exports.newWindow = function (e, url, frame, dis, opts) {
  if (config.store.get(config.names.openBrowser)) {
    e.preventDefault();
    global.electron.shell.openExternal(url);
  } else {
    e['newGuest'] = new MyBrowserWindow();
    e['newGuest'].webContents.on('new-window', exports.newWindow);
    e['newGuest'].loadURL(url);
    e.preventDefault();
  }
};

exports.windowAllClosed = function () {
  app.quit();
};

exports.hideToTray = function (action, event) {
  if (action == 'minimize' && config.store.get(config.names.minToTray)) {
    event.sender.hide();
  }

  if (action == 'close' && !config.store.get(config.names.minToTray)) {
    event.sender.hide();
    event.preventDefault();
  }
};

exports.beforeQuit = function () {
  global.electron.globalShortcut.unregisterAll();
};

exports.getIconPath = function () {
  var iconExt = null;
  switch (process.platform) {
    case 'darwin':
      iconExt = '.icns';
      break;
    case 'linux':
      iconExt = '.png';
      break;
    case 'win32':
    default:
      iconExt = '.ico';
      break;

  }
  return __dirname + '/favicon' + iconExt;
};

exports.handleRedirections = function () {
  // Related: https://github.com/electron/electron/issues/3471#issuecomment-236368409
  if (arguments[3]) {
    setTimeout(()=>this.loadURL(arguments[2]), 100);
    arguments[0].preventDefault();
  }
};

exports.loadUserCss = function () {
  var filePath = path.join(__dirname, './user.css');
  fs.readFile(filePath, (err, data) => !err && this.insertCSS(data.toString()));
};

exports.getAboutMessage = function (appName, appVersion) {
  let { electron, chrome } = process.versions;
  return appName + ' version: ' + appVersion + '\n' +
    'Electron version: ' + electron + '\n' +
    'Chrome version: ' + chrome;
};

exports.updateState = function () {
    // Don't throw an error when window was closed
    try {
      const winBounds = mainWindow.getNormalBounds();
      config.store.set(config.names.x, winBounds.x);
      config.store.set(config.names.y, winBounds.y);
      config.store.set(config.names.width, winBounds.width);
      config.store.set(config.names.height, winBounds.height);
    } catch (err) {}
}
<commit_msg>Use parent window's bounds<commit_after>const path = require('path');
const fs = require('fs');

class MyBrowserWindow extends global.electron.BrowserWindow {
  constructor (opts) {
    super(Object.assign({
      webPreferences: {
        enableRemoteModule: true,
        nativeWindowOpen: true,
        preload: global.preloadPath
      },
    }, opts));
  }
}

exports.newWindow = function (e, url, frame, dis, opts) {
  if (config.store.get(config.names.openBrowser)) {
    e.preventDefault();
    global.electron.shell.openExternal(url);
  } else {
    e.preventDefault();
    const parentWin = global.electron.BrowserWindow.fromWebContents(e.sender),
      parentBounds = parentWin.getNormalBounds();
    const win = new MyBrowserWindow({
      show: false,
      parentWindow: parentWin,
      width: parentBounds.width,
      height: parentBounds.height,
      x: parentBounds.x,
      y: parentBounds.y,
    });
    e.newGuest = win;
    win.webContents.on('new-window', exports.newWindow);
    win.loadURL(url);
    win.once('ready-to-show', () => {
      if (parentWin.isMaximized()) {
        win.maximize();
      }
      win.show();
    });
  }
};

exports.windowAllClosed = function () {
  app.quit();
};

exports.hideToTray = function (action, event) {
  if (action == 'minimize' && config.store.get(config.names.minToTray)) {
    event.sender.hide();
  }

  if (action == 'close' && !config.store.get(config.names.minToTray)) {
    event.sender.hide();
    event.preventDefault();
  }
};

exports.beforeQuit = function () {
  global.electron.globalShortcut.unregisterAll();
};

exports.getIconPath = function () {
  var iconExt = null;
  switch (process.platform) {
    case 'darwin':
      iconExt = '.icns';
      break;
    case 'linux':
      iconExt = '.png';
      break;
    case 'win32':
    default:
      iconExt = '.ico';
      break;

  }
  return __dirname + '/favicon' + iconExt;
};

exports.handleRedirections = function () {
  // Related: https://github.com/electron/electron/issues/3471#issuecomment-236368409
  if (arguments[3]) {
    setTimeout(()=>this.loadURL(arguments[2]), 100);
    arguments[0].preventDefault();
  }
};

exports.loadUserCss = function () {
  var filePath = path.join(__dirname, './user.css');
  fs.readFile(filePath, (err, data) => !err && this.insertCSS(data.toString()));
};

exports.getAboutMessage = function (appName, appVersion) {
  let { electron, chrome } = process.versions;
  return appName + ' version: ' + appVersion + '\n' +
    'Electron version: ' + electron + '\n' +
    'Chrome version: ' + chrome;
};

exports.updateState = function () {
    // Don't throw an error when window was closed
    try {
      const winBounds = mainWindow.getNormalBounds();
      config.store.set(config.names.x, winBounds.x);
      config.store.set(config.names.y, winBounds.y);
      config.store.set(config.names.width, winBounds.width);
      config.store.set(config.names.height, winBounds.height);
    } catch (err) {}
}
<|endoftext|>


<commit_before>/**
 * Created by Aaron on 7/3/2015.
 */

import Promise from 'bluebird';

let yieldHandlers = [];

export function isThenable( obj ) {
    return obj && typeof obj.then === 'function';
}

export let isPromise = isThenable;

export function isGenerator( obj ) {
    return 'function' === typeof obj.next && 'function' === typeof obj.throw;
}

export function isGeneratorFunction( obj ) {
    if( !obj.constructor ) {
        return false;

    } else if( 'GeneratorFunction' === obj.constructor.name ||
               'GeneratorFunction' === obj.constructor.displayName ) {
        return true;

    } else {
        return isGenerator( obj.constructor.prototype );
    }
}

function objectToPromise( obj ) {
    let keys = Object.keys( obj );
    let length = keys.length;

    let results = new obj.constructor();
    let promises = new Array( length );

    let i = -1;

    while( ++i < length ) {
        let key = keys[i];

        let value = obj[key];

        let promise = toPromise.call( this, value );

        if( isThenable( promise ) ) {
            results[key] = void 0;

            promises[i] = promise.then( res => results[key] = res );

        } else {
            results[key] = value;
        }
    }

    return Promise.all( promises ).return( results );
}

function resolveGenerator( gen ) {
    return new Promise( ( resolve, reject ) => {
        function next( ret ) {
            if( ret.done ) {
                return resolve( ret.value );

            } else {
                let value = toPromise.call( this, ret.value );

                if( isThenable( value ) ) {
                    return value.then( onFulfilled, onRejected );

                } else {
                    return onRejected( new TypeError( `You may only yield a function, promise, generator, array, or object, but the following object was passed: "${ret.value}"` ) );
                }
            }
        }

        function onFulfilled( res ) {
            try {
                next( gen.next( res ) );

            } catch( e ) {
                return reject( e );
            }
        }

        function onRejected( err ) {
            try {
                next( gen.throw( err ) );

            } catch( e ) {
                return reject( e );
            }
        }

        onFulfilled();
    } );
}

function arrayToPromise( value ) {
    let length = value.length;

    let results = new Array( length );
    let i = -1;

    while( ++i < length ) {
        results[i] = toPromise.call( this, value[i] );
    }

    return Promise.all( results );
}

function toPromise( value ) {
    if( isThenable( value ) ) {
        return value;

    } else if( Array.isArray( value ) ) {
        return arrayToPromise.call( this, value );

    } else if( !!value && typeof value === 'object' ) {
        if( isGenerator( value ) ) {
            return resolveGenerator.call( this, value );

        } else if( Object === value.constructor || !value.constructor ) {
            return objectToPromise.call( this, value );

        } else {
            let i = -1;
            let length = yieldHandlers.length;

            while( ++i < length ) {
                let handler = yieldHandlers[i];

                let res = handler.call( this, value );

                if( isThenable( res ) ) {
                    return res;
                }
            }
        }

    } else if( typeof value === 'function' ) {
        if( isGeneratorFunction( value ) ) {
            return Promise.coroutine( value ).call( this );

        } else {
            //Thunks
            return new Promise( ( resolve, reject ) => {
                try {
                    value.call( this, ( err, ...res ) => {
                        if( err ) {
                            reject( err );

                        } else if( res.length > 1 ) {
                            resolve( res );

                        } else {
                            resolve( res[0] );
                        }
                    } );

                } catch( err ) {
                    reject( err );
                }
            } );
        }

    } else {
        let i = -1;
        let length = yieldHandlers.length;

        while( ++i < length ) {
            let handler = yieldHandlers[i];

            let res = handler.call( this, value );

            if( isThenable( res ) ) {
                return res;
            }
        }
    }

    return value;
}

export function addYieldHandler( handler ) {
    if( typeof handler !== 'function' ) {
        throw new TypeError( 'yield handler is not a function' );

    } else {
        yieldHandlers.push( handler );
    }
}

let addedYieldHandler = false;

if( !addedYieldHandler ) {
    Promise.coroutine.addYieldHandler( toPromise );

    addedYieldHandler = true;
}

export default {
    addYieldHandler,
    isThenable,
    isPromise,
    isGenerator,
    isGeneratorFunction
};
<commit_msg>src: More attempts at optimization.<commit_after>/**
 * Created by Aaron on 7/3/2015.
 */

import Bluebird from 'bluebird';

let Promise = Bluebird;

let yieldHandlers = [];

export function isThenable( obj ) {
    return obj && typeof obj.then === 'function';
}

export let isPromise = isThenable;

export function isGenerator( obj ) {
    return 'function' === typeof obj.next && 'function' === typeof obj.throw;
}

export function isGeneratorFunction( obj ) {
    if( !obj.constructor ) {
        return false;

    } else if( 'GeneratorFunction' === obj.constructor.name ||
               'GeneratorFunction' === obj.constructor.displayName ) {
        return true;

    } else {
        return isGenerator( obj.constructor.prototype );
    }
}

/*
 * This is a deviation from tj/co because bluebird supports more.
 *
 * Basically, bluebird's Promise.all can take an array of BOTH values and promises, so I said screw the closure that
 * required binding local variables every iteration of this loop, and went with a pure array approach.
 *
 * */
function objectToPromise( obj ) {
    let keys = Object.keys( obj );
    let length = keys.length;

    let results = new obj.constructor();
    let promises = new Array( length );

    let i = -1;

    while( ++i < length ) {
        let key = keys[i];

        results[key] = void 0;

        promises[i] = toPromise.call( this, obj[key] );
    }

    return Promise.all( promises ).then( res => {
        let i = -1;

        while( ++i < length ) {
            results[keys[i]] = res[i];
        }

        return results;
    } );
}

function resolveGenerator( gen ) {
    return new Promise( ( resolve, reject ) => {
        function next( ret ) {
            if( ret.done ) {
                return resolve( ret.value );

            } else {
                let value = toPromise.call( this, ret.value );

                if( isThenable( value ) ) {
                    return value.then( onFulfilled, onRejected );

                } else {
                    return onRejected( new TypeError( `You may only yield a function, promise, generator, array, or object, but the following object was passed: "${ret.value}"` ) );
                }
            }
        }

        function onFulfilled( res ) {
            try {
                next( gen.next( res ) );

            } catch( e ) {
                return reject( e );
            }
        }

        function onRejected( err ) {
            try {
                next( gen.throw( err ) );

            } catch( e ) {
                return reject( e );
            }
        }

        onFulfilled();
    } );
}

function arrayToPromise( value ) {
    let length = value.length;

    let results = new Array( length );
    let i = -1;

    while( ++i < length ) {
        results[i] = toPromise.call( this, value[i] );
    }

    return Promise.all( results );
}

function thunkToPromise( value ) {
    return new Promise( ( resolve, reject ) => {
        try {
            value.call( this, function( err ) {
                if( err ) {
                    reject( err );

                } else if( arguments.length > 2 ) {
                    let length = arguments.length - 1;
                    let res = new Array( length );
                    let i = -1;

                    while( ++i < length ) {
                        res[i] = arguments[i + 1];
                    }

                    resolve( res );

                } else {
                    resolve( arguments[1] );
                }
            } );

        } catch( err ) {
            reject( err );
        }
    } );
}

function toPromise( value ) {
    switch( typeof value ) {
        case 'function':
        {
            if( isGeneratorFunction( value ) ) {
                return Promise.coroutine( value ).call( this );

            } else {
                return thunkToPromise.call( this, value );
            }
        }
        case 'object':
        {
            if( isThenable( value ) ) {
                return value;

            } else if( !value ) {
                return value;

            } else if( Array.isArray( value ) ) {
                return arrayToPromise.call( this, value );

            } else if( isGenerator( value ) ) {
                return resolveGenerator.call( this, value );

            } else if( Object === value.constructor ) {
                return objectToPromise.call( this, value );
            }
        }
        default:
        {
            let i = -1;
            let length = yieldHandlers.length;

            while( ++i < length ) {
                let handler = yieldHandlers[i];

                let res = handler.call( this, value );

                if( isThenable( res ) ) {
                    return res;
                }
            }
        }
        case 'undefined':
        {
            return value;
        }
    }
}

export function addYieldHandler( handler ) {
    if( typeof handler !== 'function' ) {
        throw new TypeError( 'yield handler is not a function' );

    } else {
        yieldHandlers.push( handler );
    }
}

let addedYieldHandler = false;

if( !addedYieldHandler ) {
    Promise.coroutine.addYieldHandler( toPromise );

    addedYieldHandler = true;
}

export default {
    addYieldHandler,
    isThenable,
    isPromise,
    isGenerator,
    isGeneratorFunction
};
<|endoftext|>


<commit_before>var assert = require('assert')

// exports.createSeed = function(rand){
//   assert(rand,'requires rand')
//   return rand.uuid4()
// }

exports.createSeed = function(len,possible){
    var text = "";
    len = len || 32
    possible = possible || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghipqrstuvwxyz0123456789";

    for (var i = 0; i < len; i++)
      text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}

exports.sha256 = function(crypto,seed){
  assert(crypto,'requires crypto')
  try{
    return crypto.createHash('sha256').update(seed).digest('hex');
  }catch(e){
    return crypto.SHA256(seed).toString()
  }
}

exports.rehash = function(crypto,hash,seed){
  assert(crypto,'requires crypto')
  assert(hash,'requires hash')
  if(seed == null) return hash
  try{
    return crypto.createHmac('sha256',hash).update(seed).digest('hex')
  }catch(e){
    var hmacHasher = crypto.algo.HMAC.create(crypto.algo.SHA256, hash);
    return hmacHasher.finalize(seed).toString();
  }
}

var defaultHex = 8
var defaultMaxInt = Math.pow(2,8*4)

function calcMaxInt(maxHex){
  maxHex = maxHex || defaultHex
  if(maxHex == defaultHex) return defaultMaxInt
  return Math.pow(2,maxHex*4)
}

//reads maxHex characters from end of hash ( least significant bits)
exports.toInt = function(hash,maxHex,mostSig){
  assert(hash,'requires hash')
  maxHex = maxHex || defaultHex
  assert(hash.length >= maxHex,'hash is not long enough')
  if(mostSig){
    //most signifiacate part of hash is used
    return parseInt(hash.slice(0,maxHex),16)
  }else{
    //lease significant part of hash is used
    return parseInt(hash.slice(-maxHex),16)
  }
}

exports.toFloat = function(hash,min,max,inclusive,maxHex,mostSig){
  assert(hash,'requires hash')
  if(min == null) min = 0
  if(max == null) max = 1

  var maxInt = calcMaxInt(maxHex)
  var integer = exports.toInt(hash, maxHex, mostSig)
  var scale = max - min
  var limit = inclusive ? maxInt - 1 : maxInt

  return min + scale * (integer/limit)
}

exports.toBool = function(hash,percent,maxHex,mostSig){
  if(percent == null) percent = .5
  var num = exports.toFloat(hash,0,1,false,maxHex,mostSig)
  return num < percent
}

exports.generate = function(crypto,count, seed){
  assert(seed,'requires seed value')
  count = count || 1
  var result = Array(count)
  for(var i = count-1; i >= 0; i--){
    seed = exports.sha256(crypto,seed)
    result[i]= seed
  }
  return result
}
<commit_msg>increase seed<commit_after>var assert = require('assert')

// exports.createSeed = function(rand){
//   assert(rand,'requires rand')
//   return rand.uuid4()
// }

exports.createSeed = function(len,possible){
    var text = "";
    len = len || 256
    possible = possible || " -_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghipqrstuvwxyz0123456789";

    for (var i = 0; i < len; i++)
      text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
}

exports.sha256 = function(crypto,seed){
  assert(crypto,'requires crypto')
  try{
    return crypto.createHash('sha256').update(seed).digest('hex');
  }catch(e){
    return crypto.SHA256(seed).toString()
  }
}

exports.rehash = function(crypto,hash,seed){
  assert(crypto,'requires crypto')
  assert(hash,'requires hash')
  if(seed == null) return hash
  try{
    return crypto.createHmac('sha256',hash).update(seed).digest('hex')
  }catch(e){
    var hmacHasher = crypto.algo.HMAC.create(crypto.algo.SHA256, hash);
    return hmacHasher.finalize(seed).toString();
  }
}

var defaultHex = 8
var defaultMaxInt = Math.pow(2,8*4)

function calcMaxInt(maxHex){
  maxHex = maxHex || defaultHex
  if(maxHex == defaultHex) return defaultMaxInt
  return Math.pow(2,maxHex*4)
}

//reads maxHex characters from end of hash ( least significant bits)
exports.toInt = function(hash,maxHex,mostSig){
  assert(hash,'requires hash')
  maxHex = maxHex || defaultHex
  assert(hash.length >= maxHex,'hash is not long enough')
  if(mostSig){
    //most signifiacate part of hash is used
    return parseInt(hash.slice(0,maxHex),16)
  }else{
    //lease significant part of hash is used
    return parseInt(hash.slice(-maxHex),16)
  }
}

exports.toFloat = function(hash,min,max,inclusive,maxHex,mostSig){
  assert(hash,'requires hash')
  if(min == null) min = 0
  if(max == null) max = 1

  var maxInt = calcMaxInt(maxHex)
  var integer = exports.toInt(hash, maxHex, mostSig)
  var scale = max - min
  var limit = inclusive ? maxInt - 1 : maxInt

  return min + scale * (integer/limit)
}

exports.toBool = function(hash,percent,maxHex,mostSig){
  if(percent == null) percent = .5
  var num = exports.toFloat(hash,0,1,false,maxHex,mostSig)
  return num < percent
}

exports.generate = function(crypto,count, seed){
  assert(seed,'requires seed value')
  count = count || 1
  var result = Array(count)
  for(var i = count-1; i >= 0; i--){
    seed = exports.sha256(crypto,seed)
    result[i]= seed
  }
  return result
}
<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var Color = {
    White: 0,
    Black: 1
};

var Orientation = {
    Up: 0,
    Left: 1,
    Right: 2
};

var Ant = function() {

    var position = {x: 0, y: 0};
    var orientation = Orientation.Up;

    this.position = function() {
	return position;
    };

    this.orientation = function() {
	return orientation;
    };

    this.left = function() {

	var l = {};
	l[Orientation.Up] = Orientation.Left;
	l[Orientation.Left] =  Orientation.Down;
	l[Orientation.Down] =  Orientation.Right;
	l[Orientation.Right] =  Orientation.Up;

	orientation = l[orientation];
    };

    this.right = function() {

	var r = {};
	r[Orientation.Up] = Orientation.Right;
	r[Orientation.Left] =  Orientation.Up;
	r[Orientation.Down] =  Orientation.Left;
	r[Orientation.Right] =  Orientation.Down;

	orientation = r[orientation];
    };

    this.move = function() {
	switch (orientation) {
	    case Orientation.Up: position.y += 1; break;
	    case Orientation.Left: position.x -= 1; break;
	    case Orientation.Down: position.y -= 1; break;
	    case Orientation.Right: position.x += 1; break;
	}
    };

    return this;
};

var makeAnt = function() {
    return new Ant();
};

var eqPoint = function(p1,p2) {
    return p1.x === p2.x && p1.y === p2.y;
};

var Game = function() {
    var ant = makeAnt();

    var blackSquares = [];

    this.color = function(key) {
	var n = blackSquares.length;
	for (var i=0;i<n;++i)
	    if (eqPoint(blackSquares[i],key))
		return Color.Black;

	return Color.White;
    };

    this.ant = function() {
	return ant;
    };

    this.flipAntSquareColor = function() {
	var isWhite = this.color(ant.position()) === Color.White;
	if (isWhite) {
	    blackSquares.push({
		x: ant.position().x,
		y: ant.position().y
	    });
	    assert(this.color(ant.position()) === Color.Black);
	} else {
	    var n = blackSquares.length;
	    blackSquares = blackSquares.filter(function(x) {
		return !eqPoint(x, ant.position());
	    });
	    assert.equal(n - 1, blackSquares.length);
	    assert(this.color(ant.position()) === Color.White);
	}
    };

    this.step = function() {
	if (this.color(ant.position()) === Color.White) {
	    this.flipAntSquareColor();
	    ant.right();
	} else {
	    this.flipAntSquareColor();
	    ant.left();
	}

	ant.move();
    };

    this.width = function() {
	var minX = blackSquares[0].x,
	    maxX = blackSquares[0].x;

	for (var i=0;i<blackSquares.length;++i) {
	    minX = Math.min(minX, blackSquares[i].x);
	    maxX = Math.max(maxX, blackSquares[i].x);
	}

	return maxX - minX;
    };

    this.height = function() {
	var minY = blackSquares[0].y,
	    maxY = blackSquares[0].y;

	for (var i=0;i<blackSquares.length;++i) {
	    minY = Math.min(minY, blackSquares[i].y);
	    maxY = Math.max(maxY, blackSquares[i].y);
	}

	return maxY - minY;
    };

    return this;
};

var makeGame = function() {
    return new Game();
};

describe('langton\'s ant', function() {

    describe('game', function() {
	it('exists', function() {
	    assert(makeGame());
	});

	it('records state', function() {
	    var game = makeGame();
	    assert.deepEqual({x: 0, y: 0}, game.ant().position());
	});

	it('steps ant orientation', function() {
	    var game = makeGame();
	    game.step();

	    assert.equal(Orientation.Right, game.ant().orientation());
	    assert.deepEqual({x: 1, y: 0}, game.ant().position());

	    game.step();
	    assert.equal(Orientation.Down, game.ant().orientation());
	    assert.deepEqual({x: 1, y: -1}, game.ant().position());
	});

	it('flips square color', function() {
	    var game = makeGame();
	    assert.equal(Color.White, game.color({x: 0, y: 0}));

	    game.step();
	    assert.equal(Color.Black, game.color({x: 0, y: 0}));
	});

	it('steps multiple times', function() {
	    var game = makeGame();
	    for (var i=0;i<200;++i)
		game.step();

	    assert.equal(8, game.width());
	    assert.equal(8, game.height());
	});
    });

    describe('ant', function() {
	it('exists', function() {
	    assert(makeAnt());
	});

	it('has a location', function() {
	    var ant = makeAnt();
	    assert.equal(0, ant.position().x);
	    assert.equal(0, ant.position().y);
	});

	it('orientation up', function() {
	    var ant = makeAnt();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('moves', function() {
	    var ant = makeAnt();
	    ant.move();
	    assert.equal(0, ant.position().x);
	    assert.equal(1, ant.position().y);

	    ant.left(); ant.move();
	    assert.equal(-1, ant.position().x);

	    ant.left(); ant.move();
	    assert.equal(0, ant.position().y);

	    ant.left(); ant.move();
	    assert.equal(0, ant.position().x);
	});

	it('turns left', function() {
	    var ant = makeAnt();
	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Up, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());
	});

	it('turns right', function() {
	    var ant = makeAnt();
	    ant.right();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Up, ant.orientation());
	});
    });
});
<commit_msg>does it export to a png<commit_after>"use strict";

var assert = require('assert');

var Color = {
    White: 0,
    Black: 1
};

var Orientation = {
    Up: 0,
    Left: 1,
    Right: 2
};

var Ant = function() {

    var position = {x: 0, y: 0};
    var orientation = Orientation.Up;

    this.position = function() {
	return position;
    };

    this.orientation = function() {
	return orientation;
    };

    this.left = function() {

	var l = {};
	l[Orientation.Up] = Orientation.Left;
	l[Orientation.Left] =  Orientation.Down;
	l[Orientation.Down] =  Orientation.Right;
	l[Orientation.Right] =  Orientation.Up;

	orientation = l[orientation];
    };

    this.right = function() {

	var r = {};
	r[Orientation.Up] = Orientation.Right;
	r[Orientation.Left] =  Orientation.Up;
	r[Orientation.Down] =  Orientation.Left;
	r[Orientation.Right] =  Orientation.Down;

	orientation = r[orientation];
    };

    this.move = function() {
	switch (orientation) {
	    case Orientation.Up: position.y += 1; break;
	    case Orientation.Left: position.x -= 1; break;
	    case Orientation.Down: position.y -= 1; break;
	    case Orientation.Right: position.x += 1; break;
	}
    };

    return this;
};

var makeAnt = function() {
    return new Ant();
};

var eqPoint = function(p1,p2) {
    return p1.x === p2.x && p1.y === p2.y;
};

var Game = function() {
    var ant = makeAnt();

    var blackSquares = [];

    this.color = function(key) {
	var n = blackSquares.length;
	for (var i=0;i<n;++i)
	    if (eqPoint(blackSquares[i],key))
		return Color.Black;

	return Color.White;
    };

    this.ant = function() {
	return ant;
    };

    this.flipAntSquareColor = function() {
	var isWhite = this.color(ant.position()) === Color.White;
	if (isWhite) {
	    blackSquares.push({
		x: ant.position().x,
		y: ant.position().y
	    });
	    assert(this.color(ant.position()) === Color.Black);
	} else {
	    var n = blackSquares.length;
	    blackSquares = blackSquares.filter(function(x) {
		return !eqPoint(x, ant.position());
	    });
	    assert.equal(n - 1, blackSquares.length);
	    assert(this.color(ant.position()) === Color.White);
	}
    };

    this.step = function() {
	if (this.color(ant.position()) === Color.White) {
	    this.flipAntSquareColor();
	    ant.right();
	} else {
	    this.flipAntSquareColor();
	    ant.left();
	}

	ant.move();
    };

    this.width = function() {
	var minX = blackSquares[0].x,
	    maxX = blackSquares[0].x;

	for (var i=0;i<blackSquares.length;++i) {
	    minX = Math.min(minX, blackSquares[i].x);
	    maxX = Math.max(maxX, blackSquares[i].x);
	}

	return maxX - minX;
    };

    this.height = function() {
	var minY = blackSquares[0].y,
	    maxY = blackSquares[0].y;

	for (var i=0;i<blackSquares.length;++i) {
	    minY = Math.min(minY, blackSquares[i].y);
	    maxY = Math.max(maxY, blackSquares[i].y);
	}

	return maxY - minY;
    };

    return this;
};

var makeGame = function() {
    return new Game();
};

describe('langton\'s ant', function() {

    describe('game', function() {
	it('exists', function() {
	    assert(makeGame());
	});

	it('records state', function() {
	    var game = makeGame();
	    assert.deepEqual({x: 0, y: 0}, game.ant().position());
	});

	it('steps ant orientation', function() {
	    var game = makeGame();
	    game.step();

	    assert.equal(Orientation.Right, game.ant().orientation());
	    assert.deepEqual({x: 1, y: 0}, game.ant().position());

	    game.step();
	    assert.equal(Orientation.Down, game.ant().orientation());
	    assert.deepEqual({x: 1, y: -1}, game.ant().position());
	});

	it('flips square color', function() {
	    var game = makeGame();
	    assert.equal(Color.White, game.color({x: 0, y: 0}));

	    game.step();
	    assert.equal(Color.Black, game.color({x: 0, y: 0}));
	});

	it('steps multiple times', function() {
	    var game = makeGame();
	    for (var i=0;i<200;++i)
		game.step();

	    assert.equal(8, game.width());
	    assert.equal(8, game.height());
	});

	it('exports to png', function() {
	    var game = makeGame();
	    for (var i=0;i<200;++i) game.step();

	    game.export('foo.png', function() {
		var exists = false;
		fs.exists('foo.png', function(e) {
		    exists = e;
		});
		assert(exists);
		fs.unlinkSync('foo.png');
	    });
	});
    });

    describe('ant', function() {
	it('exists', function() {
	    assert(makeAnt());
	});

	it('has a location', function() {
	    var ant = makeAnt();
	    assert.equal(0, ant.position().x);
	    assert.equal(0, ant.position().y);
	});

	it('orientation up', function() {
	    var ant = makeAnt();
	    assert.equal(Orientation.Up, ant.orientation());
	});

	it('moves', function() {
	    var ant = makeAnt();
	    ant.move();
	    assert.equal(0, ant.position().x);
	    assert.equal(1, ant.position().y);

	    ant.left(); ant.move();
	    assert.equal(-1, ant.position().x);

	    ant.left(); ant.move();
	    assert.equal(0, ant.position().y);

	    ant.left(); ant.move();
	    assert.equal(0, ant.position().x);
	});

	it('turns left', function() {
	    var ant = makeAnt();
	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Up, ant.orientation());

	    ant.left();
	    assert.equal(Orientation.Left, ant.orientation());
	});

	it('turns right', function() {
	    var ant = makeAnt();
	    ant.right();
	    assert.equal(Orientation.Right, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Down, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Left, ant.orientation());

	    ant.right();
	    assert.equal(Orientation.Up, ant.orientation());
	});
    });
});
<|endoftext|>


<commit_before>'use strict';
/**
 * parse {.class #id key=val} strings
 * @param {string} str: string to parse
 * @param {int} start: where to start parsing (including {)
 * @returns {2d array}: [['key', 'val'], ['class', 'red']]
 */
exports.getAttrs = function (str, start, options) {
  // not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign
  const allowedKeyChars = /[^\t\n\f />"'=]/;
  const pairSeparator = ' ';
  const keySeparator = '=';
  const classChar = '.';
  const idChar = '#';

  const attrs = [];
  let key = '';
  let value = '';
  let parsingKey = true;
  let valueInsideQuotes = false;

  // read inside {}
  // start + left delimiter length to avoid beginning {
  // breaks when } is found or end of string
  for (let i = start + options.leftDelimiter.length; i < str.length; i++) {
    if (str.slice(i, i + options.rightDelimiter.length) === options.rightDelimiter) {
      if (key !== '') { attrs.push([key, value]); }
      break;
    }
    let char_ = str.charAt(i);

    // switch to reading value if equal sign
    if (char_ === keySeparator && parsingKey) {
      parsingKey = false;
      continue;
    }

    // {.class} {..css-module}
    if (char_ === classChar && key === '') {
      if (str.charAt(i + 1) === classChar) {
        key = 'css-module';
        i += 1;
      } else {
        key = 'class';
      }
      parsingKey = false;
      continue;
    }

    // {#id}
    if (char_ === idChar && key === '') {
      key = 'id';
      parsingKey = false;
      continue;
    }

    // {value="inside quotes"}
    if (char_ === '"' && value === '') {
      valueInsideQuotes = true;
      continue;
    }
    if (char_ === '"' && valueInsideQuotes) {
      valueInsideQuotes = false;
      continue;
    }

    // read next key/value pair
    if ((char_ === pairSeparator && !valueInsideQuotes)) {
      if (key === '') {
        // beginning or ending space: { .red } vs {.red}
        continue;
      }
      attrs.push([key, value]);
      key = '';
      value = '';
      parsingKey = true;
      continue;
    }

    // continue if character not allowed
    if (parsingKey && char_.search(allowedKeyChars) === -1) {
      continue;
    }

    // no other conditions met; append to key/value
    if (parsingKey) {
      key += char_;
      continue;
    }
    value += char_;
  }

  if (options.allowedAttributes && options.allowedAttributes.length) {
    return attrs.filter(function (a) {
      let allowedAttributes = options.allowedAttributes;
      let attr = a[0];
      let allowed = false;
      for (let i = 0; i < allowedAttributes.length; i++) {
        let attrName = allowedAttributes[i];
        if (attrName === attr || (attrName instanceof RegExp && attrName.test(attr))) {
          allowed = true;
          break;
        }
      }
      return allowed;
    });
  } else {
    return attrs;
  }
};

/**
 * add attributes from [['key', 'val']] list
 * @param {array} attrs: [['key', 'val']]
 * @param {token} token: which token to add attributes
 * @returns token
 */
exports.addAttrs = function (attrs, token) {
  for (let j = 0, l = attrs.length; j < l; ++j) {
    let key = attrs[j][0];
    if (key === 'class') {
      token.attrJoin('class', attrs[j][1]);
    } else if (key === 'css-module') {
      token.attrJoin('css-module', attrs[j][1]);
    } else {
      token.attrPush(attrs[j]);
    }
  }
  return token;
};

/**
 * Does string have properly formatted curly?
 *
 * start: '{.a} asdf'
 * middle: 'a{.b}c'
 * end: 'asdf {.a}'
 * only: '{.a}'
 *
 * @param {string} where to expect {} curly. start, middle, end or only.
 * @return {function(string)} Function which testes if string has curly.
 */
exports.hasDelimiters = function (where, options) {

  if (!where) {
    throw new Error('Parameter `where` not passed. Should be "start", "middle", "end" or "only".');
  }

  /**
   * @param {string} str
   * @return {boolean}
   */
  return function (str) {
    // we need minimum three chars, for example {b}
    let minCurlyLength = options.leftDelimiter.length + 1 + options.rightDelimiter.length;
    if (!str || typeof str !== 'string' || str.length < minCurlyLength) {
      return false;
    }

    function validCurlyLength (curly) {
      let isClass = curly.charAt(options.leftDelimiter.length) === '.';
      let isId = curly.charAt(options.leftDelimiter.length) === '#';
      return (isClass || isId)
        ? curly.length >= (minCurlyLength + 1)
        : curly.length >= minCurlyLength;
    }

    let start, end, slice, nextChar;
    let rightDelimiterMinimumShift = minCurlyLength - options.rightDelimiter.length;
    switch (where) {
    case 'start':
      // first char should be {, } found in char 2 or more
      slice = str.slice(0, options.leftDelimiter.length);
      start = slice === options.leftDelimiter ? 0 : -1;
      end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, rightDelimiterMinimumShift);
      // check if next character is not one of the delimiters
      nextChar = str.charAt(end + options.rightDelimiter.length);
      if (nextChar && options.rightDelimiter.indexOf(nextChar) !== -1) {
        end = -1;
      }
      break;

    case 'end':
      // last char should be }
      start = str.lastIndexOf(options.leftDelimiter);
      end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + rightDelimiterMinimumShift);
      end = end === str.length - options.rightDelimiter.length ? end : -1;
      break;

    case 'only':
      // '{.a}'
      slice = str.slice(0, options.leftDelimiter.length);
      start = slice === options.leftDelimiter ? 0 : -1;
      slice = str.slice(str.length - options.rightDelimiter.length);
      end = slice === options.rightDelimiter ? str.length - options.rightDelimiter.length : -1;
      break;
    }

    return start !== -1 && end !== -1 && validCurlyLength(str.substring(start, end + options.rightDelimiter.length));
  };
};

/**
 * Removes last curly from string.
 */
exports.removeDelimiter = function (str, options) {
  const start = escapeRegExp(options.leftDelimiter);
  const end = escapeRegExp(options.rightDelimiter);

  let curly = new RegExp(
    '[ \\n]?' + start + '[^' + start + end + ']+' + end + '$'
  );
  let pos = str.search(curly);

  return pos !== -1 ? str.slice(0, pos) : str;
};

/**
 * Escapes special characters in string s such that the string
 * can be used in `new RegExp`. For example "[" becomes "\\[".
 *
 * @param {string} s Regex string.
 * @return {string} Escaped string.
 */
function escapeRegExp (s) {
  return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
exports.escapeRegExp = escapeRegExp;

/**
 * find corresponding opening block
 */
exports.getMatchingOpeningToken = function (tokens, i) {
  if (tokens[i].type === 'softbreak') {
    return false;
  }
  // non closing blocks, example img
  if (tokens[i].nesting === 0) {
    return tokens[i];
  }

  // inline tokens changes level on same token
  // that have .nesting +- 1
  let level = tokens[i].block
    ? tokens[i].level
    : tokens[i].level + 1;  // adjust for inline tokens

  let type = tokens[i].type.replace('_close', '_open');

  for (; i >= 0; --i) {
    if (tokens[i].type === type && tokens[i].level === level) {
      return tokens[i];
    }
  }
};

/**
 * from https://github.com/markdown-it/markdown-it/blob/master/lib/common/utils.js
 */
let HTML_ESCAPE_TEST_RE = /[&<>"]/;
let HTML_ESCAPE_REPLACE_RE = /[&<>"]/g;
let HTML_REPLACEMENTS = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"'
};

function replaceUnsafeChar(ch) {
  return HTML_REPLACEMENTS[ch];
}

exports.escapeHtml = function (str) {
  if (HTML_ESCAPE_TEST_RE.test(str)) {
    return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar);
  }
  return str;
};
<commit_msg>convert for loop to Array.prototype.some()<commit_after>'use strict';
/**
 * parse {.class #id key=val} strings
 * @param {string} str: string to parse
 * @param {int} start: where to start parsing (including {)
 * @returns {2d array}: [['key', 'val'], ['class', 'red']]
 */
exports.getAttrs = function (str, start, options) {
  // not tab, line feed, form feed, space, solidus, greater than sign, quotation mark, apostrophe and equals sign
  const allowedKeyChars = /[^\t\n\f />"'=]/;
  const pairSeparator = ' ';
  const keySeparator = '=';
  const classChar = '.';
  const idChar = '#';

  const attrs = [];
  let key = '';
  let value = '';
  let parsingKey = true;
  let valueInsideQuotes = false;

  // read inside {}
  // start + left delimiter length to avoid beginning {
  // breaks when } is found or end of string
  for (let i = start + options.leftDelimiter.length; i < str.length; i++) {
    if (str.slice(i, i + options.rightDelimiter.length) === options.rightDelimiter) {
      if (key !== '') { attrs.push([key, value]); }
      break;
    }
    let char_ = str.charAt(i);

    // switch to reading value if equal sign
    if (char_ === keySeparator && parsingKey) {
      parsingKey = false;
      continue;
    }

    // {.class} {..css-module}
    if (char_ === classChar && key === '') {
      if (str.charAt(i + 1) === classChar) {
        key = 'css-module';
        i += 1;
      } else {
        key = 'class';
      }
      parsingKey = false;
      continue;
    }

    // {#id}
    if (char_ === idChar && key === '') {
      key = 'id';
      parsingKey = false;
      continue;
    }

    // {value="inside quotes"}
    if (char_ === '"' && value === '') {
      valueInsideQuotes = true;
      continue;
    }
    if (char_ === '"' && valueInsideQuotes) {
      valueInsideQuotes = false;
      continue;
    }

    // read next key/value pair
    if ((char_ === pairSeparator && !valueInsideQuotes)) {
      if (key === '') {
        // beginning or ending space: { .red } vs {.red}
        continue;
      }
      attrs.push([key, value]);
      key = '';
      value = '';
      parsingKey = true;
      continue;
    }

    // continue if character not allowed
    if (parsingKey && char_.search(allowedKeyChars) === -1) {
      continue;
    }

    // no other conditions met; append to key/value
    if (parsingKey) {
      key += char_;
      continue;
    }
    value += char_;
  }

  if (options.allowedAttributes && options.allowedAttributes.length) {
    return attrs.filter(function (a) {
      let allowedAttributes = options.allowedAttributes;
      let attr = a[0];
      let allowed = false;

      allowed = allowedAttributes.some(function (attrName) {
        return attrName === attr || (attrName instanceof RegExp && attrName.test(attr));
      });

      return allowed;
    });
  } else {
    return attrs;
  }
};

/**
 * add attributes from [['key', 'val']] list
 * @param {array} attrs: [['key', 'val']]
 * @param {token} token: which token to add attributes
 * @returns token
 */
exports.addAttrs = function (attrs, token) {
  for (let j = 0, l = attrs.length; j < l; ++j) {
    let key = attrs[j][0];
    if (key === 'class') {
      token.attrJoin('class', attrs[j][1]);
    } else if (key === 'css-module') {
      token.attrJoin('css-module', attrs[j][1]);
    } else {
      token.attrPush(attrs[j]);
    }
  }
  return token;
};

/**
 * Does string have properly formatted curly?
 *
 * start: '{.a} asdf'
 * middle: 'a{.b}c'
 * end: 'asdf {.a}'
 * only: '{.a}'
 *
 * @param {string} where to expect {} curly. start, middle, end or only.
 * @return {function(string)} Function which testes if string has curly.
 */
exports.hasDelimiters = function (where, options) {

  if (!where) {
    throw new Error('Parameter `where` not passed. Should be "start", "middle", "end" or "only".');
  }

  /**
   * @param {string} str
   * @return {boolean}
   */
  return function (str) {
    // we need minimum three chars, for example {b}
    let minCurlyLength = options.leftDelimiter.length + 1 + options.rightDelimiter.length;
    if (!str || typeof str !== 'string' || str.length < minCurlyLength) {
      return false;
    }

    function validCurlyLength (curly) {
      let isClass = curly.charAt(options.leftDelimiter.length) === '.';
      let isId = curly.charAt(options.leftDelimiter.length) === '#';
      return (isClass || isId)
        ? curly.length >= (minCurlyLength + 1)
        : curly.length >= minCurlyLength;
    }

    let start, end, slice, nextChar;
    let rightDelimiterMinimumShift = minCurlyLength - options.rightDelimiter.length;
    switch (where) {
    case 'start':
      // first char should be {, } found in char 2 or more
      slice = str.slice(0, options.leftDelimiter.length);
      start = slice === options.leftDelimiter ? 0 : -1;
      end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, rightDelimiterMinimumShift);
      // check if next character is not one of the delimiters
      nextChar = str.charAt(end + options.rightDelimiter.length);
      if (nextChar && options.rightDelimiter.indexOf(nextChar) !== -1) {
        end = -1;
      }
      break;

    case 'end':
      // last char should be }
      start = str.lastIndexOf(options.leftDelimiter);
      end = start === -1 ? -1 : str.indexOf(options.rightDelimiter, start + rightDelimiterMinimumShift);
      end = end === str.length - options.rightDelimiter.length ? end : -1;
      break;

    case 'only':
      // '{.a}'
      slice = str.slice(0, options.leftDelimiter.length);
      start = slice === options.leftDelimiter ? 0 : -1;
      slice = str.slice(str.length - options.rightDelimiter.length);
      end = slice === options.rightDelimiter ? str.length - options.rightDelimiter.length : -1;
      break;
    }

    return start !== -1 && end !== -1 && validCurlyLength(str.substring(start, end + options.rightDelimiter.length));
  };
};

/**
 * Removes last curly from string.
 */
exports.removeDelimiter = function (str, options) {
  const start = escapeRegExp(options.leftDelimiter);
  const end = escapeRegExp(options.rightDelimiter);

  let curly = new RegExp(
    '[ \\n]?' + start + '[^' + start + end + ']+' + end + '$'
  );
  let pos = str.search(curly);

  return pos !== -1 ? str.slice(0, pos) : str;
};

/**
 * Escapes special characters in string s such that the string
 * can be used in `new RegExp`. For example "[" becomes "\\[".
 *
 * @param {string} s Regex string.
 * @return {string} Escaped string.
 */
function escapeRegExp (s) {
  return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
exports.escapeRegExp = escapeRegExp;

/**
 * find corresponding opening block
 */
exports.getMatchingOpeningToken = function (tokens, i) {
  if (tokens[i].type === 'softbreak') {
    return false;
  }
  // non closing blocks, example img
  if (tokens[i].nesting === 0) {
    return tokens[i];
  }

  // inline tokens changes level on same token
  // that have .nesting +- 1
  let level = tokens[i].block
    ? tokens[i].level
    : tokens[i].level + 1;  // adjust for inline tokens

  let type = tokens[i].type.replace('_close', '_open');

  for (; i >= 0; --i) {
    if (tokens[i].type === type && tokens[i].level === level) {
      return tokens[i];
    }
  }
};

/**
 * from https://github.com/markdown-it/markdown-it/blob/master/lib/common/utils.js
 */
let HTML_ESCAPE_TEST_RE = /[&<>"]/;
let HTML_ESCAPE_REPLACE_RE = /[&<>"]/g;
let HTML_REPLACEMENTS = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"'
};

function replaceUnsafeChar(ch) {
  return HTML_REPLACEMENTS[ch];
}

exports.escapeHtml = function (str) {
  if (HTML_ESCAPE_TEST_RE.test(str)) {
    return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar);
  }
  return str;
};
<|endoftext|>