<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import type {
  Profile,
  Thread,
  IndexIntoRawMarkerTable,
} from 'firefox-profiler/types';

import * as React from 'react';
import { Provider } from 'react-redux';
import { render, fireEvent } from '@testing-library/react';

import { commitRange } from '../../actions/profile-view';
import TrackScreenshots from '../../components/timeline/TrackScreenshots';
import Timeline from '../../components/timeline';
import { ensureExists } from '../../utils/flow';
import { FULL_TRACK_SCREENSHOT_HEIGHT } from '../../app-logic/constants';

import mockCanvasContext from '../fixtures/mocks/canvas-context';
import mockRaf from '../fixtures/mocks/request-animation-frame';
import { storeWithProfile } from '../fixtures/stores';
import {
  getBoundingBox,
  getMouseEvent,
  addRootOverlayElement,
  removeRootOverlayElement,
  fireFullClick,
} from '../fixtures/utils';
import { getScreenshotTrackProfile } from '../fixtures/profiles/processed-profile';
import { getProfileWithNiceTracks } from '../fixtures/profiles/tracks';
import { getPreviewSelection } from '../../selectors/profile';
import { autoMockDomRect } from 'firefox-profiler/test/fixtures/mocks/domrect.js';

// Mock out the getBoundingBox to have a 400 pixel width.
const TRACK_WIDTH = 400;
const LEFT = 100;
const TOP = 7;

describe('timeline/TrackScreenshots', function() {
  autoMockDomRect();

  beforeEach(addRootOverlayElement);
  afterEach(removeRootOverlayElement);

  it('matches the component snapshot', () => {
    const { container, unmount } = setup();
    expect(container.firstChild).toMatchSnapshot();
    // Trigger any unmounting behavior handlers, just make sure it doesn't
    // throw any errors.
    unmount();
  });

  it('shows a hover when moving the mouse', () => {
    const { screenshotHover, moveMouse } = setup();

    expect(screenshotHover).toThrow();
    moveMouse(LEFT + 0);
    expect(screenshotHover()).toBeTruthy();
  });

  it('sets a preview selection when clicking with the mouse', () => {
    const { selectionOverlay, screenshotClick, getState } = setup(
      undefined,
      <Timeline />
    );
    expect(selectionOverlay).toThrow();
    screenshotClick(LEFT);

    const expectedPreviewSelection = {
      hasSelection: true,
      isModifying: false,
      selectionEnd: 1,
      selectionStart: 0,
    };
    expect(getPreviewSelection(getState())).toEqual(expectedPreviewSelection);
    expect(selectionOverlay()).toBeTruthy();
  });

  it('removes the hover when moving the mouse out', () => {
    const { screenshotHover, screenshotTrack, moveMouse } = setup();

    expect(screenshotHover).toThrow();

    moveMouse(LEFT + 0);
    expect(screenshotHover()).toBeTruthy();

    fireEvent.mouseLeave(screenshotTrack());
    expect(screenshotHover).toThrow();
  });

  it('moves the hover when moving the mouse', () => {
    const { moveMouseAndGetLeft } = setup();
    //Considering base to be 150, this value is big enough as a base
    const base = moveMouseAndGetLeft(150);
    // LEFT is 100 and the zoomed in screenshot's width is 350px.
    // So hovering between 100 and 175px should give a result of left == 0.
    // As soon as we pass 175px the left value should also move up.
    expect(moveMouseAndGetLeft(175 + 10)).toBe(base + 10);
    expect(moveMouseAndGetLeft(175 + 20)).toBe(base + 20);
  });

  it('places the hover image in the center of the track if there is enough space', () => {
    const { setBoundingClientRectOffset, moveMouseAndGetTop } = setup();
    const containerTop = 100;
    const screenshotHeight = 175; // This is the height of the zoomed-in screenshot.
    setBoundingClientRectOffset({ left: LEFT, top: containerTop });
    const pageX = LEFT;
    const expectedTop = Math.floor(
      containerTop + FULL_TRACK_SCREENSHOT_HEIGHT / 2 - screenshotHeight / 2
    );
    expect(moveMouseAndGetTop(pageX)).toBe(expectedTop);
  });

  it('makes sure the hover image does not go off the end of the container', () => {
    const { moveMouseAndGetLeft } = setup();
    const pageX = LEFT + TRACK_WIDTH - 1;
    expect(pageX > moveMouseAndGetLeft(pageX)).toBe(true);
  });

  it('makes sure the hover image does not go off the left side of screen', () => {
    const { moveMouseAndGetLeft } = setup();

    // Because the zoomed in hover screenshot's size is 350px, it's stuck at the
    // left of the window when the mouse is between 100 (the left of
    // the track) and 175px.
    expect(moveMouseAndGetLeft(100)).toBe(0);
    expect(moveMouseAndGetLeft(150)).toBe(0);
    expect(moveMouseAndGetLeft(175)).toBe(0);

    // Starting at 176px, the hover tooltip starts to move.
    expect(moveMouseAndGetLeft(176)).toBe(1);
  });

  it('makes sure the hover image does not go off the top side of screen', () => {
    const { moveMouseAndGetTop } = setup();
    const pageX = LEFT;
    expect(moveMouseAndGetTop(pageX)).toBe(0);
  });

  it('renders a screenshot images when zooming into a range without a screenshot start time actually in the range', () => {
    const profile = getScreenshotTrackProfile();
    const [thread] = profile.threads;
    const markerIndexA = thread.markers.length - 3;
    const markerIndexB = thread.markers.length - 2;
    // We keep the last marker so that the profile's root range is correct.

    _setScreenshotMarkersToUnknown(thread, markerIndexA, markerIndexB);

    const { dispatch, container } = setup(profile);
    dispatch(
      commitRange(
        ensureExists(thread.markers.startTime[markerIndexA]),
        ensureExists(thread.markers.startTime[markerIndexB])
      )
    );

    const firstImage = ensureExists(
      container.querySelector('.timelineTrackScreenshotImgContainer'),
      `Couldn't find at least one screenshot image.`
    );
    expect(parseInt(firstImage.style.left)).toBeGreaterThanOrEqual(0);
  });

  it('renders a no images when zooming into a range before screenshots', () => {
    const profile = getScreenshotTrackProfile();
    const [thread] = profile.threads;

    const markerIndexA = 0;
    const markerIndexB = 1;

    _setScreenshotMarkersToUnknown(thread, markerIndexA, markerIndexB);

    const { dispatch, container } = setup(profile);
    dispatch(
      commitRange(
        ensureExists(thread.markers.startTime[markerIndexA]),
        ensureExists(thread.markers.startTime[markerIndexB])
      )
    );
    expect(container.querySelector('.timelineTrackScreenshotImg')).toBeFalsy();
  });

  it('is created in the <Timeline /> with a profile with screenshots', function() {
    const { getAllByText } = setup(getScreenshotTrackProfile(), <Timeline />);

    // The function `getAllByText` throws already if none are found, with a useful Error,
    // if it can't find any elements. But we still use `expect` to keep a "test-like"
    // assertion, even if it's useless.
    expect(getAllByText('Screenshots').length).toBeGreaterThan(0);
  });

  it('is not created in the <Timeline /> with a profile with no screenshots', function() {
    const { queryByText } = setup(getProfileWithNiceTracks(), <Timeline />);
    expect(queryByText('Screenshots')).toBeFalsy();
  });
});

function setup(
  profile: Profile = getScreenshotTrackProfile(),
  component = <TrackScreenshots threadIndex={0} windowId="0" />
) {
  const store = storeWithProfile(profile);
  const { getState, dispatch } = store;
  const flushRafCalls = mockRaf();
  const ctx = mockCanvasContext();
  jest
    .spyOn(HTMLCanvasElement.prototype, 'getContext')
    .mockImplementation(() => ctx);
  let leftOffset = LEFT;
  let topOffset = TOP;
  jest
    .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
    .mockImplementation(() => {
      const rect = getBoundingBox(TRACK_WIDTH, FULL_TRACK_SCREENSHOT_HEIGHT);
      // Add some arbitrary X offset.
      rect.left += leftOffset;
      rect.right += leftOffset;
      rect.x += leftOffset;
      rect.y += topOffset;
      rect.top += topOffset;
      rect.bottom += topOffset;
      return rect;
    });

  function setBoundingClientRectOffset({
    left,
    top,
  }: {
    left: number,
    top: number,
  }) {
    leftOffset = left;
    topOffset = top;
  }

  const renderResult = render(<Provider store={store}>{component}</Provider>);
  const { container } = renderResult;

  // WithSize uses requestAnimationFrame
  flushRafCalls();

  function screenshotHover() {
    return ensureExists(
      document.querySelector('.timelineTrackScreenshotHover'),
      `Couldn't find the screenshot hover element, with selector .timelineTrackScreenshotHover`
    );
  }

  function selectionOverlay() {
    return ensureExists(
      document.querySelector('.timelineSelectionOverlay'),
      `Couldn't find the selection element, with selector .timelineSelectionOverlay`
    );
  }

  function screenshotClick(pageX: number) {
    fireFullClick(screenshotTrack(), { pageX, pageY: 0 });
  }

  function screenshotTrack() {
    return ensureExists(
      container.querySelector('.timelineTrackScreenshot'),
      `Couldn't find the screenshot track, with selector .timelineTrackScreenshot`
    );
  }

  function moveMouse(pageX: number) {
    fireEvent(
      screenshotTrack(),
      getMouseEvent('mousemove', { pageX, pageY: 0 })
    );
  }

  function moveMouseAndGetLeft(pageX: number): number {
    moveMouse(pageX);
    return parseInt(screenshotHover().style.left);
  }

  function moveMouseAndGetTop(pageX: number): number {
    moveMouse(pageX);
    return parseInt(screenshotHover().style.top);
  }

  return {
    ...renderResult,
    dispatch,
    getState,
    thread: profile.threads[0],
    store,
    screenshotHover,
    screenshotTrack,
    moveMouse,
    moveMouseAndGetLeft,
    moveMouseAndGetTop,
    setBoundingClientRectOffset,
    selectionOverlay,
    screenshotClick,
  };
}

/**
 * Take a thread full screenshot markers, and set some to "Unknown" in order to
 * create gaps in a screenshot track.
 */
function _setScreenshotMarkersToUnknown(
  thread: Thread,
  ...markerIndexes: IndexIntoRawMarkerTable[]
) {
  // Remove off the last few screenshot markers
  const unknownStringIndex = thread.stringTable.indexForString('Unknown');
  const screenshotStringIndex = thread.stringTable.indexForString(
    'CompositorScreenshot'
  );
  for (const markerIndex of markerIndexes) {
    // Double check that we've actually got screenshot markers:
    if (thread.markers.name[markerIndex] !== screenshotStringIndex) {
      throw new Error('This is not a screenshot marker.');
    }
    thread.markers.name[markerIndex] = unknownStringIndex;
    thread.markers.data[markerIndex] = null;
  }
}
<commit_msg>Add a test for the PreviewSelection functionality<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// @flow
import type {
  Profile,
  Thread,
  IndexIntoRawMarkerTable,
} from 'firefox-profiler/types';

import * as React from 'react';
import { Provider } from 'react-redux';
import { render, fireEvent } from '@testing-library/react';

import { commitRange } from '../../actions/profile-view';
import TrackScreenshots from '../../components/timeline/TrackScreenshots';
import Timeline from '../../components/timeline';
import { ensureExists } from '../../utils/flow';
import { FULL_TRACK_SCREENSHOT_HEIGHT } from '../../app-logic/constants';

