<commit_before>/**
*   COMPARE TOOL
 */

var compare = function () {
    var $contEl = $("#selected_container"),
        $selEl = $contEl.find("#selected"),
        $pEl = $selEl.find("p"),
        selected = [],
        maps = [],
        selectToString = function (select) {
            var stat = select.state[select.map.getId()].mergedstats.toFixed(2);
            return "" + select.state.name + " (" + select.years.join(", ") + ") " + stat + "%";
        },
        selectToLi = function (select) {
            return "
" + selectToString(select) + "
";
        },
        render = function () {
            var o = "";
            $pEl.removeClass("pointer").unbind();
            if (selected.length === 0) {
                o += "Select states to compare.";
            } else {
                o += "You selected:
";
                o += $.map(selected, selectToLi).join("");
                o += "
";
                $pEl.addClass("pointer")
                    .on("click", compare);
            }
            $pEl.html(o);
            $contEl.animate({"opacity": 1}, 200);
        },
        compare = function () {
            var grey = $("
")
                    .addClass("modal-backdrop fade in")
                    .appendTo("body")
                    .on("click", close),
                choicecard = $("#howtocomparecard")
                    .delay(600)
                    .show()
                    .animate({ "opacity": 1}, 200),
                varcard = $("#comparevarcard"),
                gocard = $("#comparegocard"),
                gohide = [gocard, varcard, choicecard];

            function close() {
                $.map(gohide, function (h) {
                    h.animate({"opacity": 0}, 200).hide();
                });
                $contEl.animate({
                    "left": 0,
                    "bottom": 0,
                    "margin-left": 0
                }, 200);
                grey.removeClass("in").addClass("out").remove();
            }

            function changeMargin(delay) {
                var els = [choicecard, varcard, gocard],
                    marg = $.map(els, function (el) {
                        if (el.css("opacity") > 0) return - el.width() / 2;
                        else return 0;
                    }).reduce(function(a, b) { return a + b; }, 0) - (10 * (els.length - 1));

                $contEl.animate({
                    "margin-left": marg + "px"
                }, delay);
            }

            // add values
            varcard.find("select").append($.map(comparable, function (c) {
                return "<option>" + ucfirst(c) + "</option>";
            }).join(""))

            // reset values and handlers
            choicecard.find("input").each(function () {
                $(this).attr("checked", false);
                $(this).unbind().on("click", function () {
                    // reset values and handlers
                    var sel = varcard.find("select"),
                        go = varcard.find("select"),
                        type = $(this).val();

                    if (type === "pc") sel.attr("multiple", true);
                    else sel.attr("multiple", false);

                    // Show new card
                    if (varcard.css("opacity") > 0) {
                        varcard.animate({ "opacity": 0}, 100).animate({ "opacity": 1}, 100);
                        gocard.animate({ "opacity": 0}, 100).animate({ "opacity": 1}, 100);
                    }
                    else {
                        varcard.show().animate({ "opacity": 1}, 200);
                        gocard.show().animate({ "opacity": 1}, 200);
                    }

                    changeMargin(200);

                    // var select handler
                    gocard.find("button").unbind().on("click", function () {
                        close();
                        showComparison(type, sel.val().toLowerCase());
                    });
                });
            });

            $contEl.animate({
                    "left": "35%",
                    "bottom": "35%"
                }, 400);
            changeMargin(600);

        },
        showComparison = function (type, v) {
            var $contEl = $("#compare_container"),
                dh = $(document).height() - 40,
                dw = $(document).width() / selected.length - 40,
                min = Math.min(dw, dh),
                grey = $("
")
                    .addClass("modal-backdrop fade in")
                    .appendTo("body")
                    .on("click", close);

            function close() {
                $("#compare_container div.card").each(function () { $(this).remove(); });
                grey.removeClass("in").addClass("out").remove();
            }

            function createTarget() {
                return $("
")
                        .addClass("card")
                        .css({
                            "opacity": 0,
                            "width": min + "px",
                            "height": min + "px"
                        })
                        .appendTo($contEl)
                        .animate({"opacity": 1}, 200);
            }

            switch (type) {
                case "ps":
                case "bar":
                    var $target = createTarget(),
                        d3target = d3.select($target.get(0));

                    // add chart to target
                    // use selected[i].state && selected[i].years

                    break;
                case "pie":
                    $.map(selected, function (select, i) {
                        var $target = createTarget(),
                            d3target = d3.select($target.get(0));

                        // Add chart to target

                        // use select.state && select.years

                    });
                    break;

            }
            console.log("show comparison", type, v);

            $contEl.css("margin-left", selected.length * min / 2 + "px");

        },
        clickHandler = function(e, map, group, years) {
            var new_select = {
                    state: group,
                    years: years,
                    map: map
                },
                new_selected = $.grep(selected, function (select) {
                    return !selectedEquals(select, new_select);
                }),
                statecode = group.key.toUpperCase(),
                d3this = d3.select(this);

            // unselected
            if (selected.length !== new_selected.length) {
                selected = new_selected;
                // remove pie
                d3this.attr("selected", "");

            }
            // selected
            else {
                selected.push(new_select);

                // add pie
                d3this.style("fill", "mediumseagreen");
                d3this.attr("selected", "selected");
                // TODO: selector for variable and new targets divs with text above pie
//                                makePie(new_select.state, new_select.years, "education", d3.select("body"));
//                                makeParallelSets(new_select.state, [], [], ["jazz", "education", "income", "gender"], d3.select("body"));

            }
            render();
        },
        selectedEquals = function (s1, s2) {
            return (s1.state.key == s2.state.key &&
                        $(s1.years).not(s2.years).length == 0 &&
                        $(s2.years).not(s1.years).length == 0)
        },
        setMaps = function (maps_in) {
            $.extend(maps, maps_in);

            $.map(maps, function (map) {
                map.addClickStateFunction(clickHandler);
            });
        };

    return {
        setMaps: setMaps
    };
};<commit_msg>bugfixed positioning<commit_after>/**
*   COMPARE TOOL
 */

var compare = function () {
    var $contEl = $("#selected_container"),
        $selEl = $contEl.find("#selected"),
        $pEl = $selEl.find("p"),
        selected = [],
        maps = [],
        selectToString = function (select) {
            var stat = select.state[select.map.getId()].mergedstats.toFixed(2);
            return "" + select.state.name + " (" + select.years.join(", ") + ") " + stat + "%";
        },
        selectToLi = function (select) {
            return "
" + selectToString(select) + "
";
        },
        render = function () {
            var o = "";
            $pEl.removeClass("pointer").unbind();
            if (selected.length === 0) {
                o += "Select states to compare.";
            } else {
                o += "You selected:
";
                o += $.map(selected, selectToLi).join("");
                o += "
";
                $pEl.addClass("pointer")
                    .on("click", compare);
            }
            $pEl.html(o);
            $contEl.animate({"opacity": 1}, 200);
        },
        compare = function () {
            var grey = $("
")
                    .addClass("modal-backdrop fade in")
                    .appendTo("body")
                    .on("click", close),
                choicecard = $("#howtocomparecard")
                    .delay(600)
                    .show()
                    .animate({ "opacity": 1}, 200),
                varcard = $("#comparevarcard"),
                gocard = $("#comparegocard"),
                gohide = [gocard, varcard, choicecard];

            function close() {
                $.map(gohide, function (h) {
                    h.animate({"opacity": 0}, 200).hide();
                });
                $contEl.animate({
                    "left": 0,
                    "bottom": 0,
                    "margin-left": 0
                }, 200);
                grey.removeClass("in").addClass("out").remove();
            }

            function changeMargin(delay) {
                var els = [choicecard, varcard, gocard],
                    marg = $.map(els, function (el) {
                        if (el.css("opacity") > 0) return - el.width() / 2;
                        else return 0;
                    }).reduce(function(a, b) { return a + b; }, 0) - (10 * (els.length - 1));

                $contEl.animate({
                    "margin-left": marg + "px"
                }, delay);
            }

            // add values
            if (varcard.find("option").length == 0)
                varcard.find("select").append($.map(comparable, function (c) {
                    return "<option>" + ucfirst(c) + "</option>";
                }).join(""))

            // reset values and handlers
            choicecard.find("input").each(function () {
                $(this).attr("checked", false);
                $(this).unbind().on("click", function () {
                    // reset values and handlers
                    var sel = varcard.find("select"),
                        go = varcard.find("select"),
                        type = $(this).val();

                    if (type === "pc") sel.attr("multiple", true);
                    else sel.attr("multiple", false);

                    // Show new card
                    if (varcard.css("opacity") > 0) {
                        varcard.animate({ "opacity": 0}, 100).animate({ "opacity": 1}, 100);
                        gocard.animate({ "opacity": 0}, 100).animate({ "opacity": 1}, 100);
                    }
                    else {
                        varcard.show().animate({ "opacity": 1}, 200);
                        gocard.show().animate({ "opacity": 1}, 200);
                    }

                    changeMargin(200);

                    // var select handler
                    gocard.find("button").unbind().on("click", function () {
                        close();
                        showComparison(type, sel.val().toLowerCase());
                    });
                });
            });

            $contEl.animate({
                    "left": "35%",
                    "bottom": "35%"
                }, 400);
            changeMargin(600);

        },
        showComparison = function (type, v) {
            var $contEl = $("#compare_container"),
                dh = $(document).height() - 40,
                dw = $(document).width() / selected.length - 40,
                min = Math.min(dw, dh),
                grey = $("
")
                    .addClass("modal-backdrop fade in")
                    .appendTo("body")
                    .on("click", close);

            function close() {
                $("#compare_container div.card").each(function () { $(this).remove(); });
                grey.removeClass("in").addClass("out").remove();
            }

            function createTarget() {
                return $("
")
                        .addClass("card")
                        .css({
                            "opacity": 0,
                            "width": min + "px",
                            "height": min + "px"
                        })
                        .appendTo($contEl)
                        .animate({"opacity": 1}, 200);
            }

            switch (type) {
                case "ps":
                case "bar":
                    var $target = createTarget(),
                        d3target = d3.select($target.get(0));

                    // add chart to target
                    // use selected[i].state && selected[i].years

                    $contEl.css("margin-left", parseInt(min / 2) + "px");
                    break;
                case "pie":
                    $.map(selected, function (select, i) {
                        var $target = createTarget(),
                            d3target = d3.select($target.get(0));

                        // Add chart to target

                        // use select.state && select.years

                    });
                    $contEl.css("margin-left", parseInt(selected.length * min / 2) + "px");
                    break;

            }
            console.log("show comparison", type, v);

        },
        clickHandler = function(e, map, group, years) {
            var new_select = {
                    state: group,
                    years: years,
                    map: map
                },
                new_selected = $.grep(selected, function (select) {
                    return !selectedEquals(select, new_select);
                }),
                statecode = group.key.toUpperCase(),
                d3this = d3.select(this);

            // unselected
            if (selected.length !== new_selected.length) {
                selected = new_selected;
                // remove pie
                d3this.attr("selected", "");

            }
            // selected
            else {
                selected.push(new_select);

                // add pie
                d3this.style("fill", "mediumseagreen");
                d3this.attr("selected", "selected");
                // TODO: selector for variable and new targets divs with text above pie
//                                makePie(new_select.state, new_select.years, "education", d3.select("body"));
//                                makeParallelSets(new_select.state, [], [], ["jazz", "education", "income", "gender"], d3.select("body"));

            }
            render();
        },
        selectedEquals = function (s1, s2) {
            return (s1.state.key == s2.state.key &&
                        $(s1.years).not(s2.years).length == 0 &&
                        $(s2.years).not(s1.years).length == 0)
        },
        setMaps = function (maps_in) {
            $.extend(maps, maps_in);

            $.map(maps, function (map) {
                map.addClickStateFunction(clickHandler);
            });
        };

    return {
        setMaps: setMaps
    };
};<|endoftext|>


<commit_before>import React, { Component } from 'react'
import 'styled-components/macro'
import { Link } from 'react-router-dom'
import styled, { keyframes } from 'styled-components'
import { FaSignOutAlt, FaGithub, FaLightbulb } from 'react-icons/fa'

import { adjust } from '../theming'
import User from './User'
import API from '../api'
import { WhenLoggedIn } from '../auth'

const Links = styled.div`
  margin-left: auto;
  display: flex;
  align-items: center;

  a {
    margin-left: 2em;
    display: flex;
    align-items: center;
  }

  button {
    display: flex;
    background: none;
    border: none;
    padding: 0;
    font-size: inherit;
    cursor: pointer;
    color: inherit;
  }

  svg {
    margin-right: 0.5em;
  }
`

const statusTextHide = keyframes`
  from {
    width: 100%;
  }

  to {
    width: 0;
  }
`

const StyledNav = styled.nav`
  padding: 1rem 2rem;

  background: ${(props) => adjust(0.05, props.theme.bg)};

  display: flex;
  flex-flow: row nowrap;
  align-items: center;

  h1 {
    font-size: 1rem;
    margin: 0 1rem 0 0;
  }

  a {
    color: inherit;
    text-decoration: inherit;
  }

  #status {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;

    #status-circle {
      width: 0.75rem;
      height: 0.75rem;
      flex-shrink: 0;
      display: inline-block;
      border-radius: 100%;
      margin-right: 0.5rem;
    }

    #status-text {
      white-space: nowrap;
      overflow: hidden;
    }
  }

  @media (max-width: 700px) {
    svg {
      margin-right: 0 !important;
    }

    .link-text,
    .username,
    #status-text {
      display: none;
    }
  }
`

const ConnectionStatus = {
  CONNECTING: { text: 'Connecting...', color: 'hsl(40, 100%, 30%)' },
  CONNECTED: { text: 'Connected!', color: 'hsl(130, 100%, 30%)' },
  DISCONNECTED: { text: 'Disconnected.', color: 'hsl(345, 100%, 30%)' },
}

export default class Nav extends Component {
  state = {
    status: null,
  }

  async componentDidMount() {
    try {
      const status = await API.get('/api/status')
      this.setState({ status })
    } catch (error) {
      this.disconnected()
    }
  }

  disconnected() {
    this.setState({ status: { ready: false, ping: -1, guilds: 0 } })
  }

  status() {
    if (this.state.status == null) {
      return ConnectionStatus.CONNECTING
    } else if (this.state.status.ready) {
      return ConnectionStatus.CONNECTED
    } else {
      return ConnectionStatus.DISCONNECTED
    }
  }

  render() {
    const { onToggleLights } = this.props
    const status = this.status()
    let statusText

    if (status.text === ConnectionStatus.CONNECTED.text) {
      statusText = (

          {`${status.text} (${this.state.status.guilds} servers)`}

      )
    } else {
      statusText =
{status.text}

    }

    return (

    )
  }
}
<commit_msg>Tweak navbar metrics<commit_after>import React, { Component } from 'react'
import 'styled-components/macro'
import { Link } from 'react-router-dom'
import styled, { keyframes } from 'styled-components'
import { FaSignOutAlt, FaGithub, FaLightbulb } from 'react-icons/fa'

import { adjust } from '../theming'
import User from './User'
import API from '../api'
import { WhenLoggedIn } from '../auth'

const Links = styled.div`
  margin-left: auto;
  display: flex;
  align-items: center;

  a {
    margin-left: 2em;
    display: flex;
    align-items: center;
  }

  button {
    display: flex;
    background: none;
    border: none;
    padding: 0;
    font-size: inherit;
    cursor: pointer;
    color: inherit;
  }

  svg {
    margin-right: 0.5em;
  }
`

const statusTextHide = keyframes`
  from {
    width: 100%;
  }

  to {
    width: 0;
  }
`

const StyledNav = styled.nav`
  padding: 1rem 2rem;

  background: ${(props) => adjust(0.05, props.theme.bg)};

  display: flex;
  flex-flow: row nowrap;
  align-items: center;

  h1 {
    font-size: 1rem;
    margin: 0 1rem 0 0;
  }

  a {
    color: inherit;
    text-decoration: inherit;
  }

  #status {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;

    #status-circle {
      width: 0.75rem;
      height: 0.75rem;
      flex-shrink: 0;
      display: inline-block;
      border-radius: 100%;
      margin-right: 0.5rem;
    }

    #status-text {
      white-space: nowrap;
      overflow: hidden;
    }
  }

  @media (max-width: 800px) {
    svg {
      margin-right: 0 !important;
    }

    .link-text,
    .username,
    #status-text {
      display: none;
    }
  }
`

const ConnectionStatus = {
  CONNECTING: { text: 'Connecting...', color: 'hsl(40, 100%, 30%)' },
  CONNECTED: { text: 'Connected!', color: 'hsl(130, 100%, 30%)' },
  DISCONNECTED: { text: 'Disconnected.', color: 'hsl(345, 100%, 30%)' },
}

export default class Nav extends Component {
  state = {
    status: null,
  }

  async componentDidMount() {
    try {
      const status = await API.get('/api/status')
      this.setState({ status })
    } catch (error) {
      this.disconnected()
    }
  }

  disconnected() {
    this.setState({ status: { ready: false, ping: -1, guilds: 0 } })
  }

  status() {
    if (this.state.status == null) {
      return ConnectionStatus.CONNECTING
    } else if (this.state.status.ready) {
      return ConnectionStatus.CONNECTED
    } else {
      return ConnectionStatus.DISCONNECTED
    }
  }

  render() {
    const { onToggleLights } = this.props
    const status = this.status()
    let statusText

    if (status.text === ConnectionStatus.CONNECTED.text) {
      statusText = (

          {status.text}

      )
    } else {
      statusText =
{status.text}

    }

    return (

    )
  }
}
<|endoftext|>


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

const fs = require('fs')
const dbApi = require('../src-electron/db/db-api.js')
const zclLoader = require('../src-electron/zcl/zcl-loader.js')
const Validation = require('../src-electron/validation/validation.js')
const args = require('../src-electron/main-process/args.js')
const querySession = require('../src-electron/db/query-session.js')
const queryConfig = require('../src-electron/db/query-config.js')
const queryZcl = require('../src-electron/db/query-zcl.js')
const env = require('../src-electron/util/env.js')

var db
var sid

beforeAll(() => {
  var file = env.sqliteTestFile('validation')
  return dbApi
    .initDatabase(file)
    .then((d) => dbApi.loadSchema(d, env.schemaFile(), env.zapVersion()))
    .then((d) => {
      db = d
      env.logInfo('DB initialized.')
    })
}, 5000)

afterAll(() => {
  var file = env.sqliteTestFile('validation')
  return dbApi.closeDatabase(db).then(() => {
    if (fs.existsSync(file)) fs.unlinkSync(file)
  })
})

test('Load the static data.', () => {
  return zclLoader.loadZcl(db, args.zclPropertiesFile)
}, 5000)

test('isValidNumberString Functions', () => {
  // Integer
  expect(Validation.isValidNumberString('0x0000')).toBeTruthy()
  expect(Validation.isValidNumberString('0x0001')).toBeTruthy()
  expect(!Validation.isValidNumberString('0x00asdfajaklsf;01')).toBeTruthy()
  // Float
  expect(Validation.isValidFloat('5.6')).toBeTruthy()
  expect(Validation.isValidFloat('5')).toBeTruthy()
  expect(!Validation.isValidFloat('5.6....')).toBeTruthy()
  expect(Validation.isValidFloat('.0001')).toBeTruthy()
})

test('extractValue Functions', () => {
  //Integer
  expect(Validation.extractIntegerValue('5') == 5).toBeTruthy()
  expect(Validation.extractIntegerValue('0x05') == 5).toBeTruthy()
  expect(Validation.extractIntegerValue('A') == 10).toBeTruthy()
  //float
  expect(Validation.extractFloatValue('0.53') == 0.53).toBeTruthy()
  expect(Validation.extractFloatValue('.53') == 0.53).toBeTruthy()
})

test('Test int bounds', () => {
  //Integer
  expect(Validation.checkBoundsInteger(50, 25, 60)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(50, 25, 20)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(50, 51, 55)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(30, 'avaa', 2)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(30, 45, 50)).toBeTruthy()
  expect(Validation.checkBoundsInteger('asdfa', 40, 50)).toBeFalsy()

  //Float
  expect(Validation.checkBoundsFloat(35.0, 25, 50.0))
  expect(!Validation.checkBoundsFloat(351.0, 25, 50.0))
  expect(!Validation.checkBoundsFloat(351.0, 355, 5650.0))
})

test('Validate types', () => {
  expect(Validation.isStringType('CHAR_STRING'))
  expect(Validation.isStringType('OCTET_STRING'))
  expect(Validation.isStringType('LONG_CHAR_STRING'))
  expect(Validation.isStringType('LONG_OCTET_STRING'))
  expect(!Validation.isStringType('FLOAT_SEMI'))

  expect(Validation.isFloatType('FLOAT_SEMI'))
  expect(Validation.isFloatType('FLOAT_SINGLE'))
  expect(Validation.isFloatType('FLOAT_DOUBLE'))
  expect(!Validation.isFloatType('LONG_OCTET_STRING'))
})

test(
  'Integer Test',
  () =>
    queryZcl
      .selectAttributesByClusterCodeAndManufacturerCode(db, '0x0003', null)
      .then((attribute) => {
        var attribute = attribute.filter((e) => {
          return e.code === '0x0000'
        })[0]

        //Test Constraints
        var minMax = Validation.getBoundsInteger(attribute)
        expect(minMax.min == 0).toBeTruthy()
        expect(minMax.max === 0xffff).toBeTruthy()
      }),
  1000
)

test('validate Attribute Test', () => {
  var fakeEndpointAttribute = {
    defaultValue: '30',
  }

  var fakeAttribute = {
    type: 'UINT16',
    min: '0x0010',
    max: '50',
  }

  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
  // Check for if attribute is out of bounds.
  fakeEndpointAttribute.defaultValue = '60'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()
  fakeEndpointAttribute.defaultValue = '5'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  //Check if attribute is actually a number
  fakeEndpointAttribute.defaultValue = 'xxxxxx'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  var fakeAttribute = {
    type: 'FLOAT_SINGLE',
    min: '0.5',
    max: '2',
  }

  var fakeEndpointAttribute = {
    defaultValue: '1.5',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
  //Check out of bounds.
  var fakeEndpointAttribute = {
    defaultValue: '4.5',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()
  var fakeEndpointAttribute = {
    defaultValue: '.25',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  //Check if attribute is actually a number
  fakeEndpointAttribute.defaultValue = 'xxxxxx'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  // Expect no issues with strings.
  var fakeAttribute = {
    type: 'CHAR_STRING',
  }
  var fakeEndpointAttribute = {
    defaultValue: '30adfadf',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
}, 2000)

test('validate endpoint test', () => {
  //Validate normal operation
  var endpoint = {
    endpointId: '1',
    networkId: '0',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeTruthy()
  expect(
    Validation.validateSpecificEndpoint(endpoint).networkId.length == 0
  ).toBeTruthy()

  //Validate not a number
  var endpoint = {
    endpointId: 'blah',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()
  expect(
    Validation.validateSpecificEndpoint(endpoint).networkId.length == 0
  ).toBeFalsy()

  //Validate 0 not being valid Endpoint ID
  var endpoint = {
    endpointId: '0',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()

  //Validate out of bounds on endpointId
  var endpoint = {
    endpointId: '0xFFFFFFFF',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()
}, 2000)

describe('Validate endpoint for duplicate endpointIds', () => {
  var endpointTypeIdOnOff
  var endpointTypeReference
  var eptId
  beforeAll(() => {
    return zclLoader
      .loadZcl(db, args.zclPropertiesFile)
      .then((ctx) =>
        querySession.ensureZapSessionId(db, 'SESSION', 666).then((id) => {
          sid = id
        })
      )
      .then(() =>
        queryZcl.selectAllDeviceTypes(db).then((rows) => {
          let haOnOffDeviceTypeArray = rows.filter(
            (data) => data.label === 'HA-onoff'
          )
          haOnOffDeviceType = haOnOffDeviceTypeArray[0]
          return Promise.resolve(haOnOffDeviceType.id)
        })
      )
      .then((deviceTypeId) =>
        queryConfig
          .insertEndpointType(db, sid, 'testEndpointType', deviceTypeId)
          .then((rowId) => {
            endpointTypeIdOnOff = rowId
            return queryZcl.selectEndpointType(db, rowId)
          })
      )
      .then((endpointType) =>
        queryConfig
          .insertEndpoint(db, sid, 1, endpointType.endpointTypeId, 1)
          .then(
            queryConfig.insertEndpoint(
              db,
              sid,
              1,
              endpointType.endpointTypeId,
              1
            )
          )
      )
      .then((endpointId) => {
        eptId = endpointId
      })
  }, 10000)
  test('Test endpoint for duplicates', () =>
    Validation.validateEndpoint(db, eptId)
      .then((data) => Validation.validateNoDuplicateEndpoints(db, eptId, sid))
      .then((hasNoDuplicates) => {
        expect(hasNoDuplicates).toBeFalsy()
      }))
})
<commit_msg>NOJIRA Testing timeouts<commit_after>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 *
 * @jest-environment node
 */

const fs = require('fs')
const dbApi = require('../src-electron/db/db-api.js')
const zclLoader = require('../src-electron/zcl/zcl-loader.js')
const Validation = require('../src-electron/validation/validation.js')
const args = require('../src-electron/main-process/args.js')
const querySession = require('../src-electron/db/query-session.js')
const queryConfig = require('../src-electron/db/query-config.js')
const queryZcl = require('../src-electron/db/query-zcl.js')
const env = require('../src-electron/util/env.js')

var db
var sid

beforeAll(() => {
  var file = env.sqliteTestFile('validation')
  return dbApi
    .initDatabase(file)
    .then((d) => dbApi.loadSchema(d, env.schemaFile(), env.zapVersion()))
    .then((d) => {
      db = d
      env.logInfo('DB initialized.')
    })
}, 5000)

afterAll(() => {
  var file = env.sqliteTestFile('validation')
  return dbApi.closeDatabase(db).then(() => {
    if (fs.existsSync(file)) fs.unlinkSync(file)
  })
})

test('Load the static data.', () => {
  return zclLoader.loadZcl(db, args.zclPropertiesFile)
}, 5000)

test('isValidNumberString Functions', () => {
  // Integer
  expect(Validation.isValidNumberString('0x0000')).toBeTruthy()
  expect(Validation.isValidNumberString('0x0001')).toBeTruthy()
  expect(!Validation.isValidNumberString('0x00asdfajaklsf;01')).toBeTruthy()
  // Float
  expect(Validation.isValidFloat('5.6')).toBeTruthy()
  expect(Validation.isValidFloat('5')).toBeTruthy()
  expect(!Validation.isValidFloat('5.6....')).toBeTruthy()
  expect(Validation.isValidFloat('.0001')).toBeTruthy()
})

test('extractValue Functions', () => {
  //Integer
  expect(Validation.extractIntegerValue('5') == 5).toBeTruthy()
  expect(Validation.extractIntegerValue('0x05') == 5).toBeTruthy()
  expect(Validation.extractIntegerValue('A') == 10).toBeTruthy()
  //float
  expect(Validation.extractFloatValue('0.53') == 0.53).toBeTruthy()
  expect(Validation.extractFloatValue('.53') == 0.53).toBeTruthy()
})

test('Test int bounds', () => {
  //Integer
  expect(Validation.checkBoundsInteger(50, 25, 60)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(50, 25, 20)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(50, 51, 55)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(30, 'avaa', 2)).toBeTruthy()
  expect(!Validation.checkBoundsInteger(30, 45, 50)).toBeTruthy()
  expect(Validation.checkBoundsInteger('asdfa', 40, 50)).toBeFalsy()

  //Float
  expect(Validation.checkBoundsFloat(35.0, 25, 50.0))
  expect(!Validation.checkBoundsFloat(351.0, 25, 50.0))
  expect(!Validation.checkBoundsFloat(351.0, 355, 5650.0))
})

test('Validate types', () => {
  expect(Validation.isStringType('CHAR_STRING'))
  expect(Validation.isStringType('OCTET_STRING'))
  expect(Validation.isStringType('LONG_CHAR_STRING'))
  expect(Validation.isStringType('LONG_OCTET_STRING'))
  expect(!Validation.isStringType('FLOAT_SEMI'))

  expect(Validation.isFloatType('FLOAT_SEMI'))
  expect(Validation.isFloatType('FLOAT_SINGLE'))
  expect(Validation.isFloatType('FLOAT_DOUBLE'))
  expect(!Validation.isFloatType('LONG_OCTET_STRING'))
})

test(
  'Integer Test',
  () =>
    queryZcl
      .selectAttributesByClusterCodeAndManufacturerCode(db, '0x0003', null)
      .then((attribute) => {
        var attribute = attribute.filter((e) => {
          return e.code === '0x0000'
        })[0]

        //Test Constraints
        var minMax = Validation.getBoundsInteger(attribute)
        expect(minMax.min == 0).toBeTruthy()
        expect(minMax.max === 0xffff).toBeTruthy()
      }),
  2000
)

test('validate Attribute Test', () => {
  var fakeEndpointAttribute = {
    defaultValue: '30',
  }

  var fakeAttribute = {
    type: 'UINT16',
    min: '0x0010',
    max: '50',
  }

  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
  // Check for if attribute is out of bounds.
  fakeEndpointAttribute.defaultValue = '60'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()
  fakeEndpointAttribute.defaultValue = '5'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  //Check if attribute is actually a number
  fakeEndpointAttribute.defaultValue = 'xxxxxx'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  var fakeAttribute = {
    type: 'FLOAT_SINGLE',
    min: '0.5',
    max: '2',
  }

  var fakeEndpointAttribute = {
    defaultValue: '1.5',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
  //Check out of bounds.
  var fakeEndpointAttribute = {
    defaultValue: '4.5',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()
  var fakeEndpointAttribute = {
    defaultValue: '.25',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  //Check if attribute is actually a number
  fakeEndpointAttribute.defaultValue = 'xxxxxx'
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeFalsy()

  // Expect no issues with strings.
  var fakeAttribute = {
    type: 'CHAR_STRING',
  }
  var fakeEndpointAttribute = {
    defaultValue: '30adfadf',
  }
  expect(
    Validation.validateSpecificAttribute(fakeEndpointAttribute, fakeAttribute)
      .defaultValue.length == 0
  ).toBeTruthy()
}, 2000)

test('validate endpoint test', () => {
  //Validate normal operation
  var endpoint = {
    endpointId: '1',
    networkId: '0',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeTruthy()
  expect(
    Validation.validateSpecificEndpoint(endpoint).networkId.length == 0
  ).toBeTruthy()

  //Validate not a number
  var endpoint = {
    endpointId: 'blah',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()
  expect(
    Validation.validateSpecificEndpoint(endpoint).networkId.length == 0
  ).toBeFalsy()

  //Validate 0 not being valid Endpoint ID
  var endpoint = {
    endpointId: '0',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()

  //Validate out of bounds on endpointId
  var endpoint = {
    endpointId: '0xFFFFFFFF',
    networkId: 'blah',
  }
  expect(
    Validation.validateSpecificEndpoint(endpoint).endpointId.length == 0
  ).toBeFalsy()
}, 2000)

describe('Validate endpoint for duplicate endpointIds', () => {
  var endpointTypeIdOnOff
  var endpointTypeReference
  var eptId
  beforeAll(() => {
    return zclLoader
      .loadZcl(db, args.zclPropertiesFile)
      .then((ctx) =>
        querySession.ensureZapSessionId(db, 'SESSION', 666).then((id) => {
          sid = id
        })
      )
      .then(() =>
        queryZcl.selectAllDeviceTypes(db).then((rows) => {
          let haOnOffDeviceTypeArray = rows.filter(
            (data) => data.label === 'HA-onoff'
          )
          haOnOffDeviceType = haOnOffDeviceTypeArray[0]
          return Promise.resolve(haOnOffDeviceType.id)
        })
      )
      .then((deviceTypeId) =>
        queryConfig
          .insertEndpointType(db, sid, 'testEndpointType', deviceTypeId)
          .then((rowId) => {
            endpointTypeIdOnOff = rowId
            return queryZcl.selectEndpointType(db, rowId)
          })
      )
      .then((endpointType) =>
        queryConfig
          .insertEndpoint(db, sid, 1, endpointType.endpointTypeId, 1)
          .then(
            queryConfig.insertEndpoint(
              db,
              sid,
              1,
              endpointType.endpointTypeId,
              1
            )
          )
      )
      .then((endpointId) => {
        eptId = endpointId
        return Promise.resolve()
      })
  }, 10000)
  test('Test endpoint for duplicates', () =>
    Validation.validateEndpoint(db, eptId)
      .then((data) => Validation.validateNoDuplicateEndpoints(db, eptId, sid))
      .then((hasNoDuplicates) => {
        expect(hasNoDuplicates).toBeFalsy()
      }))
})
<|endoftext|>


<commit_before>'use strict';

(function(pagecode) { //eslint-disable-line wrap-iife
  pagecode(window.jQuery, window, document, R6MapsData, R6MapsRender, R6MapsControls, R6MapsLangTerms);
}(function($, window, document, R6MapsData, R6MapsRender, R6MapsControls, R6MapsLangTerms, undefined) {
  var map,
    mapElements,
    svgElements,
    HASH_SPLIT_CHAR = '/';

  $(function() { // equivanelt to $(document).ready() - but a bit faster
    map = $('#map');
    mapElements = map.find('#map-elements');
    svgElements = map.find('#svg-elements');

    tryLoadStartingLanguage();
    setupMenu();

    R6MapsControls.populateMapOptions(R6MapsData.getMapData());
    trySelectBookmarkedMap();
    loadMap();

    trySelectBookmarkedObjective();
    trySelectBookmarkedFloor();

    setupEvents();
    R6MapsControls.setupZoom(map, mapElements);
  });

  var tryLoadStartingLanguage = function tryLoadStartingLanguage(){
    var lastChosenLanguage = localStorage.getItem('language'),
      userLang = (navigator.language || navigator.userLanguage).split('-')[0];

    if (lastChosenLanguage) {
      R6MapsLangTerms.tryLoadLanguage(lastChosenLanguage);
    } else if (userLang) {
      R6MapsLangTerms.tryLoadLanguage(userLang);
    };  // default will be English otherwise
  };

  var loadMap = function loadMap() {
    var currentlySelectedMap = R6MapsControls.getCurrentlySelectedMap(),
      mapData = R6MapsData.getMapData();

    R6MapsControls.populateObjectiveOptions(mapData[currentlySelectedMap].objectives);
    R6MapsControls.populateFloorOptions(mapData[currentlySelectedMap].floors);
    R6MapsRender.renderMap(mapData[currentlySelectedMap], mapElements, svgElements);
    setupCameraScreenshots();
    setupCameraLos();
    showSelectedFloor();
    showSelectedObjective();
  };

  var trySelectBookmarkedMap = function trySelectBookmarkedMap() {
    var hashArgs = getHashArgs(),
      mapArg = hashArgs[0];

    R6MapsControls.trySelectMap(mapArg);
  };

  var trySelectBookmarkedObjective = function trySelectBookmarkedObjective() {
    var hashArgs = getHashArgs(),
      objectiveArg = hashArgs[2];

    if (R6MapsControls.trySelectObjective(objectiveArg)) {
      showSelectedObjective();
    }
  };

  var trySelectBookmarkedFloor = function trySelectBookmarkedFloor() {
    var hashArgs = getHashArgs(),
      floorArg = hashArgs[1];

    if (R6MapsControls.trySelectFloor(floorArg)) {
      showSelectedFloor();
    }
  };

  var getHashArgs = function getHashArgs() {
    return window.location.hash.substr(1).split(HASH_SPLIT_CHAR);
  };

  var setupEvents = function setupEvents() {
    map.on('click', outputCoordinates);

    R6MapsControls.setupObjectiveChangeEvent(handleObjectiveChange);
    R6MapsControls.setupMapChangeEvent(handleMapChange);
    R6MapsControls.setupFloorChangeEvent(handleFloorChange);
    R6MapsControls.setupFloorHotkeys(showSelectedFloor);
  };

  var setupCameraLos = function setupCameraLos() {
    $('.camera').on('mouseenter', handleCameraIn);
    $('.camera').on('mouseleave', handleCameraOut);
  };

  var handleCameraIn = function handleCameraHoverIn(event) {
    var cameraId = getCameraIdFromEvent(event);

    $('.camera-los.camera-' + cameraId).addClass('show-more');
  };

  var handleCameraOut = function handleCameraHoverOut(event) {
    var cameraId = getCameraIdFromEvent(event);

    $('.camera-los.camera-' + cameraId).removeClass('show-more');
  };

  var getCameraIdFromEvent = function getCameraIdFromEvent(event) {
    return $(event.target).data('camera-id');
  };

  var handleMapChange = function handleMapChange() {
    sendMapSelectAnalyticsEvent();
    loadMap();
    updateUrl();
  };

  var handleObjectiveChange = function handleObjectiveChange() {
    sendObjectiveSelectAnalyticsEvent();
    showSelectedObjective();
    updateUrl();
  };

  var handleFloorChange = function handleFloorChange() {
    sendFloorSelectAnalyticsEvent();
    showSelectedFloor();
    updateUrl();
  };

  var sendMapSelectAnalyticsEvent = function sendMapSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Map', R6MapsControls.getCurrentlySelectedMap());
  };

  var sendObjectiveSelectAnalyticsEvent = function sendObjectiveSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Objective', R6MapsControls.getCurrentlySelectedObjective());
  };

  var sendFloorSelectAnalyticsEvent = function sendFloorSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Floor', R6MapsControls.getCurrentlySelectedFloor());
  };

  var sendControlAnalyticsEvent = function sendControlAnalyticsEvent(control, value) {
    ga('send', {
      hitType: 'event',
      eventCategory: 'Controls',
      eventAction: control,
      eventLabel: value
    });
  };

  var updateUrl = function updateUrl() {
    var hashText = '';

    hashText += '' + R6MapsControls.getCurrentlySelectedMap();
    hashText += HASH_SPLIT_CHAR + R6MapsControls.getCurrentlySelectedFloor();
    hashText += HASH_SPLIT_CHAR + R6MapsControls.getCurrentlySelectedObjective();
    window.location.hash = hashText;
  };

  var outputCoordinates = function outputCoordinates(e) {
    var warning = R6MapsControls.isZoomed() ? ' Warning, currently zoomed, coordinates are not accurate for CSS.' : '';

    console.log('SINGLE LINE TEXT:');
    console.log(
      'top: ' + Math.round(e.pageY - mapElements.offset().top + 14) + ', ' +
      'left: ' + Math.round(e.pageX - mapElements.offset().left)
    );

    console.log('REGULAR/DOUBLE LINE TEXT:');
    console.log(
      'top: ' + Math.round(e.pageY - mapElements.offset().top) + ', ' +
      'left: ' + Math.round(e.pageX - mapElements.offset().left) +
      warning
    );

  };

  var setupCameraScreenshots = function setupCameraScreenShots(){
    $('a.camera').fancybox({
      padding: 0,
      helpers: {
        overlay: {
          css: {
            background: 'rgba(48,113,169, 0.3)'
          }
        }
      }
    });
  };

  var setupMenu = function setupMenu() {
    R6MapsControls.populateMenu();

    $('#mmenu-menu').mmenu({
      offCanvas: {
        position: 'right'
      },
      extensions: ['pagedim']
    },
      {
        offCanvas: {
          pageSelector: '#mmenu-page'
        }
      });

    $('#mmenu-link').click(handleMenuClick);
    $('#lang-choices').on('click','a',handleLangChange);
  };

  var handleMenuClick = function handleMenuClick(e) {
    var menuApi = getMenuApi();

    e.preventDefault();
    menuApi.open();
  };

  var handleLangChange = function handleLangChange(event) {
    var menuApi = getMenuApi(),
      newLang = $(event.target).data('lang');

    event.preventDefault();
    menuApi.close();

    R6MapsLangTerms.tryLoadLanguage(newLang);

    setupMenu();
    R6MapsControls.populateMapOptions(R6MapsData.getMapData());
    loadMap();

    localStorage.setItem('language', newLang);
  };

  var getMenuApi = function getMenuApi() {
    return $('#mmenu-menu').data( 'mmenu' );
  };

  var showSelectedFloor =  function showSelectedFloor() {
    R6MapsRender.showFloor(R6MapsControls.getCurrentlySelectedFloor(), map);
  };

  var showSelectedObjective =  function showSelectedObjective() {
    R6MapsRender.showObjective(R6MapsControls.getCurrentlySelectedObjective(), mapElements);
  };
}));
<commit_msg>Resolving lint issue<commit_after>'use strict';

(function(pagecode) { //eslint-disable-line wrap-iife
  pagecode(window.jQuery, window, document, R6MapsData, R6MapsRender, R6MapsControls, R6MapsLangTerms);
}(function($, window, document, R6MapsData, R6MapsRender, R6MapsControls, R6MapsLangTerms, undefined) {
  var map,
    mapElements,
    svgElements,
    HASH_SPLIT_CHAR = '/';

  $(function() { // equivanelt to $(document).ready() - but a bit faster
    map = $('#map');
    mapElements = map.find('#map-elements');
    svgElements = map.find('#svg-elements');

    tryLoadStartingLanguage();
    setupMenu();

    R6MapsControls.populateMapOptions(R6MapsData.getMapData());
    trySelectBookmarkedMap();
    loadMap();

    trySelectBookmarkedObjective();
    trySelectBookmarkedFloor();

    setupEvents();
    R6MapsControls.setupZoom(map, mapElements);
  });

  var tryLoadStartingLanguage = function tryLoadStartingLanguage(){
    var lastChosenLanguage = localStorage.getItem('language'),
      userLang = (navigator.language || navigator.userLanguage).split('-')[0];

    if (lastChosenLanguage) {
      R6MapsLangTerms.tryLoadLanguage(lastChosenLanguage);
    } else if (userLang) {
      R6MapsLangTerms.tryLoadLanguage(userLang);
    };  // default will be English otherwise
  };

  var loadMap = function loadMap() {
    var currentlySelectedMap = R6MapsControls.getCurrentlySelectedMap(),
      mapData = R6MapsData.getMapData();

    R6MapsControls.populateObjectiveOptions(mapData[currentlySelectedMap].objectives);
    R6MapsControls.populateFloorOptions(mapData[currentlySelectedMap].floors);
    R6MapsRender.renderMap(mapData[currentlySelectedMap], mapElements, svgElements);
    setupCameraScreenshots();
    setupCameraLos();
    showSelectedFloor();
    showSelectedObjective();
  };

  var trySelectBookmarkedMap = function trySelectBookmarkedMap() {
    var hashArgs = getHashArgs(),
      mapArg = hashArgs[0];

    R6MapsControls.trySelectMap(mapArg);
  };

  var trySelectBookmarkedObjective = function trySelectBookmarkedObjective() {
    var hashArgs = getHashArgs(),
      objectiveArg = hashArgs[2];

    if (R6MapsControls.trySelectObjective(objectiveArg)) {
      showSelectedObjective();
    }
  };

  var trySelectBookmarkedFloor = function trySelectBookmarkedFloor() {
    var hashArgs = getHashArgs(),
      floorArg = hashArgs[1];

    if (R6MapsControls.trySelectFloor(floorArg)) {
      showSelectedFloor();
    }
  };

  var getHashArgs = function getHashArgs() {
    return window.location.hash.substr(1).split(HASH_SPLIT_CHAR);
  };

  var setupEvents = function setupEvents() {
    map.on('click', outputCoordinates);

    R6MapsControls.setupObjectiveChangeEvent(handleObjectiveChange);
    R6MapsControls.setupMapChangeEvent(handleMapChange);
    R6MapsControls.setupFloorChangeEvent(handleFloorChange);
    R6MapsControls.setupFloorHotkeys(showSelectedFloor);
  };

  var setupCameraLos = function setupCameraLos() {
    $('.camera').on('mouseenter', handleCameraIn);
    $('.camera').on('mouseleave', handleCameraOut);
  };

  var handleCameraIn = function handleCameraHoverIn(event) {
    var cameraId = getCameraIdFromEvent(event);

    $('.camera-los.camera-' + cameraId).addClass('show-more');
  };

  var handleCameraOut = function handleCameraHoverOut(event) {
    var cameraId = getCameraIdFromEvent(event);

    $('.camera-los.camera-' + cameraId).removeClass('show-more');
  };

  var getCameraIdFromEvent = function getCameraIdFromEvent(event) {
    return $(event.target).data('camera-id');
  };

  var handleMapChange = function handleMapChange() {
    sendMapSelectAnalyticsEvent();
    loadMap();
    updateUrl();
  };

  var handleObjectiveChange = function handleObjectiveChange() {
    sendObjectiveSelectAnalyticsEvent();
    showSelectedObjective();
    updateUrl();
  };

  var handleFloorChange = function handleFloorChange() {
    sendFloorSelectAnalyticsEvent();
    showSelectedFloor();
    updateUrl();
  };

  var sendMapSelectAnalyticsEvent = function sendMapSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Map', R6MapsControls.getCurrentlySelectedMap());
  };

  var sendObjectiveSelectAnalyticsEvent = function sendObjectiveSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Objective', R6MapsControls.getCurrentlySelectedObjective());
  };

  var sendFloorSelectAnalyticsEvent = function sendFloorSelectAnalyticsEvent() {
    sendControlAnalyticsEvent('Floor', R6MapsControls.getCurrentlySelectedFloor());
  };

  var sendControlAnalyticsEvent = function sendControlAnalyticsEvent(control, value) {
    ga('send', {
      hitType: 'event',
      eventCategory: 'Controls',
      eventAction: control,
      eventLabel: value
    });
  };

  var updateUrl = function updateUrl() {
    var hashText = '';

    hashText += '' + R6MapsControls.getCurrentlySelectedMap();
    hashText += HASH_SPLIT_CHAR + R6MapsControls.getCurrentlySelectedFloor();
    hashText += HASH_SPLIT_CHAR + R6MapsControls.getCurrentlySelectedObjective();
    window.location.hash = hashText;
  };

  var outputCoordinates = function outputCoordinates(e) {
    var warning = R6MapsControls.isZoomed() ? ' Warning, currently zoomed, coordinates are not accurate for CSS.' : '';

    console.log('SINGLE LINE TEXT:');
    console.log(
      'top: ' + Math.round(e.pageY - mapElements.offset().top + 14) + ', ' +
      'left: ' + Math.round(e.pageX - mapElements.offset().left)
    );

    console.log('REGULAR/DOUBLE LINE TEXT:');
    console.log(
      'top: ' + Math.round(e.pageY - mapElements.offset().top) + ', ' +
      'left: ' + Math.round(e.pageX - mapElements.offset().left) +
      warning
    );
  };

  var setupCameraScreenshots = function setupCameraScreenShots(){
    $('a.camera').fancybox({
      padding: 0,
      helpers: {
        overlay: {
          css: {
            background: 'rgba(48,113,169, 0.3)'
          }
        }
      }
    });
  };

  var setupMenu = function setupMenu() {
    R6MapsControls.populateMenu();

    $('#mmenu-menu').mmenu({
      offCanvas: {
        position: 'right'
      },
      extensions: ['pagedim']
    },
      {
        offCanvas: {
          pageSelector: '#mmenu-page'
        }
      });

    $('#mmenu-link').click(handleMenuClick);
    $('#lang-choices').on('click','a',handleLangChange);
  };

  var handleMenuClick = function handleMenuClick(e) {
    var menuApi = getMenuApi();

    e.preventDefault();
    menuApi.open();
  };

  var handleLangChange = function handleLangChange(event) {
    var menuApi = getMenuApi(),
      newLang = $(event.target).data('lang');

    event.preventDefault();
    menuApi.close();

    R6MapsLangTerms.tryLoadLanguage(newLang);

    setupMenu();
    R6MapsControls.populateMapOptions(R6MapsData.getMapData());
    loadMap();

    localStorage.setItem('language', newLang);
  };

  var getMenuApi = function getMenuApi() {
    return $('#mmenu-menu').data( 'mmenu' );
  };

  var showSelectedFloor =  function showSelectedFloor() {
    R6MapsRender.showFloor(R6MapsControls.getCurrentlySelectedFloor(), map);
  };

  var showSelectedObjective =  function showSelectedObjective() {
    R6MapsRender.showObjective(R6MapsControls.getCurrentlySelectedObjective(), mapElements);
  };
}));
<|endoftext|>


<commit_before>(function (factory) {
	var L, proj4;
	if (typeof define === 'function' && define.amd) {
		// AMD
		define(['leaflet', 'proj4'], factory);
	} else if (typeof module !== 'undefined') {
		// Node/CommonJS
		L = require('leaflet');
		proj4 = require('proj4');
		module.exports = factory(L, proj4);
	} else {
		// Browser globals
		if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined')
			throw "Leaflet and proj4 must be loaded first";
		factory(window.L, window.proj4);
	}
}(function (L, proj4) {

L.Proj = {};

L.Proj._isProj4Obj = function(a) {
	return (typeof a['inverse'] !== 'undefined'
		&& typeof a['forward'] !== 'undefined');
};

L.Proj.ScaleDependantTransformation = function(scaleTransforms) {
		this.scaleTransforms = scaleTransforms;
	}

L.Proj.ScaleDependantTransformation.prototype.transform = function(point, scale) {
		return this.scaleTransforms[scale].transform(point, scale);
};

L.Proj.ScaleDependantTransformation.prototype.untransform = function(point, scale) {
		return this.scaleTransforms[scale].untransform(point, scale);
};

L.Proj.Projection = L.Class.extend({
	initialize: function(a, def) {
		if (L.Proj._isProj4Obj(a)) {
			this._proj = a;
		} else {
			var code = a;
			if (def) {
				proj4.defs(code, def);
			} else if (proj4.defs[code] === undefined) {
				var urn = code.split(':');
				if (urn.length > 3)
					code = urn[urn.length - 3] + ':' + urn[urn.length - 1];
				if (proj4.defs[code] === undefined)
					throw "No projection definition for code " + code;
			}
			this._proj = proj4(code);
		}
	},

	project: function (latlng) {
		var point = this._proj.forward([latlng.lng, latlng.lat]);
		return new L.Point(point[0], point[1]);
	},

	unproject: function (point, unbounded) {
		var point2 = this._proj.inverse([point.x, point.y]);
		return new L.LatLng(point2[1], point2[0], unbounded);
	}
});

L.Proj.CRS = L.Class.extend({
	includes: L.CRS,

	options: {
		transformation: new L.Transformation(1, 0, -1, 0)
	},

	initialize: function(a, b, c) {
		var code, proj, def, options;

		if (L.Proj._isProj4Obj(a)) {
			proj = a;
			code = proj.srsCode;
			options = b || {};

			this.projection = new L.Proj.Projection(proj);
		} else {
			code = a;
			def = b;
			options = c || {};
			this.projection = new L.Proj.Projection(code, def);
		}

		L.Util.setOptions(this, options);
		this.code = code;
		this.transformation = this.options.transformation;

		if (this.options.origin) {
			this.transformation =
				new L.Transformation(1, -this.options.origin[0],
					-1, this.options.origin[1]);
		}

		if (this.options.scales) {
			this.scale = function(zoom) {
				return this.options.scales[zoom];
			}
		} else if (this.options.resolutions) {
			this.scale = function(zoom) {
				return 1 / this.options.resolutions[zoom];
			}
		}
	}
});

L.Proj.CRS.TMS = L.Proj.CRS.extend({
	initialize: function(a, b, c, d) {
		if (L.Proj._isProj4Obj(a)) {
			var proj = a,
				projectedBounds = b,
				options = c || {};
			options.origin = [projectedBounds[0], projectedBounds[3]];
			L.Proj.CRS.prototype.initialize(proj, options);
		} else {
			var code = a,
				def = b,
				projectedBounds = c,
				options = d || {};
			options.origin = [projectedBounds[0], projectedBounds[3]];
			L.Proj.CRS.prototype.initialize(code, def, options);
		}

		this.projectedBounds = projectedBounds;
	}
});

L.Proj.TileLayer = {};

L.Proj.TileLayer.TMS = L.TileLayer.extend({
	options: {
		tms: true,
		continuousWorld: true
	},

	initialize: function(urlTemplate, crs, options) {
		var boundsMatchesGrid = true,
			scaleTransforms,
			upperY,
			crsBounds;

		if (!(crs instanceof L.Proj.CRS.TMS)) {
			throw new Error("CRS is not L.Proj.CRS.TMS.");
		}

		L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
		this.crs = crs;
		crsBounds = this.crs.projectedBounds;

		// Verify grid alignment
		for (var i = this.options.minZoom; i < this.options.maxZoom && boundsMatchesGrid; i++) {
			var gridHeight = (crsBounds[3] - crsBounds[1]) /
				this._projectedTileSize(i);
			boundsMatchesGrid = Math.abs(gridHeight - Math.round(gridHeight)) > 1e-3;
		}

		if (!boundsMatchesGrid) {
			scaleTransforms = {};
			for (var i = this.options.minZoom; i < this.options.maxZoom; i++) {
				upperY = crsBounds[1] + Math.ceil((crsBounds[3] - crsBounds[1]) /
					this._projectedTileSize(i)) * this._projectedTileSize(i);
				scaleTransforms[this.crs.scale(i)] = new L.Transformation(1, -crsBounds[0], -1, upperY);
			}

			this.crs = new L.Proj.CRS.TMS(this.crs.projection._proj, crsBounds, this.crs.options);
			this.crs.transformation = new L.Proj.ScaleDependantTransformation(scaleTransforms);
		}
	},

	getTileUrl: function(tilePoint) {
		// TODO: relies on some of TileLayer's internals
		var z = this._getZoomForUrl(),
			gridHeight = Math.ceil((this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) / this._projectedTileSize(z));

		return L.Util.template(this._url, L.Util.extend({
			s: this._getSubdomain(tilePoint),
			z: z,
			x: tilePoint.x,
			y: gridHeight - tilePoint.y - 1
		}, this.options));
	},

	_projectedTileSize: function(zoom) {
		return (this.options.tileSize / this.crs.scale(zoom));
	}
});

L.Proj.GeoJSON = L.GeoJSON.extend({
	initialize: function(geojson, options) {
		if (geojson.crs && geojson.crs.type == 'name') {
			var crs = new L.Proj.CRS(geojson.crs.properties.name)
			options = options || {};
			options.coordsToLatLng = function(coords) {
				var point = L.point(coords[0], coords[1]);
				return crs.projection.unproject(point);
			};
		}
		L.GeoJSON.prototype.initialize.call(this, geojson, options);
	}
});

L.Proj.geoJson = function(geojson, options) {
	return new L.Proj.GeoJSON(geojson, options);
};

if (typeof L.CRS !== 'undefined') {
	// This is left here for backwards compatibility
	L.CRS.proj4js = (function () {
		return function (code, def, transformation, options) {
			options = options || {};
			if (transformation) options.transformation = transformation;

			return new L.Proj.CRS(code, def, options);
		};
	}());
}

return L.Proj;

}));
<commit_msg>Fixed issue with calling L.Proj.CRS.initialize with wrong context<commit_after>(function (factory) {
	var L, proj4;
	if (typeof define === 'function' && define.amd) {
		// AMD
		define(['leaflet', 'proj4'], factory);
	} else if (typeof module !== 'undefined') {
		// Node/CommonJS
		L = require('leaflet');
		proj4 = require('proj4');
		module.exports = factory(L, proj4);
	} else {
		// Browser globals
		if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined')
			throw "Leaflet and proj4 must be loaded first";
		factory(window.L, window.proj4);
	}
}(function (L, proj4) {

L.Proj = {};

L.Proj._isProj4Obj = function(a) {
	return (typeof a['inverse'] !== 'undefined'
		&& typeof a['forward'] !== 'undefined');
};

L.Proj.ScaleDependantTransformation = function(scaleTransforms) {
		this.scaleTransforms = scaleTransforms;
	}

L.Proj.ScaleDependantTransformation.prototype.transform = function(point, scale) {
		return this.scaleTransforms[scale].transform(point, scale);
};

L.Proj.ScaleDependantTransformation.prototype.untransform = function(point, scale) {
		return this.scaleTransforms[scale].untransform(point, scale);
};

L.Proj.Projection = L.Class.extend({
	initialize: function(a, def) {
		if (L.Proj._isProj4Obj(a)) {
			this._proj = a;
		} else {
			var code = a;
			if (def) {
				proj4.defs(code, def);
			} else if (proj4.defs[code] === undefined) {
				var urn = code.split(':');
				if (urn.length > 3)
					code = urn[urn.length - 3] + ':' + urn[urn.length - 1];
				if (proj4.defs[code] === undefined)
					throw "No projection definition for code " + code;
			}
			this._proj = proj4(code);
		}
	},

	project: function (latlng) {
		var point = this._proj.forward([latlng.lng, latlng.lat]);
		return new L.Point(point[0], point[1]);
	},

	unproject: function (point, unbounded) {
		var point2 = this._proj.inverse([point.x, point.y]);
		return new L.LatLng(point2[1], point2[0], unbounded);
	}
});

L.Proj.CRS = L.Class.extend({
	includes: L.CRS,

	options: {
		transformation: new L.Transformation(1, 0, -1, 0)
	},

	initialize: function(a, b, c) {
		var code, proj, def, options;

		if (L.Proj._isProj4Obj(a)) {
			proj = a;
			code = proj.srsCode;
			options = b || {};

			this.projection = new L.Proj.Projection(proj);
		} else {
			code = a;
			def = b;
			options = c || {};
			this.projection = new L.Proj.Projection(code, def);
		}

		L.Util.setOptions(this, options);
		this.code = code;
		this.transformation = this.options.transformation;

		if (this.options.origin) {
			this.transformation =
				new L.Transformation(1, -this.options.origin[0],
					-1, this.options.origin[1]);
		}

		if (this.options.scales) {
			this.scale = function(zoom) {
				return this.options.scales[zoom];
			}
		} else if (this.options.resolutions) {
			this.scale = function(zoom) {
				return 1 / this.options.resolutions[zoom];
			}
		}
	}
});

L.Proj.CRS.TMS = L.Proj.CRS.extend({
	initialize: function(a, b, c, d) {
		if (L.Proj._isProj4Obj(a)) {
			var proj = a,
				projectedBounds = b,
				options = c || {};
			options.origin = [projectedBounds[0], projectedBounds[3]];
			L.Proj.CRS.prototype.initialize.call(this, proj, options);
		} else {
			var code = a,
				def = b,
				projectedBounds = c,
				options = d || {};
			options.origin = [projectedBounds[0], projectedBounds[3]];
			L.Proj.CRS.prototype.initialize.call(this, code, def, options);
		}

		this.projectedBounds = projectedBounds;
	}
});

L.Proj.TileLayer = {};

L.Proj.TileLayer.TMS = L.TileLayer.extend({
	options: {
		tms: true,
		continuousWorld: true
	},

	initialize: function(urlTemplate, crs, options) {
		var boundsMatchesGrid = true,
			scaleTransforms,
			upperY,
			crsBounds;

		if (!(crs instanceof L.Proj.CRS.TMS)) {
			throw new Error("CRS is not L.Proj.CRS.TMS.");
		}

		L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
		this.crs = crs;
		crsBounds = this.crs.projectedBounds;

		// Verify grid alignment
		for (var i = this.options.minZoom; i < this.options.maxZoom && boundsMatchesGrid; i++) {
			var gridHeight = (crsBounds[3] - crsBounds[1]) /
				this._projectedTileSize(i);
			boundsMatchesGrid = Math.abs(gridHeight - Math.round(gridHeight)) > 1e-3;
		}

		if (!boundsMatchesGrid) {
			scaleTransforms = {};
			for (var i = this.options.minZoom; i < this.options.maxZoom; i++) {
				upperY = crsBounds[1] + Math.ceil((crsBounds[3] - crsBounds[1]) /
					this._projectedTileSize(i)) * this._projectedTileSize(i);
				scaleTransforms[this.crs.scale(i)] = new L.Transformation(1, -crsBounds[0], -1, upperY);
			}

			this.crs = new L.Proj.CRS.TMS(this.crs.projection._proj, crsBounds, this.crs.options);
			this.crs.transformation = new L.Proj.ScaleDependantTransformation(scaleTransforms);
		}
	},

	getTileUrl: function(tilePoint) {
		// TODO: relies on some of TileLayer's internals
		var z = this._getZoomForUrl(),
			gridHeight = Math.ceil((this.crs.projectedBounds[3] - this.crs.projectedBounds[1]) / this._projectedTileSize(z));

		return L.Util.template(this._url, L.Util.extend({
			s: this._getSubdomain(tilePoint),
			z: z,
			x: tilePoint.x,
			y: gridHeight - tilePoint.y - 1
		}, this.options));
	},

	_projectedTileSize: function(zoom) {
		return (this.options.tileSize / this.crs.scale(zoom));
	}
});

L.Proj.GeoJSON = L.GeoJSON.extend({
	initialize: function(geojson, options) {
		if (geojson.crs && geojson.crs.type == 'name') {
			var crs = new L.Proj.CRS(geojson.crs.properties.name)
			options = options || {};
			options.coordsToLatLng = function(coords) {
				var point = L.point(coords[0], coords[1]);
				return crs.projection.unproject(point);
			};
		}
		L.GeoJSON.prototype.initialize.call(this, geojson, options);
	}
});

L.Proj.geoJson = function(geojson, options) {
	return new L.Proj.GeoJSON(geojson, options);
};

if (typeof L.CRS !== 'undefined') {
	// This is left here for backwards compatibility
	L.CRS.proj4js = (function () {
		return function (code, def, transformation, options) {
			options = options || {};
			if (transformation) options.transformation = transformation;

			return new L.Proj.CRS(code, def, options);
		};
	}());
}

return L.Proj;

}));
<|endoftext|>


<commit_before>/**
 * content.js
 */
"use strict";
{
  /* api */
  const {runtime} = browser;

  /* constants */
  const CONTEXT_INFO = "contextInfo";
  const CONTEXT_INFO_GET = "getContextInfo";
  const MOUSE_BUTTON_RIGHT = 2;
  const NS_HTML = "http://www.w3.org/1999/xhtml";

  /**
   * throw error
   * @param {!Object} e - Error
   * @throws
   */
  const throwErr = e => {
    throw e;
  };

  /**
   * send message
   * @param {*} msg - message
   * @returns {?AsyncFunction} - send message to runtime
   */
  const sendMsg = async msg => {
    const func = msg && runtime.sendMessage(msg);
    return func || null;
  };

  /**
   * get active element
   * @returns {Object} - active element
   */
  const getActiveElm = async () => {
    const sel = window.getSelection();
    const {anchorNode, focusNode, isCollapsed, rangeCount} = sel;
    let elm;
    if (!isCollapsed) {
      if (anchorNode === focusNode) {
        if (anchorNode.nodeType === Node.ELEMENT_NODE) {
          elm = anchorNode;
        } else {
          const root = document.documentElement;
          let parent = anchorNode.parentNode;
          while (parent && parent.parentNode && parent.parentNode !== root) {
            if (parent.nodeType === Node.ELEMENT_NODE) {
              elm = parent;
              break;
            }
            parent = parent.parentNode;
          }
        }
      } else if (rangeCount === 1) {
        const range = sel.getRangeAt(rangeCount - 1);
        if (range) {
          elm = range.commonAncestorContainer;
        }
      }
    }
    return elm || document.activeElement;
  };

  /**
   * get anchor element
   * @param {Object} node - element
   * @returns {Object} - anchor element
   */
  const getAnchorElm = async node => {
    const root = document.documentElement;
    let elm;
    if (root) {
      while (node && node.parentNode && node.parentNode !== root) {
        if (node.localName === "a") {
          elm = node;
          break;
        }
        node = node.parentNode;
      }
    }
    return elm || null;
  };

  /* context info */
  const contextInfo = {
    isLink: false,
    content: null,
    title: null,
    url: null,
    canonicalUrl: null,
  };

  /**
   * init context info
   * @returns {Object} - context info
   */
  const initContextInfo = async () => {
    contextInfo.isLink = false;
    contextInfo.content = null;
    contextInfo.title = null;
    contextInfo.url = null;
    contextInfo.canonicalUrl = null;
    return contextInfo;
  };

  /**
   * create context info
   * @param {Object} node - element
   * @returns {Object} - context info
   */
  const createContextInfo = async node => {
    await initContextInfo();
    if (node.nodeType === Node.ELEMENT_NODE) {
      const anchor = await getAnchorElm(node);
      const canonical = document.querySelector("link[rel=canonical][href]");
      if (anchor) {
        const {textContent, href, title} = anchor;
        if (href) {
          const content = textContent.replace(/\s+/g, " ").trim();
          const url = href instanceof SVGAnimatedString && href.baseVal || href;
          contextInfo.isLink = true;
          contextInfo.content = content;
          contextInfo.title = title || content;
          contextInfo.url = url;
        }
      }
      if (canonical) {
        contextInfo.canonicalUrl = canonical.getAttribute("href");
      }
    }
    return contextInfo;
  };

  /**
   * send status
   * @param {!Object} evt - Event
   * @returns {AsyncFunction} - send message
   */
  const sendStatus = async evt => {
    const {target, type} = evt;
    const enabled = document.documentElement.namespaceURI === NS_HTML;
    const info = await createContextInfo(target);
    const msg = {
      [type]: {
        enabled,
        contextInfo: info,
      },
    };
    return sendMsg(msg);
  };

  /**
   * send context info
   * @returns {AsyncFunction} - send message
   */
  const sendContextInfo = async () => {
    const elm = await getActiveElm();
    const info = await createContextInfo(elm);
    const msg = {
      [CONTEXT_INFO]: {
        contextInfo: info,
      },
    };
    return sendMsg(msg);
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Promise.<Array>} - results of each handler
   */
  const handleMsg = async (msg = {}) => {
    const items = msg && Object.keys(msg);
    const func = [];
    if (items && items.length) {
      for (const item of items) {
        switch (item) {
          case CONTEXT_INFO_GET:
            func.push(sendContextInfo());
            break;
          default:
        }
      }
    }
    return Promise.all(func);
  };

  /**
   * handle key / mouse event
   * @param {!Object} evt - Event
   * @returns {?AsyncFunction} - send status
   */
  const handleKeyMouseEvt = evt => {
    const {altKey, button, key, shiftKey, type} = evt;
    let func;
    switch (type) {
      case "keydown":
        if (altKey && shiftKey && key === "C" ||
            shiftKey && key === "F10" || key === "ContextMenu") {
          func = sendStatus(evt).catch(throwErr);
        }
        break;
      case "mousedown":
        if (button === MOUSE_BUTTON_RIGHT) {
          func = sendStatus(evt).catch(throwErr);
        }
        break;
      default:
    }
    return func || null;
  };

  /* listeners */
  runtime.onMessage.addListener(msg => handleMsg(msg).catch(throwErr));

  window.addEventListener("load", evt => sendStatus(evt).catch(throwErr));
  window.addEventListener("keydown", handleKeyMouseEvt, true);
  window.addEventListener("mousedown", handleKeyMouseEvt, true);
}
<commit_msg> Parse canonical url (#52)<commit_after>/**
 * content.js
 */
"use strict";
{
  /* api */
  const {runtime} = browser;

  /* constants */
  const CONTEXT_INFO = "contextInfo";
  const CONTEXT_INFO_GET = "getContextInfo";
  const MOUSE_BUTTON_RIGHT = 2;
  const NS_HTML = "http://www.w3.org/1999/xhtml";

  /**
   * throw error
   * @param {!Object} e - Error
   * @throws
   */
  const throwErr = e => {
    throw e;
  };

  /**
   * send message
   * @param {*} msg - message
   * @returns {?AsyncFunction} - send message to runtime
   */
  const sendMsg = async msg => {
    const func = msg && runtime.sendMessage(msg);
    return func || null;
  };

  /**
   * get active element
   * @returns {Object} - active element
   */
  const getActiveElm = async () => {
    const sel = window.getSelection();
    const {anchorNode, focusNode, isCollapsed, rangeCount} = sel;
    let elm;
    if (!isCollapsed) {
      if (anchorNode === focusNode) {
        if (anchorNode.nodeType === Node.ELEMENT_NODE) {
          elm = anchorNode;
        } else {
          const root = document.documentElement;
          let parent = anchorNode.parentNode;
          while (parent && parent.parentNode && parent.parentNode !== root) {
            if (parent.nodeType === Node.ELEMENT_NODE) {
              elm = parent;
              break;
            }
            parent = parent.parentNode;
          }
        }
      } else if (rangeCount === 1) {
        const range = sel.getRangeAt(rangeCount - 1);
        if (range) {
          elm = range.commonAncestorContainer;
        }
      }
    }
    return elm || document.activeElement;
  };

  /**
   * get anchor element
   * @param {Object} node - element
   * @returns {Object} - anchor element
   */
  const getAnchorElm = async node => {
    const root = document.documentElement;
    let elm;
    if (root) {
      while (node && node.parentNode && node.parentNode !== root) {
        if (node.localName === "a") {
          elm = node;
          break;
        }
        node = node.parentNode;
      }
    }
    return elm || null;
  };

  /* context info */
  const contextInfo = {
    isLink: false,
    content: null,
    title: null,
    url: null,
    canonicalUrl: null,
  };

  /**
   * init context info
   * @returns {Object} - context info
   */
  const initContextInfo = async () => {
    contextInfo.isLink = false;
    contextInfo.content = null;
    contextInfo.title = null;
    contextInfo.url = null;
    contextInfo.canonicalUrl = null;
    return contextInfo;
  };

  /**
   * create context info
   * @param {Object} node - element
   * @returns {Object} - context info
   */
  const createContextInfo = async node => {
    await initContextInfo();
    if (node.nodeType === Node.ELEMENT_NODE) {
      const anchor = await getAnchorElm(node);
      const canonical = document.querySelector("link[rel=canonical][href]");
      if (anchor) {
        const {textContent, href, title} = anchor;
        if (href) {
          const content = textContent.replace(/\s+/g, " ").trim();
          const url = href instanceof SVGAnimatedString && href.baseVal || href;
          contextInfo.isLink = true;
          contextInfo.content = content;
          contextInfo.title = title || content;
          contextInfo.url = url;
        }
      }
      if (canonical) {
        const {origin} = new URL(document.URL);
        const url = new URL(canonical.getAttribute("href"), origin);
        if (url) {
          contextInfo.canonicalUrl = url.href;
        }
      }
    }
    return contextInfo;
  };

  /**
   * send status
   * @param {!Object} evt - Event
   * @returns {AsyncFunction} - send message
   */
  const sendStatus = async evt => {
    const {target, type} = evt;
    const enabled = document.documentElement.namespaceURI === NS_HTML;
    const info = await createContextInfo(target);
    const msg = {
      [type]: {
        enabled,
        contextInfo: info,
      },
    };
    return sendMsg(msg);
  };

  /**
   * send context info
   * @returns {AsyncFunction} - send message
   */
  const sendContextInfo = async () => {
    const elm = await getActiveElm();
    const info = await createContextInfo(elm);
    const msg = {
      [CONTEXT_INFO]: {
        contextInfo: info,
      },
    };
    return sendMsg(msg);
  };

  /**
   * handle message
   * @param {*} msg - message
   * @returns {Promise.<Array>} - results of each handler
   */
  const handleMsg = async (msg = {}) => {
    const items = msg && Object.keys(msg);
    const func = [];
    if (items && items.length) {
      for (const item of items) {
        switch (item) {
          case CONTEXT_INFO_GET:
            func.push(sendContextInfo());
            break;
          default:
        }
      }
    }
    return Promise.all(func);
  };

  /**
   * handle key / mouse event
   * @param {!Object} evt - Event
   * @returns {?AsyncFunction} - send status
   */
  const handleKeyMouseEvt = evt => {
    const {altKey, button, key, shiftKey, type} = evt;
    let func;
    switch (type) {
      case "keydown":
        if (altKey && shiftKey && key === "C" ||
            shiftKey && key === "F10" || key === "ContextMenu") {
          func = sendStatus(evt).catch(throwErr);
        }
        break;
      case "mousedown":
        if (button === MOUSE_BUTTON_RIGHT) {
          func = sendStatus(evt).catch(throwErr);
        }
        break;
      default:
    }
    return func || null;
  };

  /* listeners */
  runtime.onMessage.addListener(msg => handleMsg(msg).catch(throwErr));

  window.addEventListener("load", evt => sendStatus(evt).catch(throwErr));
  window.addEventListener("keydown", handleKeyMouseEvt, true);
  window.addEventListener("mousedown", handleKeyMouseEvt, true);
}
<|endoftext|>


<commit_before>var $ = require('jquery-browserify');
var _ = require('underscore');
var Backbone = require('backbone');
var utils = require('./util');

module.exports = Backbone.Router.extend({

  routes: {
    'about(/)': 'about',
    'error/:code': 'error',
    ':user(/)': 'profile',
    ':user/:repo(/)': 'repo',
    ':user/:repo/*path(/)': 'path',
    '*default': 'start'
  },

  initialize: function(options) {
    this.model = options.model;
    this.eventRegister = app.eventRegister;

    // Load up the main layout
    this.application = new app.views.App({
      el: '#prose',
      model: this.model
    });
  },

  resetState: function() {
    app.state = {
      user: '',
      repo: '',
      mode: 'page',
      branch: '',
      path: '',
      file: ''
    };
  },

  about: function() {
    this.resetState();
    router.application.render({
      noMenu: true
    });

    var view = new app.views.Documentation({
      page: 'about'
    }).render();

    $('#content').empty().append(view.el);
  },

  // #example-user
  // #example-organization
  profile: function(user) {
    var router = this;
    utils.loader.loading('Loading Profile');

    // Clean any previous view
    this.eventRegister.trigger('remove');

    app.state = app.state || {};
    app.state.user = user;
    app.state.title = user;
    app.state.repo = '';
    app.state.mode = '';
    app.state.branch = '';
    app.state.path = '';
    app.state.file = '';

    app.models.loadRepos(user, function(err, data) {
      data.authenticated = !! window.authenticated;

      router.application.render();
      var view = new app.views.Profile({
        model: _.extend(router.model, data)
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    });
  },

  // #example-user/example-repo
  repo: function(user, repo) {
    var router = this;
    utils.loader.loading('Loading Posts');

    // Clean any previous view
    this.eventRegister.trigger('remove');

    app.state = {
      user: user,
      repo: repo,
      mode: 'tree',
      branch: '',
      path: '',
      file: ''
    };

    app.models.loadPosts(user, repo, app.state.branch, app.state.path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');

      router.application.render();
      var view = new app.views.Posts({
        model: data
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    }, this));
  },

  // #example-user/example-repo/tree/BRANCH
  repoBranch: function(user, repo, branch, path) {

    var router = this;
    utils.loader.loading('Loading Posts');

    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');
      router.application.render();

      var view = new app.views.Posts({
        model: data
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    }, this));
  },

  path: function(user, repo, path) {
    var parts;
    app.state.user = user;
    app.state.repo = repo;

    // Clean any previous view
    this.eventRegister.trigger('remove');
    url = _.extractURL(path);

    if (url.mode === 'tree') {
      this.repoBranch(user, repo, url.branch, url.path);
    } else if (url.mode === 'new') {
      this.newPost(user, repo, url.branch, url.path);
    } else if (url.mode === 'preview') {
      parts = _.extractFilename(url.path);
      app.state.file = parts[1];
      this.preview(user, repo, url.branch, parts[0], parts[1], url.mode);
    } else { // blob or edit ..
      parts = _.extractFilename(url.path);
      app.state.file = parts[1];
      this.post(user, repo, url.branch, parts[0], parts[1], url.mode);
    }
  },

  newPost: function (user, repo, branch, path) {
    // TODO Fix this, shouldn't have to pass
    // something like this here.
    app.state.markdown = true;

    utils.loader.loading('Creating a new post');
    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      app.models.emptyPost(user, repo, branch, path, _.bind(function (err, data) {

        data.jekyll = _.jekyll(path, data.file);
        data.preview = false;
        data.markdown = _.markdown(data.file);
        data.lang = _.mode(data.file);

        this.application.render({
          jekyll: data.jekyll,
          noMenu: true
        });

        var view = new app.views.Post({
          model: data
        }).render();

        utils.loader.loaded();
        $('#content').empty().append(view.el);
        app.state.file = data.file;

      }, this));
    }, this));
  },

  post: function(user, repo, branch, path, file, mode) {
    if (mode === 'edit') {
      utils.loader.loading('Loading Post');
    } else {
      utils.loader.loading('Previewing Post');
    }

    app.models.loadPosts(user, repo, branch, path, _.bind(function(err, data) {
      if (err) return this.notify('error', 'This file does not exist.');
      app.models.loadPost(user, repo, branch, path, file, _.bind(function(err, data) {
        if (err) return this.notify('error', 'This file does not exist.');

        app.state.markdown = data.markdown;
        data.jekyll = !!data.metadata;
        data.lang = _.mode(file);

        this.application.render({
          jekyll: data.jekyll,
          noMenu: true
        });

        var view = new app.views.Post({
          model: data
        }).render();

        utils.loader.loaded();
        $('#content').empty().html(view.el);
      }, this));
    }, this));
  },

  preview: function(user, repo, branch, path, file, mode) {
    var router = this;
    utils.loader.loading('Previewing Post');

    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');
      app.models.loadPost(user, repo, branch, path, file, _.bind(function (err, data) {
        if (err) {
          app.models.emptyPost(user, repo, branch, path, _.bind(cb, this));
        } else {
          cb(err, data);
        }

        function cb(err, data) {
          var view = new app.views.Preview({
            model: data
          }).render();

          utils.loader.loaded();
        }
      }, this));
    }, this));
  },

  start: function() {
    if (window.authenticated) {
      $('#start').remove();

      // Redirect
      router.navigate(app.username, {trigger: true});
    } else {
      this.application.render({
        hideInterface: true
      });

      var view = new app.views.Start({
        model: _.extend(this.model, {
          authenticated: !! window.authenticated
        })
      }).render();

      $('#content').empty();
      $('#prose').append(view.el);
    }
  },

  // if the application after routing
  // hits an error code router.navigate('error' + err.error)
  // sends the route here.
  error: function(code) {
    switch (code) {
      case '404':
        code = 'Page not Found'
      break;
      default:
        code = 'Error'
      break;
    }

    this.application.render({
      error: true
    });

    var view = new app.views.Notification({
      'type': 'Error',
      'key': 'error',
      'message': code
    }).render();

    utils.loader.loaded();
    $('#content').empty().append(view.el);
  },

  notify: function(type, message) {
    // TODO Fix this, shouldn't have to pass
    // something like this here.
    app.state.markdown = false;
    this.application.render({
      error: true
    });

    var view = new app.views.Notification({
      'type': type,
      'key': 'page-error',
      'message': message
    }).render();

    utils.loader.loaded();
    $('#content').empty().append(view.el);
  }
});
<commit_msg>linting the router<commit_after>var $ = require('jquery-browserify');
var _ = require('underscore');
var Backbone = require('backbone');
var utils = require('./util');

module.exports = Backbone.Router.extend({

  routes: {
    'about(/)': 'about',
    'error/:code': 'error',
    ':user(/)': 'profile',
    ':user/:repo(/)': 'repo',
    ':user/:repo/*path(/)': 'path',
    '*default': 'start'
  },

  initialize: function(options) {
    this.model = options.model;
    this.eventRegister = app.eventRegister;

    // Load up the main layout
    this.application = new app.views.App({
      el: '#prose',
      model: this.model
    });
  },

  resetState: function() {
    app.state = {
      user: '',
      repo: '',
      mode: 'page',
      branch: '',
      path: '',
      file: ''
    };
  },

  about: function() {
    this.resetState();
    router.application.render({
      noMenu: true
    });

    var view = new app.views.Documentation({
      page: 'about'
    }).render();

    $('#content').empty().append(view.el);
  },

  // #example-user
  // #example-organization
  profile: function(user) {
    var router = this;
    utils.loader.loading('Loading Profile');

    // Clean any previous view
    this.eventRegister.trigger('remove');

    app.state = app.state || {};
    app.state.user = user;
    app.state.title = user;
    app.state.repo = '';
    app.state.mode = '';
    app.state.branch = '';
    app.state.path = '';
    app.state.file = '';

    app.models.loadRepos(user, function(err, data) {
      data.authenticated = !! window.authenticated;

      router.application.render();
      var view = new app.views.Profile({
        model: _.extend(router.model, data)
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    });
  },

  // #example-user/example-repo
  repo: function(user, repo) {
    var router = this;
    utils.loader.loading('Loading Posts');

    // Clean any previous view
    this.eventRegister.trigger('remove');

    app.state = {
      user: user,
      repo: repo,
      mode: 'tree',
      branch: '',
      path: '',
      file: ''
    };

    app.models.loadPosts(user, repo, app.state.branch, app.state.path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');

      router.application.render();
      var view = new app.views.Posts({
        model: data
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    }, this));
  },

  // #example-user/example-repo/tree/BRANCH
  repoBranch: function(user, repo, branch, path) {

    var router = this;
    utils.loader.loading('Loading Posts');

    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');
      router.application.render();

      var view = new app.views.Posts({
        model: data
      }).render();

      utils.loader.loaded();
      $('#content').empty().append(view.el);
    }, this));
  },

  path: function(user, repo, path) {
    var parts;
    app.state.user = user;
    app.state.repo = repo;

    // Clean any previous view
    this.eventRegister.trigger('remove');
    url = _.extractURL(path);

    if (url.mode === 'tree') {
      this.repoBranch(user, repo, url.branch, url.path);
    } else if (url.mode === 'new') {
      this.newPost(user, repo, url.branch, url.path);
    } else if (url.mode === 'preview') {
      parts = _.extractFilename(url.path);
      app.state.file = parts[1];
      this.preview(user, repo, url.branch, parts[0], parts[1], url.mode);
    } else { // blob or edit ..
      parts = _.extractFilename(url.path);
      app.state.file = parts[1];
      this.post(user, repo, url.branch, parts[0], parts[1], url.mode);
    }
  },

  newPost: function (user, repo, branch, path) {
    // TODO Fix this, shouldn't have to pass
    // something like this here.
    app.state.markdown = true;

    utils.loader.loading('Creating a new post');
    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      app.models.emptyPost(user, repo, branch, path, _.bind(function (err, data) {

        data.jekyll = _.jekyll(path, data.file);
        data.preview = false;
        data.markdown = _.markdown(data.file);
        data.lang = _.mode(data.file);

        this.application.render({
          jekyll: data.jekyll,
          noMenu: true
        });

        var view = new app.views.Post({
          model: data
        }).render();

        utils.loader.loaded();
        $('#content').empty().append(view.el);
        app.state.file = data.file;

      }, this));
    }, this));
  },

  post: function(user, repo, branch, path, file, mode) {
    if (mode === 'edit') {
      utils.loader.loading('Loading Post');
    } else {
      utils.loader.loading('Previewing Post');
    }

    app.models.loadPosts(user, repo, branch, path, _.bind(function(err, data) {
      if (err) return this.notify('error', 'This file does not exist.');
      app.models.loadPost(user, repo, branch, path, file, _.bind(function(err, data) {
        if (err) return this.notify('error', 'This file does not exist.');

        app.state.markdown = data.markdown;
        data.jekyll = !!data.metadata;
        data.lang = _.mode(file);

        this.application.render({
          jekyll: data.jekyll,
          noMenu: true
        });

        var view = new app.views.Post({
          model: data
        }).render();

        utils.loader.loaded();
        $('#content').empty().html(view.el);
      }, this));
    }, this));
  },

  preview: function(user, repo, branch, path, file, mode) {
    var router = this;
    utils.loader.loading('Previewing Post');

    app.models.loadPosts(user, repo, branch, path, _.bind(function (err, data) {
      if (err) return router.notify('error', 'This post does not exist.');
      app.models.loadPost(user, repo, branch, path, file, _.bind(function (err, data) {
        if (err) {
          app.models.emptyPost(user, repo, branch, path, _.bind(cb, this));
        } else {
          cb(err, data);
        }

        function cb(err, data) {
          var view = new app.views.Preview({
            model: data
          }).render();

          utils.loader.loaded();
        }
      }, this));
    }, this));
  },

  start: function() {
    if (window.authenticated) {
      $('#start').remove();

      // Redirect
      router.navigate(app.username, {trigger: true});
    } else {
      this.application.render({
        hideInterface: true
      });

      var view = new app.views.Start({
        model: _.extend(this.model, {
          authenticated: !! window.authenticated
        })
      }).render();

      $('#content').empty();
      $('#prose').append(view.el);
    }
  },

  // if the application after routing
  // hits an error code router.navigate('error' + err.error)
  // sends the route here.
  error: function(code) {
    code = (code && code === '404') ? 'Page not Found.' : 'Error';

    this.application.render({
      error: true
    });

    var view = new app.views.Notification({
      'type': 'Error',
      'key': 'error',
      'message': code
    }).render();

    utils.loader.loaded();
    $('#content').empty().append(view.el);
  },

  notify: function(type, message) {
    // TODO Fix this, shouldn't have to pass
    // something like this here.
    app.state.markdown = false;
    this.application.render({
      error: true
    });

    var view = new app.views.Notification({
      'type': type,
      'key': 'page-error',
      'message': message
    }).render();

    utils.loader.loaded();
    $('#content').empty().append(view.el);
  }
});
<|endoftext|>