import mockCanvasContext from '../fixtures/mocks/canvas-context';
import mockRaf from '../fixtures/mocks/request-animation-frame';
import { storeWithProfile } from '../fixtures/stores';
import {
  getBoundingBox,
  getMouseEvent,
  addRootOverlayElement,
  removeRootOverlayElement,
  fireFullClick,
} from '../fixtures/utils';
import { getScreenshotTrackProfile } from '../fixtures/profiles/processed-profile';
import { getProfileWithNiceTracks } from '../fixtures/profiles/tracks';
import { getPreviewSelection } from '../../selectors/profile';
import { autoMockDomRect } from 'firefox-profiler/test/fixtures/mocks/domrect.js';

// Mock out the getBoundingBox to have a 400 pixel width.
const TRACK_WIDTH = 400;
const LEFT = 100;
const TOP = 7;

describe('timeline/TrackScreenshots', function() {
  autoMockDomRect();

  beforeEach(addRootOverlayElement);
  afterEach(removeRootOverlayElement);

  it('matches the component snapshot', () => {
    const { container, unmount } = setup();
    expect(container.firstChild).toMatchSnapshot();
    // Trigger any unmounting behavior handlers, just make sure it doesn't
    // throw any errors.
    unmount();
  });

  it('shows a hover when moving the mouse', () => {
    const { screenshotHover, moveMouse } = setup();

    expect(screenshotHover).toThrow();
    moveMouse(LEFT + 0);
    expect(screenshotHover()).toBeTruthy();
  });

  it('sets a preview selection when clicking with the mouse', () => {
    const { selectionOverlay, screenshotClick, getState } = setup(
      undefined,
      <Timeline />
    );
    expect(selectionOverlay).toThrow();
    screenshotClick(LEFT);

    const expectedPreviewSelection = {
      hasSelection: true,
      isModifying: false,
      selectionEnd: 1,
      selectionStart: 0,
    };
    expect(getPreviewSelection(getState())).toEqual(expectedPreviewSelection);
    expect(selectionOverlay()).toBeTruthy();
  });

  it('does not change the preview selection when clicking if a selection is already present', () => {
    const { selectionOverlay, screenshotTrack, getState } = setup(
      undefined,
      <Timeline />
    );

    const track = screenshotTrack();
    expect(selectionOverlay).toThrow();

    // Mousedown then Mousemove will do a selection.
    fireEvent(track, getMouseEvent('mousedown', { pageX: LEFT, pageY: TOP }));
    fireEvent(
      track,
      getMouseEvent('mousemove', { pageX: LEFT + 200, pageY: TOP })
    );

    expect(selectionOverlay()).toBeTruthy();
    const selectedPreviewSelection = getPreviewSelection(getState());

    // Mouseup should keep this selection.
    fireEvent(
      track,
      getMouseEvent('mouseup', { pageX: LEFT + 200, pageY: TOP })
    );
    fireEvent(track, getMouseEvent('click', { pageX: LEFT + 200, pageY: TOP }));
    expect(getPreviewSelection(getState())).toEqual({
      ...selectedPreviewSelection,
      isModifying: false,
    });
    expect(selectionOverlay()).toBeTruthy();
  });

  it('removes the hover when moving the mouse out', () => {
    const { screenshotHover, screenshotTrack, moveMouse } = setup();

    expect(screenshotHover).toThrow();

    moveMouse(LEFT + 0);
    expect(screenshotHover()).toBeTruthy();

    fireEvent.mouseLeave(screenshotTrack());
    expect(screenshotHover).toThrow();
  });

  it('moves the hover when moving the mouse', () => {
    const { moveMouseAndGetLeft } = setup();
    //Considering base to be 150, this value is big enough as a base
    const base = moveMouseAndGetLeft(150);
    // LEFT is 100 and the zoomed in screenshot's width is 350px.
    // So hovering between 100 and 175px should give a result of left == 0.
    // As soon as we pass 175px the left value should also move up.
    expect(moveMouseAndGetLeft(175 + 10)).toBe(base + 10);
    expect(moveMouseAndGetLeft(175 + 20)).toBe(base + 20);
  });

  it('places the hover image in the center of the track if there is enough space', () => {
    const { setBoundingClientRectOffset, moveMouseAndGetTop } = setup();
    const containerTop = 100;
    const screenshotHeight = 175; // This is the height of the zoomed-in screenshot.
    setBoundingClientRectOffset({ left: LEFT, top: containerTop });
    const pageX = LEFT;
    const expectedTop = Math.floor(
      containerTop + FULL_TRACK_SCREENSHOT_HEIGHT / 2 - screenshotHeight / 2
    );
    expect(moveMouseAndGetTop(pageX)).toBe(expectedTop);
  });

  it('makes sure the hover image does not go off the end of the container', () => {
    const { moveMouseAndGetLeft } = setup();
    const pageX = LEFT + TRACK_WIDTH - 1;
    expect(pageX > moveMouseAndGetLeft(pageX)).toBe(true);
  });

  it('makes sure the hover image does not go off the left side of screen', () => {
    const { moveMouseAndGetLeft } = setup();

    // Because the zoomed in hover screenshot's size is 350px, it's stuck at the
    // left of the window when the mouse is between 100 (the left of
    // the track) and 175px.
    expect(moveMouseAndGetLeft(100)).toBe(0);
    expect(moveMouseAndGetLeft(150)).toBe(0);
    expect(moveMouseAndGetLeft(175)).toBe(0);

    // Starting at 176px, the hover tooltip starts to move.
    expect(moveMouseAndGetLeft(176)).toBe(1);
  });

  it('makes sure the hover image does not go off the top side of screen', () => {
    const { moveMouseAndGetTop } = setup();
    const pageX = LEFT;
    expect(moveMouseAndGetTop(pageX)).toBe(0);
  });

  it('renders a screenshot images when zooming into a range without a screenshot start time actually in the range', () => {
    const profile = getScreenshotTrackProfile();
    const [thread] = profile.threads;
    const markerIndexA = thread.markers.length - 3;
    const markerIndexB = thread.markers.length - 2;
    // We keep the last marker so that the profile's root range is correct.

    _setScreenshotMarkersToUnknown(thread, markerIndexA, markerIndexB);

    const { dispatch, container } = setup(profile);
    dispatch(
      commitRange(
        ensureExists(thread.markers.startTime[markerIndexA]),
        ensureExists(thread.markers.startTime[markerIndexB])
      )
    );

    const firstImage = ensureExists(
      container.querySelector('.timelineTrackScreenshotImgContainer'),
      `Couldn't find at least one screenshot image.`
    );
    expect(parseInt(firstImage.style.left)).toBeGreaterThanOrEqual(0);
  });

  it('renders a no images when zooming into a range before screenshots', () => {
    const profile = getScreenshotTrackProfile();
    const [thread] = profile.threads;

    const markerIndexA = 0;
    const markerIndexB = 1;

    _setScreenshotMarkersToUnknown(thread, markerIndexA, markerIndexB);

    const { dispatch, container } = setup(profile);
    dispatch(
      commitRange(
        ensureExists(thread.markers.startTime[markerIndexA]),
        ensureExists(thread.markers.startTime[markerIndexB])
      )
    );
    expect(container.querySelector('.timelineTrackScreenshotImg')).toBeFalsy();
  });

  it('is created in the <Timeline /> with a profile with screenshots', function() {
    const { getAllByText } = setup(getScreenshotTrackProfile(), <Timeline />);

    // The function `getAllByText` throws already if none are found, with a useful Error,
    // if it can't find any elements. But we still use `expect` to keep a "test-like"
    // assertion, even if it's useless.
    expect(getAllByText('Screenshots').length).toBeGreaterThan(0);
  });

  it('is not created in the <Timeline /> with a profile with no screenshots', function() {
    const { queryByText } = setup(getProfileWithNiceTracks(), <Timeline />);
    expect(queryByText('Screenshots')).toBeFalsy();
  });
});

function setup(
  profile: Profile = getScreenshotTrackProfile(),
  component = <TrackScreenshots threadIndex={0} windowId="0" />
) {
  const store = storeWithProfile(profile);
  const { getState, dispatch } = store;
  const flushRafCalls = mockRaf();
  const ctx = mockCanvasContext();
  jest
    .spyOn(HTMLCanvasElement.prototype, 'getContext')
    .mockImplementation(() => ctx);
  let leftOffset = LEFT;
  let topOffset = TOP;
  jest
    .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
    .mockImplementation(() => {
      const rect = getBoundingBox(TRACK_WIDTH, FULL_TRACK_SCREENSHOT_HEIGHT);
      // Add some arbitrary X offset.
      rect.left += leftOffset;
      rect.right += leftOffset;
      rect.x += leftOffset;
      rect.y += topOffset;
      rect.top += topOffset;
      rect.bottom += topOffset;
      return rect;
    });

  jest.spyOn(HTMLElement.prototype, 'getClientRects').mockImplementation(() => {
    return [
      new DOMRect(
        LEFT,
        TOP,
        LEFT + TRACK_WIDTH,
        TOP + FULL_TRACK_SCREENSHOT_HEIGHT
      ),
    ];
  });

  function setBoundingClientRectOffset({
    left,
    top,
  }: {
    left: number,
    top: number,
  }) {
    leftOffset = left;
    topOffset = top;
  }

  const renderResult = render(<Provider store={store}>{component}</Provider>);
  const { container } = renderResult;

  // WithSize uses requestAnimationFrame
  flushRafCalls();

  function screenshotHover() {
    return ensureExists(
      document.querySelector('.timelineTrackScreenshotHover'),
      `Couldn't find the screenshot hover element, with selector .timelineTrackScreenshotHover`
    );
  }

  function selectionOverlay() {
    return ensureExists(
      document.querySelector('.timelineSelectionOverlay'),
      `Couldn't find the selection element, with selector .timelineSelectionOverlay`
    );
  }

  function screenshotClick(pageX: number) {
    fireFullClick(screenshotTrack(), { pageX, pageY: TOP });
  }

  function screenshotTrack() {
    return ensureExists(
      container.querySelector('.timelineTrackScreenshot'),
      `Couldn't find the screenshot track, with selector .timelineTrackScreenshot`
    );
  }

  function moveMouse(pageX: number) {
    fireEvent(
      screenshotTrack(),
      getMouseEvent('mousemove', { pageX, pageY: TOP })
    );
  }

  function moveMouseAndGetLeft(pageX: number): number {
    moveMouse(pageX);
    return parseInt(screenshotHover().style.left);
  }

  function moveMouseAndGetTop(pageX: number): number {
    moveMouse(pageX);
    return parseInt(screenshotHover().style.top);
  }

  return {
    ...renderResult,
    dispatch,
    getState,
    thread: profile.threads[0],
    store,
    screenshotHover,
    screenshotTrack,
    moveMouse,
    moveMouseAndGetLeft,
    moveMouseAndGetTop,
    setBoundingClientRectOffset,
    selectionOverlay,
    screenshotClick,
  };
}

/**
 * Take a thread full screenshot markers, and set some to "Unknown" in order to
 * create gaps in a screenshot track.
 */
function _setScreenshotMarkersToUnknown(
  thread: Thread,
  ...markerIndexes: IndexIntoRawMarkerTable[]
) {
  // Remove off the last few screenshot markers
  const unknownStringIndex = thread.stringTable.indexForString('Unknown');
  const screenshotStringIndex = thread.stringTable.indexForString(
    'CompositorScreenshot'
  );
  for (const markerIndex of markerIndexes) {
    // Double check that we've actually got screenshot markers:
    if (thread.markers.name[markerIndex] !== screenshotStringIndex) {
      throw new Error('This is not a screenshot marker.');
    }
    thread.markers.name[markerIndex] = unknownStringIndex;
    thread.markers.data[markerIndex] = null;
  }
}
<|endoftext|>


<commit_before>var resolveAppliedStyle = (function () {
    var documentElement = document.documentElement,
        matchesSelector =
            documentElement.matchesSelector ||
            documentElement.mozMatchesSelector ||
            documentElement.webkitMatchesSelector ||
            documentElement.msMatchesSelector ||
            documentElement.oMatchesSelector;

    if (!matchesSelector) {
        if (document.querySelectorAll) {
            matchesSelector = function (selector) {
                var matchingElements = this.parentNode.querySelectorAll(selector),
                    count = matchingElements.length;

                for (var i = 0; i < count; ++i) {
                    if (matchingElements[i] === this) {
                        return true;
                    }
                }
                return false;
            };
        }
        else {
            throw new Error("no browser support for selector matching");
        }
    }

    function styleImportanceByRule(rule, styleProperty) {
        if (rule.style.getPropertyPriority) {
            return rule.style.getPropertyPriority(styleProperty) === "important" ? 100000 : 0;
        }
        else {
            return 0;
        }

    }

    function styleImportanceByStyleText(styleText) {
        return styleText.match(/\s!important/gi) ? 100000 : 0;
    }

    function resolveAppliedStyle(element, style) {

        var styleSheets = document.styleSheets,
            numberOfStyleSheets = styleSheets.length,
            styleSheet,
            styleSheetIndex,
            rule,
            rules,
            numberOfRules,
            ruleIndex,
            appliedRule,
            scoredStyles = [],
            inlineStyle,
            potentiallyAppliedStyle,
            cssStyleSpecificityScore,
            styleSpecificity,
            matchesSelectorResult;

        for (styleSheetIndex = 0; styleSheetIndex < numberOfStyleSheets; ++styleSheetIndex) {
            styleSheet = styleSheets[styleSheetIndex];

            rules = styleSheet.cssRules || styleSheet.rules;

            if (!rules) {
                continue;
            }

            numberOfRules = rules.length;

            for (ruleIndex = 0; ruleIndex < numberOfRules; ++ruleIndex) {
                rule = rules[ruleIndex];
                try {
                    matchesSelectorResult = matchesSelector.call(element, rule.selectorText);
                }
                catch (err) {
                    // console.log("invalid call to matchesSelector using string '", rule.selectorText, "', error >> ",err);
                    matchesSelectorResult = false;
                }

                if (matchesSelectorResult) {
                    potentiallyAppliedStyle = rule.style[style];
                    if (potentiallyAppliedStyle !== undefined) {

                        cssStyleSpecificityScore = +(SPECIFICITY.calculate(rule.selectorText)[0]
                            .specificity
                            .replace(/,/g, ""));

                        styleSpecificity =
                            scoredStyles.length +
                            (cssStyleSpecificityScore * 10) +
                            styleImportanceByRule(rule, style);

                        scoredStyles.push({
                            "selector": rule.selectorText,
                            "specificity": styleSpecificity,
                            "style": potentiallyAppliedStyle
                        });
                    }
                }
            }
        }

        inlineStyle = element.style[style];

        if (inlineStyle) {
            scoredStyles.push({
                "selector": "! this is an inline style !",
                "specificity": 10000 + styleImportanceByStyleText(inlineStyle),
                "style": inlineStyle
            });
        }

        if (!scoredStyles.length) {
            return undefined;
        }

        scoredStyles.sort(function (a, b) {
            if (a.specificity > b.specificity) {
                return -1;
            }
            if (a.specificity < b.specificity) {
                return 1;
            }
            return 0;
        });

        return scoredStyles[0].style;
    }

    var SPECIFICITY = (function () {

        var calculate,
            calculateSingle;

        calculate = function (input) {

            var selectors,
                selector,
                i,
                len,
                results = [];

// Separate input by commas

            selectors = input.split(',');

            for (i = 0, len = selectors.length; i < len; i += 1) {

                selector = selectors[i];

                if (selector.length > 0) {

                    results.push(calculateSingle(selector));

                }

            }

            return results;

        };

// Calculate the specificity for a selector by dividing it into simple selectors and counting them

        calculateSingle = function (input) {

            var selector = input,
                findMatch,
                typeCount = {
                    'a': 0,
                    'b': 0,
                    'c': 0

                },
                parts = [],
// The following regular expressions assume that selectors matching the preceding regular expressions have been removed

                attributeRegex = /(\[[^\]]+\])/g,
                idRegex = /(#[^\s\+>~\.\[:]+)/g,
                classRegex = /(\.[^\s\+>~\.\[:]+)/g,
                pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi,
// A regex for pseudo classes with brackets - :nth-child(), :nth-last-child(), :nth-of-type(), :nth-last-type(), :lang()

                pseudoClassWithBracketsRegex = /(:[\w-]+\([^\)]*\))/gi,
// A regex for other pseudo classes, which don't have brackets

                pseudoClassRegex = /(:[^\s\+>~\.\[:]+)/g,
                elementRegex = /([^\s\+>~\.\[:]+)/g;

// Find matches for a regular expression in a string and push their details to parts

// Type is "a" for IDs, "b" for classes, attributes and pseudo-classes and "c" for elements and pseudo-elements

            findMatch = function (regex, type) {

                var matches, i, len, match, index, length;

                if (regex.test(selector)) {

                    matches = selector.match(regex);

                    for (i = 0, len = matches.length; i < len; i += 1) {

                        typeCount[type] += 1;

                        match = matches[i];

                        index = selector.indexOf(match);

                        length = match.length;

                        parts.push({
                            selector: match,
                            type: type,
                            index: index,
                            length: length

                        });

// Replace this simple selector with whitespace so it won't be counted in further simple selectors

                        selector = selector.replace(match, Array(length + 1).join(' '));

                    }

                }

            };

// Remove the negation psuedo-class (:not) but leave its argument because specificity is calculated on its argument

            (function () {

                var regex = /:not\(([^\)]*)\)/g;

                if (regex.test(selector)) {

                    selector = selector.replace(regex, '     $1 ');

                }

            }());

// Remove anything after a left brace in case a user has pasted in a rule, not just a selector

            (function () {

                var regex = /{[^]*/gm,
                    matches, i, len, match;

                if (regex.test(selector)) {

                    matches = selector.match(regex);

                    for (i = 0, len = matches.length; i < len; i += 1) {

                        match = matches[i];

                        selector = selector.replace(match, Array(match.length + 1).join(' '));

                    }

                }

            }());

// Add attribute selectors to parts collection (type b)

            findMatch(attributeRegex, 'b');

// Add ID selectors to parts collection (type a)

            findMatch(idRegex, 'a');

// Add class selectors to parts collection (type b)

            findMatch(classRegex, 'b');

// Add pseudo-element selectors to parts collection (type c)

            findMatch(pseudoElementRegex, 'c');

// Add pseudo-class selectors to parts collection (type b)

            findMatch(pseudoClassWithBracketsRegex, 'b');

            findMatch(pseudoClassRegex, 'b');

// Remove universal selector and separator characters

            selector = selector.replace(/[\*\s\+>~]/g, ' ');

// Remove any stray dots or hashes which aren't attached to words

// These may be present if the user is live-editing this selector

            selector = selector.replace(/[#\.]/g, ' ');

// The only things left should be element selectors (type c)

            findMatch(elementRegex, 'c');

// Order the parts in the order they appear in the original selector

// This is neater for external apps to deal with

            parts.sort(function (a, b) {

                return a.index - b.index;

            });

            return {
                selector: input,
                specificity: '0,' + typeCount.a.toString() + ',' + typeCount.b.toString() + ',' + typeCount.c.toString(),
                parts: parts

            };

        };

        return {
            calculate: calculate

        };
    }());
    return resolveAppliedStyle;
})();

/*
 * determine whether forbiden units ["pt", "pc", "mm", "cm" , "in"] are used or not
 */
var attrs = ["font-size", "width", "height", "min-width", "min-height", "max-width", "max-height", "left", "right", "top", "bottom",
    "background-size", "margin", "margin-top", "margin-bottom", "margin-left", "margin-right", "padding", "padding-top",
    "padding-bottom", "padding-left", "padding-right", "border", "border-top", "border-bottom", "border-left", "border-right",
    "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-left-radius",
    "border-bottom-right-radius", "text-shadow"];

//attrs = ["font-size", "border"];

var forbiddenUnits = ["pt", "pc", "mm", "cm", "in"];
var styleValueUnit;

//a voir si n'est plus utilisée
function isForbiddenUnitUsed(elem) {
    var forbiddenUnitUsed = false;
    for (var i = 0; i < attrs.length; i++) {
        if (forbiddenUnitUsed) {
            break;
        }
        styleValueUnit = resolveAppliedStyle(elem, attrs[i]) || "";  //  getStyle(elem , "" +attrs[i]);
        if (styleValueUnit !== null && styleValueUnit !== "") {
            for (var j = 0; j < forbiddenUnits.length; j++) {
                if (styleValueUnit.indexOf("" + forbiddenUnits[j]) > -1) {
                    forbiddenUnitUsed = true;
                    break;
                }

            }
        }
    }
    return forbiddenUnitUsed;
}

function isForbiddenUnitUsed2(elem) {
    var forbiddenUnitUsed = false;
    for (var x = 0; x < elementWithForbiddenUnitsRoot.length; x++) {
        if (elementWithForbiddenUnitsRoot[x] === elem) {//(elementsWithForbiddenUnits.indexOf(elem) !== -1) {
            forbiddenUnitUsed = true;
        }
    }
    return forbiddenUnitUsed;
}

/*
 * determine whether authorized units [%, em, rem, vw, vh, vmin , vmax , xx-small, x-small, small, medium, large, x-large, xx-large, xsmaller, larger] are used or not
 */
var attrs = ["font-size", "width", "height", "min-width", "min-height", "max-width", "max-height", "left", "right", "top", "bottom",
    "background-size", "margin", "margin-top", "margin-bottom", "margin-left", "margin-right", "padding", "padding-top", "line-height",
    "padding-bottom", "padding-left", "padding-right", "border", "border-top", "border-top-width", "border-bottom", "border-left", "border-right",
    "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-left-radius",
    "border-bottom-right-radius", "text-shadow", "outline-width", "vertical-align"];

//attrs = ["font-size", "border"];

var authorizedUnits = ["%", "em", "rem", "vw", "vh", "vmin" , "vmax" , "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xsmaller", "larger",
    "pt", "pc", "mm", "cm", "in"]; //not to be compute twice unrelative units
var styleValueUnit;

function isAuthorizedUnitUsed(elem) {
    var authorizedUnitsUsed = false;
    for (var i = 0; i < attrs.length; i++) {
        if (authorizedUnitsUsed) {
            break;
        }
        styleValueUnit = resolveAppliedStyle(elem, attrs[i]) || "";  //  getStyle(elem , "" +attrs[i]);
        if (styleValueUnit !== null && styleValueUnit !== "") {
            for (var j = 0; j < authorizedUnits.length; j++) {
                if (styleValueUnit.indexOf("" + authorizedUnits[j]) > -1) {
                    authorizedUnitsUsed = true;
                    break;
                }

            }
        }
    }
    return authorizedUnitsUsed;
}

function isNotAuthorizedUnitUsed2(elem) {
    var authorizedUnitsUsed = false;
    for (var x = 0; x < elementsWithoutAuthorizedUnits.length; x++) {
        if (elementsWithoutAuthorizedUnits[x] === elem) {
            authorizedUnitsUsed = true;
        }
    }
    return authorizedUnitsUsed;
}

/*
 * determine whether a node is a text node
 */
function isTextNode(elem) {
    if (isElementOfType(elem, 'input') && elem.hasAttribute('type') && (elem.getAttribute('type') === 'text')) {
        return true;
    }
    if (isElementOfType(elem, 'iframe')) {
        return false;
    }
    if (isElementOfType(elem, 'noscript')) {
        return false;
    }
    if (isElementOfType(elem, 'script')) {
        return false;
    }
    if (isElementOfType(elem, 'style')) {
        return false;
    }
    if (isElementOfType(elem, 'img') && elem.hasAttribute('alt') && (elem.getAttribute('alt').trim().length > 0)) {
        return true;
    }
    for (var i = 0; i < elem.childNodes.length; i++) {
        if (elem.childNodes[i].nodeName === "#text" &&
            elem.childNodes[i].textContent.trim().length > 0) {
            return true;
        }
    }
    return false;
}
;

/*
 * Build a unique css Path from the parent path and the current element index
 */
function buildPath(elem, parentPath, index) {
//    if (elem.id) {
//        return "#" + elem.id;
//    }
    var name = getElementName(elem);

    if (name === 'body') {
        return name;
    } else {
        name += ':eq(' + index + ')';
        return (parentPath ? parentPath + ' > ' + name : '' + name);
    }
}

/*
 * Replace comma in color string.
 */
function formatColor(color) {
    while (color.indexOf(',') > -1) {
        color = color.replace(',', ';');
    }
    return color;
}

/*
 * compute the background color of a node
 */
function getBackgroundColor(elem) {
    var bgImg = getStyle(elem, 'background-image');
    if (bgImg !== 'none') {
        while (bgImg.indexOf('"') > -1) {
            bgImg = bgImg.replace('"', '\'');
        }
        return "background-image:" + bgImg;
    }
    var bgColor = getStyle(elem, 'background-color');
    return formatColor(bgColor);
}

/*
 * compute the foreground color of a node
 */
function getForegroundColor(elem) {
    var color = getStyle(elem, 'color');
    return formatColor(color);
    ;
}

/*
 * determine whether a node is hidden
 */
function isHidden(elem) {
    var isElementHidden =
        (getStyle(elem, 'display') === 'none') ||
        (getStyle(elem, 'visibility') === 'hidden');
    if (!isElementHidden && !isElementOfType(getElementName(elem), 'html')) {
        return isHidden(elem.parentNode);
    }
    return isElementHidden;
}

/*
 * get the computed style of an element
 */
function getStyle(elem, strCssRule, pseudoSelector) {
    var style = "";
    if (elem.currentStyle) {
        style = elem.currentStyle[strCssRule];
    } else if (window.getComputedStyle) {
        style = document.defaultView.getComputedStyle(elem, pseudoSelector).
        getPropertyValue(strCssRule);
    }
    return style;
}

/*
 * get the element name
 */
function getElementName(elem) {
    return elem.tagName.toLowerCase();
}

/*
 * determine whether an element is of a given type
 */
function isElementOfType(elemName, typeName) {
    if (elemName === typeName) {
        return true;
    }
    return false;
}

function isAllowedElement(elem) {
    var tagName = getElementName(elem);
    if (isElementOfType(tagName, 'script')) {
        return false;
    }
    if (isElementOfType(tagName, 'noscript')) {
        return false;
    }
    if (isElementOfType(tagName, 'br')) {
        return false;
    }
    if (isElementOfType(tagName, 'svg')) {
        return false;
    }
    if (isElementOfType(tagName, 'head')) {
        return false;
    }
    if (isElementOfType(tagName, 'style')) {
        return false;
    }
    if (isElementOfType(tagName, 'meta')) {
        return false;
    }
    if (isElementOfType(tagName, 'link')) {
        return false;
    }
    if (isElementOfType(tagName, 'title')) {
        return false;
    }
    if (isElementOfType(tagName, 'option')) {
        return false;
    }
    return true;
}
;

/*
 * extract info from each element of the dom
 */
function extractInfo(elem, parentFgColor, parentBgColor, result, parentPath, elemIndex) {
    if (isAllowedElement(elem)) {
        var path, element = {}, bgColor, color, children, focus;
        bgColor = getBackgroundColor(elem);
        if ((bgColor === 'transparent' || bgColor === 'rgba(0; 0; 0; 0)')) {
            if (parentBgColor !== null) {
                //  console.log(parentBgColor);
                bgColor = parentBgColor;
            } else if (isElementOfType(getElementName(elem), 'body')) {
                bgColor = 'rgb(255; 255; 255)';
            }
        }
        //  console.log(bgColor);
        color = getForegroundColor(elem);
        if ((color === 'transparent' || color === 'rgba(0; 0; 0; 0)') && parentFgColor !== null) {
            color = parentFgColor;
        }

        path = buildPath(elem, parentPath, elemIndex);

        element = {};
        element.path = '\"' + path + '\"';
        element.bgColor = '\"' + bgColor + '\"';
        element.isHidden = isHidden(elem);
        element.color = '\"' + color + '\"';
        element.fontSize = getStyle(elem, 'font-size');
        // element.fontSizeNotComputed = resolveAppliedStyle(elem, 'font-size') || "";
        element.fontWeight = getStyle(elem, 'font-weight');
        element.textAlign = getStyle(elem, 'text-align');
        element.isTextNode = isTextNode(elem);
        elem.focus(); // get the focus
        focus = document.activeElement;
        if (elem === focus) {
            element.isFocusable = true;
            element.outlineColorFocus = '\"' + formatColor(getStyle(elem, 'outline-color', 'focus')) + '\"';
            element.outlineStyleFocus = '\"' + getStyle(elem, 'outline-style', 'focus') + '\"';
            element.outlineWidthFocus = getStyle(elem, 'outline-width', 'focus');
        } else {
            element.isFocusable = false;
        }
        elem.blur(); // release the focus
        // creticla area   not comuted
        element.isForbiddenUnitUsed_NotComputed = isForbiddenUnitUsed2(elem);
        element.isAuthorizedUnitUsed_NotComputed = isNotAuthorizedUnitUsed2(elem);

        result.push(element);

        children = elem.children;
        for (var i = 0; i < children.length; i++) {
            extractInfo(children[i], color, bgColor, result, path, i);
        }
    }
}
;
/*
 * Extraction of not calculculated css property
 *
 */

function  getAllElementsWithForbiddenUnits( ) {
//var e = new Date().getTime();
    var forbiddenUnits = ["pt", "pc", "mm", "cm", "in"];
    var propList = [];
    var elementsWithForbiddenUnits = [];
    try{
        for (var h = 0; h < document.styleSheets.length; h++) {
            for (var i in document.styleSheets[h].cssRules) {

                var keyList = [];
                for (var j in document.styleSheets[h].cssRules[i].style) {
                    if (!isNaN(j))
                        keyList.push(document.styleSheets[h].cssRules[i].style[j]);
                }

                //init propList
                for (var k in keyList) {
                    for (var u in forbiddenUnits) {
                        if(document.styleSheets[h].cssRules[i].style[keyList[k]] !== undefined){
                            var reg = new RegExp('(\\d*.*\\d*\\s*' + forbiddenUnits[u] + ')');

                            if (document.styleSheets[h].cssRules[i].style[keyList[k]].match(reg) !== null) {
                                propList.push(document.styleSheets[h].cssRules[i]);
                                break;
                            }
                        }
                    }
                }
            }
        }
    } catch(e) {
        if(e.name !== "SecurityError") {
            throw e;
        }
    }
//console.log(propList);
    // var f = new Date().getTime();
//console.log("execution : " + (f - e) + "ms")
//var elementsWithForbiddenUnits = [];
    var tmpList = []
    for (var i = 0; i < propList.length; i++) {
        if (document.querySelector(propList[i].selectorText) !== null) {
            tmpList.push(document.querySelector(propList[i].selectorText));
            for (x in tmpList) {
                if (elementsWithForbiddenUnits.indexOf(tmpList[x]) === -1) {
                    elementsWithForbiddenUnits.push(tmpList[x]);
                }

            }
            tmpList = [];
        }
    }

    var reg = [];
    for (var l in forbiddenUnits) {//regex
        reg.push(new RegExp('(\\w\\s*:\\s*\\d*.*\\d*' + forbiddenUnits[l] + ')'));
    }
    var tmpElemInLineStyle = document.querySelectorAll('*[style]'); //find all element with inligne style
    for (var m = 0; m < tmpElemInLineStyle.length; m++) {
        for (var z = 0; z < reg.length; z++) {
            if (tmpElemInLineStyle[m].style.cssText.match(reg[z]) !== null) {
                elementsWithForbiddenUnits.push(tmpElemInLineStyle[m]);
                break;
            }
        }
    }

    return elementsWithForbiddenUnits;
}

//get all not authorized elements == haven't the authorized unit
function  getAllElementsWithoutAuthorizedUnits( ) {

    var hasRelativeUnit = false;
    //var e = new Date().getTime();
    var authorizedUnitsDigit = ["%", "em", "rem", "vw", "vh",
        "pt", "pc", "mm", "cm", "in"]; //not to be compute twice by unrelatives units
    var authorizedUnitsAlpha = [ "vmin" , "vmax" , "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "xsmaller", "larger"];
    var propList = [];
    var elementsWithoutAuthorizedUnits = [];

    try{
        for (var h = 0; h < document.styleSheets.length; h++) {
            for (var i in document.styleSheets[h].cssRules) {

                var keyList = []; //init style
                for (var j in document.styleSheets[h].cssRules[i].style) {
                    if (!isNaN(j))
                        keyList.push(document.styleSheets[h].cssRules[i].style[j]);
                }

                //init propList  - CSS SELECTION
                for (var k in keyList) {
                    hasRelativeUnit = false;
                    if(document.styleSheets[h].cssRules[i].style[keyList[k]] !== undefined){
                        for (var u in authorizedUnitsDigit) {

                            var reg1 = new RegExp('(\\d*.*\\d*\\s*' + authorizedUnitsDigit[u] + ')');

                            if (document.styleSheets[h].cssRules[i].style[keyList[k]].match(reg1) !== null) { // if digit unit
                                hasRelativeUnit = true;
                                break;
                            }
                        }
                        if(!hasRelativeUnit){ // if always no correspondence
                            for (var u in authorizedUnitsAlpha) {

                                var reg2 = new RegExp('('+ authorizedUnitsAlpha[u] + ')');

                                if (document.styleSheets[h].cssRules[i].style[keyList[k]].match(reg2) !== null) { //if alphabetic unit
                                    hasRelativeUnit = true;
                                    break;
                                }
                            }
                        }
                        if(!hasRelativeUnit){ // if no relative unit
                            propList.push(document.styleSheets[h].cssRules[i]);
                        }
                    }
                }
            }
        }
    } catch(e) {
        if(e.name !== "SecurityError") {
            throw e;
        }
    }
    //console.log(propList);
    // var f = new Date().getTime();
    //console.log("execution : " + (f - e) + "ms")
    //var elementsWithForbiddenUnits = [];

    //CSS SELECTION - REMOVE NULL ELEMENTS
    var tmpList = [];
    for (var i = 0; i < propList.length; i++) {
        if (document.querySelector(propList[i].selectorText) !== null) {
            tmpList.push(document.querySelector(propList[i].selectorText));
            for (x in tmpList) {
                if (elementsWithoutAuthorizedUnits.indexOf(tmpList[x]) === -1) {
                    elementsWithoutAuthorizedUnits.push(tmpList[x]);
                }
            }
            tmpList = [];
        }
    }

    try{
        //STYLE IN BODY SELECTION
        var tmpElemInLineStyle = document.querySelectorAll('*[style]'); //find all element with inligne style
        var canCheck = false;
        var str = "";

        for (var m in tmpElemInLineStyle) {
            hasRelativeUnit = false;
            canCheck = false;

            if(tmpElemInLineStyle[m].style !== undefined){

                //Select if the attribute in style can have a relative unit
                str = tmpElemInLineStyle[m].style.cssText.split(':');
                if(str.length > 2) { //if there is more that one attribute in style

                    for(var i = 0; i<str.length; i++){
                        if(i === 0 ){ //to check the first style's attribute
                            for(var j in attrs){ //correspondence with style's attributes which can have a relative unit
                                if(str[0] === attrs[j]){
                                    canCheck = true;
                                    break;
                                }
                            }
                        }else if(!canCheck){ // check the other attributes
                            var str2 = str[i].split('; '); // Form : "20px; width" -> "20px","width"

                            if(str2[1] !== undefined){
                                for(var j in attrs){ //correspondence with style's attributes which can have a relative unit
                                    if(str2[1] === attrs[j]){
                                        canCheck = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }else{ //if there is only one attribute in style
                    for(var j in attrs){
                        if(str[0] === attrs[j]){//correspondence with style's attributes which can have a relative unit
                            canCheck = true;
                            break;
                        }
                    }
                }
                if(canCheck){ //if one of the style's attributes is suspected to have a relative unit

                    for (var u in authorizedUnitsDigit) {
                        var reg1 = new RegExp('(\\w*\\s*:\\s*\\d*.*\\d*' + authorizedUnitsDigit[u] + ')');

                        if(tmpElemInLineStyle[m].style.cssText.match(reg1) !== null){ // if digit unit
                            hasRelativeUnit = true;
                            break;

                        }
                    }
                    if(!hasRelativeUnit){ // if always no correspondence
                        for (var u in authorizedUnitsAlpha) {
                            var reg2 = new RegExp('(\\w\\s*:\\s*' + authorizedUnitsAlpha[u] + ')');

                            if(tmpElemInLineStyle[m].style.cssText.match(reg2) !== null){ //if alphabetic unit
                                hasRelativeUnit = true;
                                break;
                            }
                        }
                    }
                    if(!hasRelativeUnit){ // if no relative unit
                        elementsWithoutAuthorizedUnits.push(tmpElemInLineStyle[m]);
                    }
                }
            }
        }
    } catch(e) {
        if(e.name !== "SecurityError") {
            throw e;
        }
    }

    return elementsWithoutAuthorizedUnits;
}

//console.log(elementsWithForbiddenUnits)
//var f2 = new Date().getTime();
//console.log("execution : "+ (f2-e) +"ms")

/*
 * Get all the elements of the DOM from body,
 * extracts the textual one and store bg-color, font-size, color and font-weight.
 */
var result = [], element, rootElem, htmlElem, htmlBgColor;
rootElem = document.body;
var elementWithForbiddenUnitsRoot = [];
var elementsWithoutAuthorizedUnits = [];
var e = new Date().getTime();
if (rootElem.length !== 0) {
    htmlElem = rootElem.parentNode;
    htmlBgColor = getBackgroundColor(htmlElem);
    if ((htmlBgColor === 'transparent' || htmlBgColor === 'rgba(0; 0; 0; 0)')) {
        htmlBgColor = 'rgb(255; 255; 255)';
    }
    elementWithForbiddenUnitsRoot = getAllElementsWithForbiddenUnits();
    elementsWithoutAuthorizedUnits = getAllElementsWithoutAuthorizedUnits();
    extractInfo(rootElem, getForegroundColor(htmlElem), htmlBgColor, result, "", 0);
}
/*var f = new Date().getTime();
 console.log("Execution : "+ (f-e)  + "ms");
 console.log(result.length);*/
return result;<commit_msg>remove colorextractor<commit_after><|endoftext|>


<commit_before>'use strict';

module.exports = {
  client: {
    lib: {
      css: [
        // bower:css
        'public/lib/bootstrap/dist/css/bootstrap.min.css',
        'public/lib/bootstrap/dist/css/bootstrap-theme.min.css'
        // endbower
      ],
      js: [
        // bower:js
        'public/lib/angular/angular.min.js',
        'public/lib/angular-resource/angular-resource.min.js',
        'public/lib/angular-animate/angular-animate.min.js',
        'public/lib/angular-messages/angular-messages.min.js',
        'public/lib/angular-ui-router/release/angular-ui-router.min.js',
        'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',
        'public/lib/angular-file-upload/dist/angular-file-upload.min.js',
        'public/lib/owasp-password-strength-test/owasp-password-strength-test.js'
        // endbower
      ]
    },
    css: 'public/dist/application.min.css',
    js: 'public/dist/application.min.js'
  }
};
<commit_msg>update prod config<commit_after>'use strict';

module.exports = {
  client: {
    lib: {
      css: [
        // bower:css
        'public/lib/bootstrap/dist/css/bootstrap.min.css',
        'public/lib/bootstrap/dist/css/bootstrap-theme.min.css'
        // endbower
      ],
      js: [
        // bower:js
        'public/lib/angular/angular.js',
        'public/lib/angular-resource/angular-resource.js',
        'public/lib/angular-animate/angular-animate.js',
        'public/lib/angular-messages/angular-messages.js',
        'public/lib/angular-sanitize/angular-sanitize.js',
        'public/lib/angular-ui-router/release/angular-ui-router.js',
        'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
        'public/lib/angular-file-upload/dist/angular-file-upload.js',
        'public/lib/owasp-password-strength-test/owasp-password-strength-test.js',
        'public/lib/tinymce-dist/tinymce.js',
        'public/lib/angular-ui-tinymce/src/tinymce.js'
        // endbower
      ]
    },
    css: [
      'modules/*/client/css/*.css'
    ],
    less: [
      'modules/*/client/less/*.less'
    ],
    sass: [
      'modules/*/client/scss/*.scss'
    ],
    js: [
      'modules/core/client/app/config.js',
      'modules/core/client/app/init.js',
      'modules/*/client/*.js',
      'modules/*/client/**/*.js'
    ],
    img: [
      'modules/**/*/img/**/*.jpg',
      'modules/**/*/img/**/*.png',
      'modules/**/*/img/**/*.gif',
      'modules/**/*/img/**/*.svg'
    ],
    views: ['modules/*/client/views/**/*.html'],
    templates: ['build/templates.js']
  },
  server: {
    gruntConfig: ['gruntfile.js'],
    gulpConfig: ['gulpfile.js'],
    allJS: ['server.js', 'config/**/*.js', 'modules/*/server/**/*.js'],
    models: 'modules/*/server/models/**/*.js',
    routes: ['modules/!(core)/server/routes/**/*.js', 'modules/core/server/routes/**/*.js'],
    sockets: 'modules/*/server/sockets/**/*.js',
    config: ['modules/*/server/config/*.js'],
    policies: 'modules/*/server/policies/*.js',
    views: ['modules/*/server/views/*.html']
  }
};
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage } from 'react-intl';

import A from './A';
import Img from './Img';
import NavBar from './NavBar';
import HeaderLink from './HeaderLink';
import Banner from './banner.png';
import messages from './messages';
import HeaderBar from './HeaderBar';

// import DrawerBar from 'components/DrawerBar';

class Header extends React.Component { // eslint-disable-line react/prefer-stateless-function
  render() {
    return (
      <HeaderBar>

        <NavBar
          style={{
            marginTop: '15px',
          }}
        >
          <HeaderLink to="/energies">
            <FormattedMessage {...messages.energies} />
          </HeaderLink>
          <HeaderLink to="/activityMaps">
            <FormattedMessage {...messages.activityMaps} />
          </HeaderLink>
          <HeaderLink to="/scalingRelations">
            <FormattedMessage {...messages.scalingRelations} />
          </HeaderLink>
          <HeaderLink to="/pourbaixDiagrams">
            <FormattedMessage {...messages.pourbaixDiagrams} />
          </HeaderLink>
          <HeaderLink to="/generalSearch">
            <FormattedMessage {...messages.generalSearch} />
          </HeaderLink>
          <HeaderLink to="/publications">
            <FormattedMessage {...messages.publications} />
          </HeaderLink>
          <HeaderLink to="/yourNextApp">
            <FormattedMessage {...messages.yourNextApp} />
          </HeaderLink>
        </NavBar>
      </HeaderBar>
    );
  }
}

export default Header;
<commit_msg>Take out link for scaling relation app for now<commit_after>import React from 'react';
import { FormattedMessage } from 'react-intl';

import A from './A';
import Img from './Img';
import NavBar from './NavBar';
import HeaderLink from './HeaderLink';
import Banner from './banner.png';
import messages from './messages';
import HeaderBar from './HeaderBar';

// import DrawerBar from 'components/DrawerBar';

class Header extends React.Component { // eslint-disable-line react/prefer-stateless-function
  render() {
    return (
      <HeaderBar>

        <NavBar
          style={{
            marginTop: '15px',
          }}
        >
          <HeaderLink to="/energies">
            <FormattedMessage {...messages.energies} />
          </HeaderLink>
          <HeaderLink to="/activityMaps">
            <FormattedMessage {...messages.activityMaps} />
          </HeaderLink>
          {/*
          <HeaderLink to="/scalingRelations">
            <FormattedMessage {...messages.scalingRelations} />
          </HeaderLink>
          */}
          <HeaderLink to="/pourbaixDiagrams">
            <FormattedMessage {...messages.pourbaixDiagrams} />
          </HeaderLink>
          <HeaderLink to="/generalSearch">
            <FormattedMessage {...messages.generalSearch} />
          </HeaderLink>
          <HeaderLink to="/publications">
            <FormattedMessage {...messages.publications} />
          </HeaderLink>
          <HeaderLink to="/yourNextApp">
            <FormattedMessage {...messages.yourNextApp} />
          </HeaderLink>
        </NavBar>
      </HeaderBar>
    );
  }
}

export default Header;
<|endoftext|>


<commit_before>// Paths
export const HOME_PATHNAME = 'home';
export const OWNERS_PATHNAME = 'owners';
export const EQUIPMENT_PATHNAME = 'equipment';
export const PROJECTS_PATHNAME = 'projects';
export const CONTACTS_PATHNAME = 'contacts';
export const RENTAL_REQUESTS_PATHNAME = 'rental-requests';
export const RENTAL_AGREEMENTS_PATHNAME = 'rental-agreements';
export const OVERTIME_RATES_PATHNAME = 'overtime-rates';
export const USERS_PATHNAME = 'users';
export const ROLES_PATHNAME = 'roles';
export const ROLLOVER_PATHNAME = 'roll-over';
export const DISTRICT_ADMIN_PATHNAME = 'district-admin';
export const TIME_ENTRY_PATHNAME = 'time-entry';
export const VERSION_PATHNAME = 'version';
export const BUSINESS_PORTAL_PATHNAME = '/business';
export const BUSINESS_DETAILS_PATHNAME = '/business/details';

// Permissions
export const PERMISSION_LOGIN = 'Login';
export const PERMISSION_BUSINESS_LOGIN = 'BusinessLogin';
export const PERMISSION_ADMIN = 'Admin';
export const PERMISSION_USER_MANAGEMENT = 'UserManagement';
export const PERMISSION_ROLES_AND_PERMISSIONS = 'RolesAndPermissions';
export const PERMISSION_IMPORT_DATA = 'ImportData';
export const PERMISSION_CODE_TABLE_MANAGEMENT = 'CodeTableManagement';
export const PERMISSION_DISTRICT_CODE_TABLE_MANAGEMENT = 'DistrictCodeTableManagement';
export const PERMISSION_DISTRICT_ROLLOVER = 'DistrictRollover';
export const PERMISSION_VERSION = 'Version';

// Roles
export const ADMINISTRATOR_ROLE = 'Administrator';

// Equipments
export const EQUIPMENT_DAYS_SINCE_VERIFIED_WARNING = 270;
export const EQUIPMENT_DAYS_SINCE_VERIFIED_CRITICAL = 365;

export const EQUIPMENT_STATUS_CODE_PENDING = 'Unapproved';
export const EQUIPMENT_STATUS_CODE_APPROVED = 'Approved';
export const EQUIPMENT_STATUS_CODE_ARCHIVED = 'Archived';

// Owners
export const OWNER_STATUS_CODE_PENDING = 'Unapproved';
export const OWNER_STATUS_CODE_APPROVED = 'Approved';
export const OWNER_STATUS_CODE_ARCHIVED = 'Archived';

// Projects
export const PROJECT_STATUS_CODE_ACTIVE = 'Active';
export const PROJECT_STATUS_CODE_COMPLETED = 'Completed';

// Rental Requests
export const RENTAL_REQUEST_STATUS_CODE_IN_PROGRESS = 'In Progress';
export const RENTAL_REQUEST_STATUS_CODE_COMPLETED = 'Complete';
export const RENTAL_REQUEST_STATUS_CODE_CANCELLED = 'Cancelled';

// Rental Agreements
export const RENTAL_AGREEMENT_STATUS_CODE_ACTIVE = 'Active';
export const RENTAL_AGREEMENT_STATUS_CODE_COMPLETED = 'Completed';
export const RENTAL_RATE_PERIOD_HOURLY = 'Hr';
export const RENTAL_RATE_PERIOD_DAILY = 'Daily';
export const RENTAL_RATE_PERIOD_WEEKLY = 'Weekly';
export const RENTAL_RATE_PERIOD_MONTHLY = 'Monthly';
export const RENTAL_RATE_PERIOD_NEGOTIATED = 'Negotiated';
export const MAX_UNASSOCIATED_RENTAL_AGREEMENTS = 3;

// Users
export const USER_STATUS_ACTIVE = 'Active';
export const USER_STATUS_ARCHIVED = 'Archived';

// Date Formats
export const DATE_FULL_MONTH_DAY_YEAR = 'MMMM D, YYYY';
export const DATE_SHORT_MONTH_DAY_YEAR = 'MMM D, YYYY';
export const DATE_YEAR_SHORT_MONTH_DAY = 'YYYY-MMM-DD';
export const DATE_ISO_8601 = 'YYYY-MM-DD';

export const DATE_ZULU = 'YYYY-MM-DDThh:mm:ss[Z]';

export const DATE_TIME_ISO_8601 = 'YYYY-MM-DDTHH:mm:ss';
export const DATE_TIME_READABLE = 'MMMM D, YYYY [at] h:mm:ss A';
export const DATE_TIME_LOG = 'YYYY/MM/DD HH:mm:ss';
export const DATE_TIME_FILENAME = 'YYYY-MM-DD-HHmmss';

// RegEx
export const EMAIL_REGEX = /\S+@\S+\.\S+/;
export const NANP_REGEX = /^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/;
export const MONEY_REGEX = /^\d+(\.\d\d?)?$/;

// Conditions
export const NON_STANDARD_CONDITION = 'Other';

// ResponseTypes

export const RESPONSE_TYPE_BLOB = 'blob';

// Cloning
export const BY_PROJECT = 'By Project';
export const BY_EQUIPMENT = 'By Equipment';

export var headerHeight = 0;
export function setHeaderHeight(num) {
  headerHeight = num;
}

// Session
export const SESSION_TIMEOUT = 7200000; // 120 minutes
export const SESSION_KEEP_ALIVE_INTERVAL = 600000; // 10 minutes

// Max Field Lengths
export const MAX_LENGTH_CGL_COMPANY_NAME = 150;
export const MAX_LENGTH_NOTE_TEXT = 2048;
export const MAX_LENGTH_PHONE_NUMBER = 20;
export const MAX_LENGTH_RENTAL_AGREEMENT_NOTE = 150;

// Max File Sizes
export const MAX_ATTACHMENT_FILE_SIZE = 5242880; // 5 MB
export const MAX_ATTACHMENT_FILE_SIZE_READABLE = '5 MB';
<commit_msg>HETS-1007 fix reference to old administrator role name<commit_after>// Paths
export const HOME_PATHNAME = 'home';
export const OWNERS_PATHNAME = 'owners';
export const EQUIPMENT_PATHNAME = 'equipment';
export const PROJECTS_PATHNAME = 'projects';
export const CONTACTS_PATHNAME = 'contacts';
export const RENTAL_REQUESTS_PATHNAME = 'rental-requests';
export const RENTAL_AGREEMENTS_PATHNAME = 'rental-agreements';
export const OVERTIME_RATES_PATHNAME = 'overtime-rates';
export const USERS_PATHNAME = 'users';
export const ROLES_PATHNAME = 'roles';
export const ROLLOVER_PATHNAME = 'roll-over';
export const DISTRICT_ADMIN_PATHNAME = 'district-admin';
export const TIME_ENTRY_PATHNAME = 'time-entry';
export const VERSION_PATHNAME = 'version';
export const BUSINESS_PORTAL_PATHNAME = '/business';
export const BUSINESS_DETAILS_PATHNAME = '/business/details';

// Permissions
export const PERMISSION_LOGIN = 'Login';
export const PERMISSION_BUSINESS_LOGIN = 'BusinessLogin';
export const PERMISSION_ADMIN = 'Admin';
export const PERMISSION_USER_MANAGEMENT = 'UserManagement';
export const PERMISSION_ROLES_AND_PERMISSIONS = 'RolesAndPermissions';
export const PERMISSION_IMPORT_DATA = 'ImportData';
export const PERMISSION_CODE_TABLE_MANAGEMENT = 'CodeTableManagement';
export const PERMISSION_DISTRICT_CODE_TABLE_MANAGEMENT = 'DistrictCodeTableManagement';
export const PERMISSION_DISTRICT_ROLLOVER = 'DistrictRollover';
export const PERMISSION_VERSION = 'Version';

// Roles
export const ADMINISTRATOR_ROLE = '4-HETS System Administrator';

// Equipments
export const EQUIPMENT_DAYS_SINCE_VERIFIED_WARNING = 270;
export const EQUIPMENT_DAYS_SINCE_VERIFIED_CRITICAL = 365;

export const EQUIPMENT_STATUS_CODE_PENDING = 'Unapproved';
export const EQUIPMENT_STATUS_CODE_APPROVED = 'Approved';
export const EQUIPMENT_STATUS_CODE_ARCHIVED = 'Archived';

// Owners
export const OWNER_STATUS_CODE_PENDING = 'Unapproved';
export const OWNER_STATUS_CODE_APPROVED = 'Approved';
export const OWNER_STATUS_CODE_ARCHIVED = 'Archived';

// Projects
export const PROJECT_STATUS_CODE_ACTIVE = 'Active';
export const PROJECT_STATUS_CODE_COMPLETED = 'Completed';

// Rental Requests
export const RENTAL_REQUEST_STATUS_CODE_IN_PROGRESS = 'In Progress';
export const RENTAL_REQUEST_STATUS_CODE_COMPLETED = 'Complete';
export const RENTAL_REQUEST_STATUS_CODE_CANCELLED = 'Cancelled';

// Rental Agreements
export const RENTAL_AGREEMENT_STATUS_CODE_ACTIVE = 'Active';
export const RENTAL_AGREEMENT_STATUS_CODE_COMPLETED = 'Completed';
export const RENTAL_RATE_PERIOD_HOURLY = 'Hr';
export const RENTAL_RATE_PERIOD_DAILY = 'Daily';
export const RENTAL_RATE_PERIOD_WEEKLY = 'Weekly';
export const RENTAL_RATE_PERIOD_MONTHLY = 'Monthly';
export const RENTAL_RATE_PERIOD_NEGOTIATED = 'Negotiated';
export const MAX_UNASSOCIATED_RENTAL_AGREEMENTS = 3;

// Users
export const USER_STATUS_ACTIVE = 'Active';
export const USER_STATUS_ARCHIVED = 'Archived';

// Date Formats
export const DATE_FULL_MONTH_DAY_YEAR = 'MMMM D, YYYY';
export const DATE_SHORT_MONTH_DAY_YEAR = 'MMM D, YYYY';
export const DATE_YEAR_SHORT_MONTH_DAY = 'YYYY-MMM-DD';
export const DATE_ISO_8601 = 'YYYY-MM-DD';

export const DATE_ZULU = 'YYYY-MM-DDThh:mm:ss[Z]';

export const DATE_TIME_ISO_8601 = 'YYYY-MM-DDTHH:mm:ss';
export const DATE_TIME_READABLE = 'MMMM D, YYYY [at] h:mm:ss A';
export const DATE_TIME_LOG = 'YYYY/MM/DD HH:mm:ss';
export const DATE_TIME_FILENAME = 'YYYY-MM-DD-HHmmss';

// RegEx
export const EMAIL_REGEX = /\S+@\S+\.\S+/;
export const NANP_REGEX = /^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/;
export const MONEY_REGEX = /^\d+(\.\d\d?)?$/;

// Conditions
export const NON_STANDARD_CONDITION = 'Other';

// ResponseTypes

export const RESPONSE_TYPE_BLOB = 'blob';

// Cloning
export const BY_PROJECT = 'By Project';
export const BY_EQUIPMENT = 'By Equipment';

export var headerHeight = 0;
export function setHeaderHeight(num) {
  headerHeight = num;
}

// Session
export const SESSION_TIMEOUT = 7200000; // 120 minutes
export const SESSION_KEEP_ALIVE_INTERVAL = 600000; // 10 minutes

// Max Field Lengths
export const MAX_LENGTH_CGL_COMPANY_NAME = 150;
export const MAX_LENGTH_NOTE_TEXT = 2048;
export const MAX_LENGTH_PHONE_NUMBER = 20;
export const MAX_LENGTH_RENTAL_AGREEMENT_NOTE = 150;

// Max File Sizes
export const MAX_ATTACHMENT_FILE_SIZE = 5242880; // 5 MB
export const MAX_ATTACHMENT_FILE_SIZE_READABLE = '5 MB';
<|endoftext|>


<commit_before>/* eslint-disable */
define(
  [
    'Class',
    'uri',
    'bluebird',
    'helpers/geojsonUtilsHelper',
    'map/services/GeostoreService',
    'map/services/DataService'
  ],
  function(
    Class,
    UriTemplate,
    Promise,
    geojsonUtilsHelper,
    GeostoreService,
    ds
  ) {
    'use strict';

    var GET_REQUEST_ID = 'AnalysisService:get';

    var APIURL = 'https://production-api.globalforestwatch.org';
    var APIURLV2 = 'https://production-api.globalforestwatch.org' + '/v3';

    var APIURLS = {
      draw: '/{dataset}{?geostore,period,thresh,gladConfirmOnly}',
      country:
        '/{dataset}/admin{/country}{/region}{/subRegion}{?period,thresh,gladConfirmOnly}',
      wdpaid: '/{dataset}/wdpa{/wdpaid}{?period,thresh,gladConfirmOnly}',
      use: '/{dataset}/use{/use}{/useid}{?period,thresh,gladConfirmOnly}',
      'use-geostore': '/{dataset}{?geostore,period,thresh,gladConfirmOnly}'
    };

    var AnalysisService = Class.extend({
      get: function(status) {
        if (
          (status.baselayers.indexOf('umd_as_it_happens') > -1 ||
            status.baselayers.indexOf('places_to_watch') > -1 ||
            status.baselayers.indexOf('terrailoss') > -1 ||
            status.baselayers.indexOf('forma_month_3') > -1 ||
            status.baselayers.indexOf('forma_activity') > -1 ||
            status.baselayers.indexOf('imazon') > -1 ||
            status.baselayers.indexOf('guyra') > -1 ||
            status.baselayers.indexOf('viirs_fires_alerts') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_per') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_cog') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_idn') > -1 ||
            status.baselayers.indexOf('prodes') > -1) &&
          status.type === 'draw'
        ) {
          var umdUrl = UriTemplate(APIURLS['draw']).fillFromObject({
            dataset: 'umd-loss-gain',
            geostore: status.geostore,
            period: status.period,
            thresh: status.threshold,
            gladConfirmOnly: status.gladConfirmOnly
          });
          return fetch(APIURL + umdUrl)
            .then(function(response) {
              return response.json();
            })
            .then(
              function(umdResponse) {
                return new Promise(
                  function(resolve, reject) {
                    this.analysis = this.buildAnalysisFromStatus(status);
                    this.apiHost = this.getApiHost(this.analysis.dataset);
                    this.getUrl().then(
                      function(data) {
                        ds.define(GET_REQUEST_ID, {
                          cache: false,
                          url: this.apiHost + data.url,
                          type: 'GET',
                          dataType: 'json',
                          decoder: function(data, status, xhr, success, error) {
                            if (status === 'success') {
                              success(data, xhr);
                            } else if (
                              status === 'fail' ||
                              status === 'error'
                            ) {
                              error(xhr.responseText);
                            } else if (status !== 'abort') {
                              error(xhr.responseText);
                            }
                          }
                        });
                        console.log('UMD', umdResponse);
                        console.log('RESPONSE', response);

                        var requestConfig = {
                          resourceId: GET_REQUEST_ID,
                          success: function(response, status) {
                            var data = {};
                            if (this.analysis.type === 'country') {
                              data = {
                                data: {
                                  attributes: {
                                    areaHa:
                                      response.data.attributes.totals.areaHa,
                                    gain: response.data.attributes.totals.gain,
                                    loss: response.data.attributes.totals.loss,
                                    alerts:
                                      response.data.attributes.totals
                                        .gladAlerts,
                                    treeExtent:
                                      response.data.attributes.totals
                                        .extent2000,
                                    treeExtent2010:
                                      response.data.attributes.totals.extent2010
                                  }
                                }
                              };
                            } else {
                              data = {
                                data: {
                                  attributes: {
                                    areaHa: response.data.attributes.areaHa,
                                    gain: response.data.attributes.gain,
                                    loss: response.data.attributes.value,
                                    alerts:
                                      response.data.attributes.alertCounts,
                                    treeExtent:
                                      response.data.attributes.treeExtent,
                                    treeExtent2010:
                                      response.data.attributes.treeExtent2010,
                                    downloadUrls:
                                      response.data.attributes.downloadUrls
                                  }
                                }
                              };
                            }
                            resolve(data, status);
                          }.bind(this),
                          error: function(errors) {
                            reject(errors);
                          }.bind(this)
                        };

                        this.abortRequest();
                        this.currentRequest = ds.request(requestConfig);
                      }.bind(this)
                    );
                  }.bind(this)
                );
              }.bind(this)
            );
        } else {
          return new Promise(
            function(resolve, reject) {
              this.analysis = this.buildAnalysisFromStatus(status);
              this.apiHost = this.getApiHost(this.analysis.dataset);
              this.getUrl().then(
                function(data) {
                  ds.define(GET_REQUEST_ID, {
                    cache: false,
                    url: this.apiHost + data.url,
                    type: 'GET',
                    dataType: 'json',
                    decoder: function(data, status, xhr, success, error) {
                      if (status === 'success') {
                        success(data, xhr);
                      } else if (status === 'fail' || status === 'error') {
                        error(xhr.responseText);
                      } else if (status !== 'abort') {
                        error(xhr.responseText);
                      }
                    }
                  });

                  var requestConfig = {
                    resourceId: GET_REQUEST_ID,
                    success: function(response, status) {
                      var data = {};
                      if (this.analysis.type === 'country') {
                        data = {
                          data: {
                            attributes: {
                              areaHa: response.data.attributes.totals.areaHa,
                              gain: response.data.attributes.totals.gain,
                              loss: response.data.attributes.totals.loss,
                              alerts:
                                response.data.attributes.totals.gladAlerts,
                              treeExtent:
                                response.data.attributes.totals.extent2000,
                              treeExtent2010:
                                response.data.attributes.totals.extent2010
                            }
                          }
                        };
                      } else {
                        data = {
                          data: {
                            attributes: {
                              areaHa: response.data.attributes.areaHa,
                              gain: response.data.attributes.gain,
                              loss: response.data.attributes.loss,
                              alerts: response.data.attributes.value,
                              treeExtent: response.data.attributes.treeExtent,
                              treeExtent2010:
                                response.data.attributes.treeExtent2010,
                              downloadUrls:
                                response.data.attributes.downloadUrls
                            }
                          }
                        };
                      }
                      resolve(data, status);
                    }.bind(this),
                    error: function(errors) {
                      reject(errors);
                    }.bind(this)
                  };

                  this.abortRequest();
                  this.currentRequest = ds.request(requestConfig);
                }.bind(this)
              );
            }.bind(this)
          );
        }
      },

      getUrl: function() {
        return new Promise(
          function(resolve, reject) {
            resolve({
              url: UriTemplate(APIURLS[this.analysis.type]).fillFromObject(
                Object.assign({}, this.analysis, {
                  dataset:
                    this.analysis.type === 'country'
                      ? 'umd-loss-gain'
                      : this.analysis.dataset
                })
              )
            });
          }.bind(this)
        );
      },

      buildAnalysisFromStatus: function(status) {
        // To allow layerOptions
        // I really think that this should be an object instead of an array, or an array of objects
        var layerOptions = {};
        _.each(status.layerOptions, function(val) {
          layerOptions[val] = true;
        });

        // TEMP
        var period = status.begin + ',' + status.end;
        if (status.dataset === 'umd-loss-gain') {
          period =
            status.begin +
            ',' +
            moment
              .utc(status.end)
              .subtract(1, 'days')
              .format('YYYY-MM-DD');
        }

        return _.extend(
          {},
          status,
          layerOptions,
          {
            country: status.iso.country,
            region: status.iso.region,
            subRegion: status.iso.subRegion,
            thresh: status.threshold,
            period: period,

            // If a userGeostore exists we need to set geostore and type manually
            geostore: status.useGeostore ? status.useGeostore : status.geostore,
            type: status.useGeostore ? 'use-geostore' : status.type
          },
          layerOptions
        );
      },

      getApiHost: function(dataset) {
        return this.analysis.type === 'country' ? APIURLV2 : APIURL;
      },

      /**
       * Abort the current request if it exists.
       */
      abortRequest: function() {
        if (this.currentRequest) {
          this.currentRequest.abort();
          this.currentRequest = null;
        }
      }
    });

    return new AnalysisService();
  }
);
<commit_msg>Tidying<commit_after>/* eslint-disable */
define(
  [
    'Class',
    'uri',
    'bluebird',
    'helpers/geojsonUtilsHelper',
    'map/services/GeostoreService',
    'map/services/DataService'
  ],
  function(
    Class,
    UriTemplate,
    Promise,
    geojsonUtilsHelper,
    GeostoreService,
    ds
  ) {
    'use strict';

    var GET_REQUEST_ID = 'AnalysisService:get';

    var APIURL = 'https://production-api.globalforestwatch.org';
    var APIURLV2 = 'https://production-api.globalforestwatch.org' + '/v3';

    var APIURLS = {
      draw: '/{dataset}{?geostore,period,thresh,gladConfirmOnly}',
      country:
        '/{dataset}/admin{/country}{/region}{/subRegion}{?period,thresh,gladConfirmOnly}',
      wdpaid: '/{dataset}/wdpa{/wdpaid}{?period,thresh,gladConfirmOnly}',
      use: '/{dataset}/use{/use}{/useid}{?period,thresh,gladConfirmOnly}',
      'use-geostore': '/{dataset}{?geostore,period,thresh,gladConfirmOnly}'
    };

    var AnalysisService = Class.extend({
      get: function(status) {
        if (
          (status.baselayers.indexOf('umd_as_it_happens') > -1 ||
            status.baselayers.indexOf('places_to_watch') > -1 ||
            status.baselayers.indexOf('terrailoss') > -1 ||
            status.baselayers.indexOf('forma_month_3') > -1 ||
            status.baselayers.indexOf('forma_activity') > -1 ||
            status.baselayers.indexOf('imazon') > -1 ||
            status.baselayers.indexOf('guyra') > -1 ||
            status.baselayers.indexOf('viirs_fires_alerts') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_per') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_cog') > -1 ||
            status.baselayers.indexOf('umd_as_it_happens_idn') > -1 ||
            status.baselayers.indexOf('prodes') > -1) &&
          status.type === 'draw'
        ) {
          var umdUrl = UriTemplate(APIURLS['draw']).fillFromObject({
            dataset: 'umd-loss-gain',
            geostore: status.geostore,
            period: status.period,
            thresh: status.threshold,
            gladConfirmOnly: status.gladConfirmOnly
          });
          return fetch(APIURL + umdUrl)
            .then(function(response) {
              return response.json();
            })
            .then(
              function(umdResponse) {
                return new Promise(
                  function(resolve, reject) {
                    this.analysis = this.buildAnalysisFromStatus(status);
                    this.apiHost = this.getApiHost(this.analysis.dataset);
                    this.getUrl().then(
                      function(data) {
                        ds.define(GET_REQUEST_ID, {
                          cache: false,
                          url: this.apiHost + data.url,
                          type: 'GET',
                          dataType: 'json',
                          decoder: function(data, status, xhr, success, error) {
                            if (status === 'success') {
                              success(data, xhr);
                            } else if (
                              status === 'fail' ||
                              status === 'error'
                            ) {
                              error(xhr.responseText);
                            } else if (status !== 'abort') {
                              error(xhr.responseText);
                            }
                          }
                        });

                        var requestConfig = {
                          resourceId: GET_REQUEST_ID,
                          success: function(response, status) {
                            var data = {};
                            if (this.analysis.type === 'country') {
                              data = {
                                data: {
                                  attributes: {
                                    areaHa:
                                      response.data.attributes.totals.areaHa,
                                    gain: response.data.attributes.totals.gain,
                                    loss: response.data.attributes.totals.loss,
                                    alerts:
                                      response.data.attributes.totals
                                        .gladAlerts,
                                    treeExtent:
                                      response.data.attributes.totals
                                        .extent2000,
                                    treeExtent2010:
                                      response.data.attributes.totals.extent2010
                                  }
                                }
                              };
                            } else {
                              data = {
                                data: {
                                  attributes: {
                                    areaHa: response.data.attributes.areaHa,
                                    gain: response.data.attributes.gain,
                                    loss: response.data.attributes.value,
                                    alerts:
                                      response.data.attributes.alertCounts,
                                    treeExtent:
                                      response.data.attributes.treeExtent,
                                    treeExtent2010:
                                      response.data.attributes.treeExtent2010,
                                    downloadUrls:
                                      response.data.attributes.downloadUrls
                                  }
                                }
                              };
                            }
                            resolve(data, status);
                          }.bind(this),
                          error: function(errors) {
                            reject(errors);
                          }.bind(this)
                        };

                        this.abortRequest();
                        this.currentRequest = ds.request(requestConfig);
                      }.bind(this)
                    );
                  }.bind(this)
                );
              }.bind(this)
            );
        } else {
          return new Promise(
            function(resolve, reject) {
              this.analysis = this.buildAnalysisFromStatus(status);
              this.apiHost = this.getApiHost(this.analysis.dataset);
              this.getUrl().then(
                function(data) {
                  ds.define(GET_REQUEST_ID, {
                    cache: false,
                    url: this.apiHost + data.url,
                    type: 'GET',
                    dataType: 'json',
                    decoder: function(data, status, xhr, success, error) {
                      if (status === 'success') {
                        success(data, xhr);
                      } else if (status === 'fail' || status === 'error') {
                        error(xhr.responseText);
                      } else if (status !== 'abort') {
                        error(xhr.responseText);
                      }
                    }
                  });

                  var requestConfig = {
                    resourceId: GET_REQUEST_ID,
                    success: function(response, status) {
                      var data = {};
                      if (this.analysis.type === 'country') {
                        data = {
                          data: {
                            attributes: {
                              areaHa: response.data.attributes.totals.areaHa,
                              gain: response.data.attributes.totals.gain,
                              loss: response.data.attributes.totals.loss,
                              alerts:
                                response.data.attributes.totals.gladAlerts,
                              treeExtent:
                                response.data.attributes.totals.extent2000,
                              treeExtent2010:
                                response.data.attributes.totals.extent2010
                            }
                          }
                        };
                      } else {
                        data = {
                          data: {
                            attributes: {
                              areaHa: response.data.attributes.areaHa,
                              gain: response.data.attributes.gain,
                              loss: response.data.attributes.loss,
                              alerts: response.data.attributes.value,
                              treeExtent: response.data.attributes.treeExtent,
                              treeExtent2010:
                                response.data.attributes.treeExtent2010,
                              downloadUrls:
                                response.data.attributes.downloadUrls
                            }
                          }
                        };
                      }
                      resolve(data, status);
                    }.bind(this),
                    error: function(errors) {
                      reject(errors);
                    }.bind(this)
                  };

                  this.abortRequest();
                  this.currentRequest = ds.request(requestConfig);
                }.bind(this)
              );
            }.bind(this)
          );
        }
      },

      getUrl: function() {
        return new Promise(
          function(resolve, reject) {
            resolve({
              url: UriTemplate(APIURLS[this.analysis.type]).fillFromObject(
                Object.assign({}, this.analysis, {
                  dataset:
                    this.analysis.type === 'country'
                      ? 'umd-loss-gain'
                      : this.analysis.dataset
                })
              )
            });
          }.bind(this)
        );
      },

      buildAnalysisFromStatus: function(status) {
        // To allow layerOptions
        // I really think that this should be an object instead of an array, or an array of objects
        var layerOptions = {};
        _.each(status.layerOptions, function(val) {
          layerOptions[val] = true;
        });

        // TEMP
        var period = status.begin + ',' + status.end;
        if (status.dataset === 'umd-loss-gain') {
          period =
            status.begin +
            ',' +
            moment
              .utc(status.end)
              .subtract(1, 'days')
              .format('YYYY-MM-DD');
        }

        return _.extend(
          {},
          status,
          layerOptions,
          {
            country: status.iso.country,
            region: status.iso.region,
            subRegion: status.iso.subRegion,
            thresh: status.threshold,
            period: period,

            // If a userGeostore exists we need to set geostore and type manually
            geostore: status.useGeostore ? status.useGeostore : status.geostore,
            type: status.useGeostore ? 'use-geostore' : status.type
          },
          layerOptions
        );
      },

      getApiHost: function(dataset) {
        return this.analysis.type === 'country' ? APIURLV2 : APIURL;
      },

      /**
       * Abort the current request if it exists.
       */
      abortRequest: function() {
        if (this.currentRequest) {
          this.currentRequest.abort();
          this.currentRequest = null;
        }
      }
    });

    return new AnalysisService();
  }
);
<|endoftext|>


<commit_before>// Copyright 2013-2018, University of Colorado Boulder

/**
 * Convenience type for creating a group of CheckBoxes with vertical orientation.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Checkbox = require( 'SUN/Checkbox' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var HStrut = require( 'SCENERY/nodes/HStrut' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  /**
   * @param {Object[]} items - Each item describes a checkbox, and is an object with these properties:
   *    node: Node, // label for the button
   *    property: Property.<boolean>, // Property associated with the button
   *    indent: number, // how much to indent each check box from the left edge
   *    [tandemName: Tandem] // optional tandem for PhET-iO
   * @param {Object} [options]
   * @constructor
   */
  function VerticalCheckboxGroup( items, options ) {

    options = _.extend( {

      // {Object|null} options passed to constructor of the Checkbox
      checkboxOptions: null,

      // dilation of pointer areas, y dimension is computed
      touchAreaXDilation: 5,
      mouseAreaXDilation: 5,

      //TODO #344 this is the total of left and right margins, replace with xMargin?
      padding: 8,

      // supertype options
      spacing: 10, // vertical spacing
      align: 'left',
      tandem: Tandem.optional
    }, options );

    // Verify that the client hasn't set options that we will be overwriting.
    assert && assert( !options.children, 'VerticalCheckboxGroup sets children' );

    //TODO #344 there's a bug here, indent for each item is not considered
    // Determine the max item width
    var maxWidth = 0;
    for ( var i = 0; i < items.length; i++ ) {
      maxWidth = Math.max( maxWidth, items[ i ].node.width );
    }

    //TODO #344 this for loop looks very much like VerticalAquaRadioButtonGroup, something to factor out?
    // Create a checkbox for each item
    options.children = [];
    for ( i = 0; i < items.length; i++ ) {

      var item = items[ i ];
      var indent = item.indent || 0;

      // Content for the checkbox. Add an invisible strut, so that buttons have uniform width.
      var content = new Node( {
        children: [ new HStrut( maxWidth + options.padding - indent ), item.node ]
      } );

      var checkbox = new Checkbox( content, item.property, _.extend( {}, options.checkboxOptions, {
        tandem: item.tandem || Tandem.optional
      } ) );

      // set pointer areas, y dimensions are computed
      var yDilation = options.spacing / 2;
      checkbox.mouseArea = checkbox.localBounds.dilatedXY( options.mouseAreaXDilation, yDilation );
      checkbox.touchArea = checkbox.localBounds.dilatedXY( options.touchAreaXDilation, yDilation );

      //TODO #344 indent feature is missing from VerticalAquaRadioButtonGroup, should it be added?
      //TODO #344 I can think of other ways to indent that don't involve 2 additional nodes
      if ( item.indent ) {

        // indent the checkbox from the left edge using a strut
        options.children.push( new HBox( {
          children: [ new HStrut( item.indent ), checkbox ]
        } ) );
      }
      else {
        options.children.push( checkbox );
      }
    }

    VBox.call( this, options );
  }

  sun.register( 'VerticalCheckboxGroup', VerticalCheckboxGroup );

  return inherit( VBox, VerticalCheckboxGroup );
} );<commit_msg>remove TODO<commit_after>// Copyright 2013-2018, University of Colorado Boulder

/**
 * Convenience type for creating a group of CheckBoxes with vertical orientation.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Checkbox = require( 'SUN/Checkbox' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var HStrut = require( 'SCENERY/nodes/HStrut' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  /**
   * @param {Object[]} items - Each item describes a checkbox, and is an object with these properties:
   *    node: Node, // label for the button
   *    property: Property.<boolean>, // Property associated with the button
   *    indent: number, // how much to indent each check box from the left edge
   *    [tandemName: Tandem] // optional tandem for PhET-iO
   * @param {Object} [options]
   * @constructor
   */
  function VerticalCheckboxGroup( items, options ) {

    options = _.extend( {

      // {Object|null} options passed to constructor of the Checkbox
      checkboxOptions: null,

      // dilation of pointer areas, y dimension is computed
      touchAreaXDilation: 5,
      mouseAreaXDilation: 5,

      //TODO #344 this is the total of left and right margins, replace with xMargin?
      padding: 8,

      // supertype options
      spacing: 10, // vertical spacing
      align: 'left',
      tandem: Tandem.optional
    }, options );

    // Verify that the client hasn't set options that we will be overwriting.
    assert && assert( !options.children, 'VerticalCheckboxGroup sets children' );

    //TODO #344 there's a bug here, indent for each item is not considered
    // Determine the max item width
    var maxWidth = 0;
    for ( var i = 0; i < items.length; i++ ) {
      maxWidth = Math.max( maxWidth, items[ i ].node.width );
    }

    // Create a checkbox for each item
    options.children = [];
    for ( i = 0; i < items.length; i++ ) {

      var item = items[ i ];
      var indent = item.indent || 0;

      // Content for the checkbox. Add an invisible strut, so that buttons have uniform width.
      var content = new Node( {
        children: [ new HStrut( maxWidth + options.padding - indent ), item.node ]
      } );

      var checkbox = new Checkbox( content, item.property, _.extend( {}, options.checkboxOptions, {
        tandem: item.tandem || Tandem.optional
      } ) );

      // set pointer areas, y dimensions are computed
      var yDilation = options.spacing / 2;
      checkbox.mouseArea = checkbox.localBounds.dilatedXY( options.mouseAreaXDilation, yDilation );
      checkbox.touchArea = checkbox.localBounds.dilatedXY( options.touchAreaXDilation, yDilation );

      //TODO #344 indent feature is missing from VerticalAquaRadioButtonGroup, should it be added?
      //TODO #344 I can think of other ways to indent that don't involve 2 additional nodes
      if ( item.indent ) {

        // indent the checkbox from the left edge using a strut
        options.children.push( new HBox( {
          children: [ new HStrut( item.indent ), checkbox ]
        } ) );
      }
      else {
        options.children.push( checkbox );
      }
    }

    VBox.call( this, options );
  }

  sun.register( 'VerticalCheckboxGroup', VerticalCheckboxGroup );

  return inherit( VBox, VerticalCheckboxGroup );
} );<|endoftext|>