<commit_before>import React from 'react';
import Radium from 'radium';
import { withRouter } from 'react-router';
import Sticky from 'react-stickydiv';

import { Toolbar, ToolbarGroup, ToolbarTitle, IconButton } from 'material-ui';

const InnerToolbar = Radium(React.createClass({
  displayName: 'InnerToolbar',

  propTypes: {
    children: React.PropTypes.node
  },

  getDefaultProps() {
    return {
      backButton: false,
      backButtonTooltipPosition: 'bottom-right',
      forceBackFallback: false
    };
  },

  getStyles() {
    return {
      toolbar: {
        background: 'rgba(243, 243, 243, .90)',
        padding: '0 24px 0 12px',
        zIndex: 6,
        justifyContent: 'flex-start'
      },
      toolbarRight: {
        height: '100%',
        display: 'flex',
        alignItems: 'center',
        flex: 1,
        justifyContent: 'flex-end'
      },
      icon: {
        color: 'rgba(0, 0, 0, .4)'
      },
      iconButton: {
        marginTop: 4
      }
    };
  },

  handleBackButtonTouchTap() {
    const { backFallback, forceBackFallback, router } = this.props;

    if (!forceBackFallback) {
      return router.goBack();
    }

    return backFallback();
  },

  renderBackButton() {
    const { backButtonTooltip, backButtonTooltipPosition } = this.props;
    const styles = this.getStyles();

    return (
      <ToolbarGroup>
        <IconButton
          iconClassName="synicon-arrow-left"
          tooltip={backButtonTooltip}
          tooltipPosition={backButtonTooltipPosition}
          onClick={this.handleBackButtonTouchTap}
          style={styles.iconButton}
          iconStyle={styles.icon}
        />
      </ToolbarGroup>
    );
  },

  renderChildren(children) {
    const styles = this.getStyles();

    return (
      <ToolbarGroup style={styles.toolbarRight}>
        {children}
      </ToolbarGroup>
    );
  },

  renderMenu(menu) {
    return (
      <ToolbarGroup>
        {menu}
      </ToolbarGroup>
    );
  },

  renderTitle(title) {
    return (
      <ToolbarGroup>
        <ToolbarTitle
          text={title}
          style={{ paddingLeft: 10 }}
        />
      </ToolbarGroup>
    );
  },

  render() {
    const styles = this.getStyles();
    const { children, menu, title, backButton, backFallback } = this.props;

    return (
      <Sticky offsetTop={50} zIndex={12}>
        <Toolbar
          style={styles.toolbar}
          data-e2e="inner-toolbar"
        >
          {backFallback && backButton ? this.renderBackButton() : null}
          {title ? this.renderTitle(title) : null}
          {menu ? this.renderMenu(menu) : null}
          {children ? this.renderChildren(children) : null}
        </Toolbar>
      </Sticky>
    );
  }
}));

export default withRouter(InnerToolbar);
<commit_msg>[DASH-2228] Fix styles<commit_after>import React from 'react';
import Radium from 'radium';
import { withRouter } from 'react-router';
import Sticky from 'react-stickydiv';

import { Toolbar, ToolbarGroup, ToolbarTitle, IconButton } from 'material-ui';

const InnerToolbar = Radium(React.createClass({
  displayName: 'InnerToolbar',

  propTypes: {
    children: React.PropTypes.node
  },

  getDefaultProps() {
    return {
      backButton: false,
      backButtonTooltipPosition: 'bottom-right',
      forceBackFallback: false
    };
  },

  getStyles() {
    return {
      toolbar: {
        background: 'rgba(243, 243, 243, .90)',
        padding: '0 24px 0 14px',
        zIndex: 6,
        justifyContent: 'flex-start'
      },
      toolbarRight: {
        height: '100%',
        display: 'flex',
        alignItems: 'center',
        flex: 1,
        justifyContent: 'flex-end'
      },
      icon: {
        color: 'rgba(0, 0, 0, .4)'
      },
      iconButton: {
        marginTop: 4
      }
    };
  },

  handleBackButtonTouchTap() {
    const { backFallback, forceBackFallback, router } = this.props;

    if (!forceBackFallback) {
      return router.goBack();
    }

    return backFallback();
  },

  renderBackButton() {
    const { backButtonTooltip, backButtonTooltipPosition } = this.props;
    const styles = this.getStyles();

    return (
      <ToolbarGroup>
        <IconButton
          iconClassName="synicon-arrow-left"
          tooltip={backButtonTooltip}
          tooltipPosition={backButtonTooltipPosition}
          onClick={this.handleBackButtonTouchTap}
          style={styles.iconButton}
          iconStyle={styles.icon}
        />
      </ToolbarGroup>
    );
  },

  renderChildren(children) {
    const styles = this.getStyles();

    return (
      <ToolbarGroup style={styles.toolbarRight}>
        {children}
      </ToolbarGroup>
    );
  },

  renderMenu(menu) {
    return (
      <ToolbarGroup>
        {menu}
      </ToolbarGroup>
    );
  },

  renderTitle(title) {
    return (
      <ToolbarGroup>
        <ToolbarTitle
          text={title}
          style={{ paddingLeft: 10 }}
        />
      </ToolbarGroup>
    );
  },

  render() {
    const styles = this.getStyles();
    const { children, menu, title, backButton, backFallback } = this.props;

    return (
      <Sticky offsetTop={50} zIndex={12}>
        <Toolbar
          style={styles.toolbar}
          data-e2e="inner-toolbar"
        >
          {backFallback && backButton ? this.renderBackButton() : null}
          {title ? this.renderTitle(title) : null}
          {menu ? this.renderMenu(menu) : null}
          {children ? this.renderChildren(children) : null}
        </Toolbar>
      </Sticky>
    );
  }
}));

export default withRouter(InnerToolbar);
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import YoutubeService from '../services/YoutubeService';

export default class RelatedVideoComponent extends Component {

  selectVideo(event) {

    event.preventDefault();

    const youtube = new YoutubeService();
    youtube.videos(this.props.id);
  }

  render() {

    return (

          {this.props.title.substr(0, 62)}

{this.props.channel}

    );
  }
}
<commit_msg>add cursor style<commit_after>import React, {Component} from 'react';
import YoutubeService from '../services/YoutubeService';

export default class RelatedVideoComponent extends Component {

  selectVideo(event) {

    event.preventDefault();

    const youtube = new YoutubeService();
    youtube.videos(this.props.id);
  }

  render() {

    return (

          {this.props.title.substr(0, 62)}

{this.props.channel}

    );
  }
}
<|endoftext|>


<commit_before>import '../../stylus/components/_tables.styl'
import '../../stylus/components/_data-table.styl'

import DataIterable from '../../mixins/data-iterable'

import Head from './mixins/head'
import Body from './mixins/body'
import Foot from './mixins/foot'
import Progress from './mixins/progress'

import {
  createSimpleFunctional,
  getObjectValueByPath
} from '../../util/helpers'

// Importing does not work properly
const VTableOverflow = createSimpleFunctional('v-table__overflow')

/* @vue/component */
export default {
  name: 'v-data-table',

  mixins: [DataIterable, Head, Body, Foot, Progress],

  props: {
    headers: {
      type: Array,
      default: () => []
    },
    headersLength: {
      type: Number
    },
    headerText: {
      type: String,
      default: 'text'
    },
    headerKey: {
      type: String,
      default: null
    },
    hideHeaders: Boolean,
    rowsPerPageText: {
      type: String,
      default: '$vuetify.dataTable.rowsPerPageText'
    },
    customFilter: {
      type: Function,
      default: (items, search, filter, headers) => {
        search = search.toString().toLowerCase()
        if (search.trim() === '') return items

        const props = headers.map(h => h.value)

        return items.filter(item => props.some(prop => filter(getObjectValueByPath(item, prop), search)))
      }
    }
  },

  data () {
    return {
      actionsClasses: 'v-datatable__actions',
      actionsRangeControlsClasses: 'v-datatable__actions__range-controls',
      actionsSelectClasses: 'v-datatable__actions__select',
      actionsPaginationClasses: 'v-datatable__actions__pagination'
    }
  },

  computed: {
    classes () {
      return {
        'v-datatable v-table': true,
        'v-datatable--select-all': this.selectAll !== false,
        ...this.themeClasses
      }
    },
    filteredItems () {
      return this.filteredItemsImpl(this.headers)
    },
    headerColumns () {
      return this.headersLength || this.headers.length + (this.selectAll !== false)
    }
  },

  created () {
    const firstSortable = this.headers.find(h => (
      !('sortable' in h) || h.sortable)
    )

    this.defaultPagination.sortBy = !this.disableInitialSort && firstSortable
      ? firstSortable.value
      : null

    this.initPagination()
  },

  methods: {
    hasTag (elements, tag) {
      return Array.isArray(elements) && elements.find(e => e.tag === tag)
    },
    genTR (children, data = {}) {
      return this.$createElement('tr', data, children)
    }
  },

  render (h) {
    const tableOverflow = h(VTableOverflow, {}, [
      h('table', {
        'class': this.classes
      }, [
        this.genTHead(),
        this.genTBody(),
        this.genTFoot()
      ])
    ])

    return h('div', [
      tableOverflow,
      this.genActionsFooter()
    ])
  }
}
<commit_msg>Add fallback when get object by path (#5090)<commit_after>import '../../stylus/components/_tables.styl'
import '../../stylus/components/_data-table.styl'

import DataIterable from '../../mixins/data-iterable'

import Head from './mixins/head'
import Body from './mixins/body'
import Foot from './mixins/foot'
import Progress from './mixins/progress'

import {
  createSimpleFunctional,
  getObjectValueByPath
} from '../../util/helpers'

// Importing does not work properly
const VTableOverflow = createSimpleFunctional('v-table__overflow')

/* @vue/component */
export default {
  name: 'v-data-table',

  mixins: [DataIterable, Head, Body, Foot, Progress],

  props: {
    headers: {
      type: Array,
      default: () => []
    },
    headersLength: {
      type: Number
    },
    headerText: {
      type: String,
      default: 'text'
    },
    headerKey: {
      type: String,
      default: null
    },
    hideHeaders: Boolean,
    rowsPerPageText: {
      type: String,
      default: '$vuetify.dataTable.rowsPerPageText'
    },
    customFilter: {
      type: Function,
      default: (items, search, filter, headers) => {
        search = search.toString().toLowerCase()
        if (search.trim() === '') return items

        const props = headers.map(h => h.value)

        return items.filter(item => props.some(prop => filter(getObjectValueByPath(item, prop, item[prop]), search)))
      }
    }
  },

  data () {
    return {
      actionsClasses: 'v-datatable__actions',
      actionsRangeControlsClasses: 'v-datatable__actions__range-controls',
      actionsSelectClasses: 'v-datatable__actions__select',
      actionsPaginationClasses: 'v-datatable__actions__pagination'
    }
  },

  computed: {
    classes () {
      return {
        'v-datatable v-table': true,
        'v-datatable--select-all': this.selectAll !== false,
        ...this.themeClasses
      }
    },
    filteredItems () {
      return this.filteredItemsImpl(this.headers)
    },
    headerColumns () {
      return this.headersLength || this.headers.length + (this.selectAll !== false)
    }
  },

  created () {
    const firstSortable = this.headers.find(h => (
      !('sortable' in h) || h.sortable)
    )

    this.defaultPagination.sortBy = !this.disableInitialSort && firstSortable
      ? firstSortable.value
      : null

    this.initPagination()
  },

  methods: {
    hasTag (elements, tag) {
      return Array.isArray(elements) && elements.find(e => e.tag === tag)
    },
    genTR (children, data = {}) {
      return this.$createElement('tr', data, children)
    }
  },

  render (h) {
    const tableOverflow = h(VTableOverflow, {}, [
      h('table', {
        'class': this.classes
      }, [
        this.genTHead(),
        this.genTBody(),
        this.genTFoot()
      ])
    ])

    return h('div', [
      tableOverflow,
      this.genActionsFooter()
    ])
  }
}
<|endoftext|>


<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 React, { PureComponent } from 'react';

import { Tooltip } from 'firefox-profiler/components/tooltip/Tooltip';
import { TooltipMarker } from 'firefox-profiler/components/tooltip/Marker';
import { withSize } from 'firefox-profiler/components/shared/WithSize';
import { VerticalIndicators } from './VerticalIndicators';

import {
  getCommittedRange,
  getZeroAt,
  getPageList,
  getPreviewSelection,
} from 'firefox-profiler/selectors/profile';
import { getThreadSelectors } from 'firefox-profiler/selectors/per-thread';
import {
  TRACK_NETWORK_ROW_HEIGHT,
  TRACK_NETWORK_ROW_REPEAT,
  TRACK_NETWORK_HEIGHT,
} from 'firefox-profiler/app-logic/constants';
import explicitConnect from 'firefox-profiler/utils/connect';
import { bisectionRight } from 'firefox-profiler/utils/bisect';

import type {
  CssPixels,
  ThreadIndex,
  PageList,
  Marker,
  MarkerIndex,
  MarkerTiming,
  Milliseconds,
} from 'firefox-profiler/types';

import type { SizeProps } from 'firefox-profiler/components/shared/WithSize';
import type { ConnectedProps } from 'firefox-profiler/utils/connect';

import './TrackNetwork.css';

/**
 * When adding properties to these props, please consider the comment above the component.
 */
type CanvasProps = {|
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +hoveredMarkerIndex: MarkerIndex | null,
  +width: CssPixels,
  +networkTiming: MarkerTiming[],
  +onHoveredMarkerChange: (
    hoveredMarkerIndex: MarkerIndex | null,
    mouseX?: CssPixels,
    mouseY?: CssPixels
  ) => void,
|};

/**
 * This component controls the rendering of the canvas. Every render call through
 * React triggers a new canvas render. Because of this, it's important to only pass
 * in the props that are needed for the canvas draw call.
 */
class NetworkCanvas extends PureComponent<CanvasProps> {
  _requestedAnimationFrame: boolean = false;
  _canvas = React.createRef<HTMLCanvasElement>();

  _hitTest(e: SyntheticMouseEvent<>): MarkerIndex | null {
    const { rangeStart, rangeEnd, networkTiming, width } = this.props;
    // React's Synthetic event doesn't have these properties, but the native event does.
    const { offsetX: x, offsetY: y } = e.nativeEvent;

    const row = Math.floor(y / TRACK_NETWORK_ROW_HEIGHT);
    const rangeLength = rangeEnd - rangeStart;
    const time = rangeStart + (x / width) * rangeLength;

    // Row i matches network timing's rows i, i + TRACK_NETWORK_ROW_REPEAT, i +
    // TRACK_NETWORK_ROW_REPEAT * 2, etc
    // In each of these row, there can be either 0 or 1 marker that contains
    // this time. We want to keep the marker that's the closest to the time, and
    // these 2 variables will help us with that.
    let closestMarkerIndex = null;
    let closestMarkerStart = -Infinity;
    for (let i = row; i < networkTiming.length; i += TRACK_NETWORK_ROW_REPEAT) {
      const timingRow = networkTiming[i];

      // Bisection returns the index where we would insert the element.
      // Therefore the previous index is where the closest smaller start is,
      // that's the only one in this row that could contain this time.
      const indexInRow = bisectionRight(timingRow.start, time) - 1;

      if (indexInRow < 0) {
        // All markers on this row are after this time.
        continue;
      }

      const start = timingRow.start[indexInRow];
      const end = timingRow.end[indexInRow];

      if (end < time) {
        // The marker we found ends before this time.
        continue;
      }

      if (start > closestMarkerStart) {
        closestMarkerStart = start;
        closestMarkerIndex = timingRow.index[indexInRow];
      }
    }

    return closestMarkerIndex;
  }

  _onMouseLeave = () => {
    this.props.onHoveredMarkerChange(null);
  };

  _onMouseMove = (e: SyntheticMouseEvent<>) => {
    const hoveredMarkerIndex = this._hitTest(e);
    this.props.onHoveredMarkerChange(hoveredMarkerIndex, e.pageX, e.pageY);
  };

  _scheduleDraw() {
    if (!this._requestedAnimationFrame) {
      this._requestedAnimationFrame = true;
      window.requestAnimationFrame(() => {
        this._requestedAnimationFrame = false;
        const canvas = this._canvas.current;
        if (canvas) {
          this.drawCanvas(canvas);
        }
      });
    }
  }

  drawCanvas(canvas: HTMLCanvasElement) {
    const {
      rangeStart,
      rangeEnd,
      networkTiming,
      hoveredMarkerIndex,
      width: containerWidth,
    } = this.props;

    const NORMAL_STYLE = 'rgba(0, 127, 255, 0.3)';
    const HOVERED_STYLE = '#0069aa';
    const rangeLength = rangeEnd - rangeStart;

    const devicePixelRatio = window.devicePixelRatio;
    const rowHeight = TRACK_NETWORK_ROW_HEIGHT * devicePixelRatio;
    canvas.width = Math.round(containerWidth * devicePixelRatio);
    canvas.height = Math.round(TRACK_NETWORK_HEIGHT * devicePixelRatio);
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = NORMAL_STYLE;

    ctx.lineCap = 'round';
    ctx.lineWidth = rowHeight * 0.75;

    let hoveredPath = null;
    for (let rowIndex = 0; rowIndex < networkTiming.length; rowIndex++) {
      const timing = networkTiming[rowIndex];
      for (let timingIndex = 0; timingIndex < timing.length; timingIndex++) {
        const start =
          (canvas.width / rangeLength) *
          (timing.start[timingIndex] - rangeStart);
        const end =
          (canvas.width / rangeLength) * (timing.end[timingIndex] - rangeStart);
        const y =
          (rowIndex % TRACK_NETWORK_ROW_REPEAT) * rowHeight + rowHeight * 0.5;
        const path = new Path2D();
        path.moveTo(start, y);
        path.lineTo(end, y);
        if (timing.index[timingIndex] === hoveredMarkerIndex) {
          // Save the hovered path to draw it at the end, on top of everything else.
          hoveredPath = path;
        } else {
          ctx.stroke(path);
        }
      }
    }

    if (hoveredPath) {
      ctx.strokeStyle = HOVERED_STYLE;
      ctx.stroke(hoveredPath);
    }
  }

  render() {
    this._scheduleDraw();
    return (
      <canvas
        className="timelineTrackNetworkCanvas"
        ref={this._canvas}
        onMouseMove={this._onMouseMove}
        onMouseLeave={this._onMouseLeave}
      />
    );
  }
}

type OwnProps = {|
  +threadIndex: ThreadIndex,
|};

type StateProps = {|
  +pages: PageList | null,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +isModifyingSelection: boolean,
  +zeroAt: Milliseconds,
  +getMarker: MarkerIndex => Marker,
  +networkTiming: MarkerTiming[],
  +verticalMarkerIndexes: MarkerIndex[],
|};
type DispatchProps = {||};
type Props = {|
  ...ConnectedProps<OwnProps, StateProps, DispatchProps>,
  ...SizeProps,
|};
type State = {|
  +hoveredMarkerIndex: MarkerIndex | null,
  +mouseX: CssPixels,
  +mouseY: CssPixels,
|};

class Network extends PureComponent<Props, State> {
  state = { hoveredMarkerIndex: null, mouseX: 0, mouseY: 0 };

  _onHoveredMarkerChange = (
    hoveredMarkerIndex: MarkerIndex | null,
    mouseX?: CssPixels,
    mouseY?: CssPixels
  ) => {
    if (hoveredMarkerIndex === null) {
      if (!window.persistTooltips) {
        // This persistTooltips property is part of the web console API. It helps
        // in being able to inspect and debug tooltips.
        this.setState({
          hoveredMarkerIndex: null,
        });
      }
    } else {
      this.setState({ hoveredMarkerIndex, mouseX, mouseY });
    }
  };

  render() {
    const {
      pages,
      rangeStart,
      rangeEnd,
      getMarker,
      verticalMarkerIndexes,
      zeroAt,
      networkTiming,
      isModifyingSelection,
      threadIndex,
      width: containerWidth,
    } = this.props;
    const { hoveredMarkerIndex, mouseX, mouseY } = this.state;
    const hoveredMarker =
      hoveredMarkerIndex === null ? null : getMarker(hoveredMarkerIndex);
    const shouldShowTooltip = !isModifyingSelection;

    return (
      <div
        className="timelineTrackNetwork"
        style={{
          height: TRACK_NETWORK_HEIGHT,
        }}
      >
        <NetworkCanvas
          rangeStart={rangeStart}
          rangeEnd={rangeEnd}
          networkTiming={networkTiming}
          hoveredMarkerIndex={hoveredMarkerIndex}
          width={containerWidth}
          onHoveredMarkerChange={this._onHoveredMarkerChange}
        />
        <VerticalIndicators
          verticalMarkerIndexes={verticalMarkerIndexes}
          getMarker={getMarker}
          pages={pages}
          rangeStart={rangeStart}
          rangeEnd={rangeEnd}
          zeroAt={zeroAt}
          width={containerWidth}
        />
        {shouldShowTooltip && hoveredMarkerIndex !== null && hoveredMarker ? (
          <Tooltip mouseX={mouseX} mouseY={mouseY}>
            <TooltipMarker
              markerIndex={hoveredMarkerIndex}
              marker={hoveredMarker}
              threadsKey={threadIndex}
              restrictHeightWidth={true}
            />
          </Tooltip>
        ) : null}
      </div>
    );
  }
}

export const TrackNetwork = explicitConnect<
  OwnProps,
  StateProps,
  DispatchProps
>({
  mapStateToProps: (state, ownProps) => {
    const { threadIndex } = ownProps;
    const selectors = getThreadSelectors(threadIndex);
    const { start, end } = getCommittedRange(state);
    const networkTiming = selectors.getNetworkTrackTiming(state);
    return {
      getMarker: selectors.getMarkerGetter(state),
      pages: getPageList(state),
      networkTiming: networkTiming,
      rangeStart: start,
      rangeEnd: end,
      zeroAt: getZeroAt(state),
      isModifyingSelection: getPreviewSelection(state).isModifying,
      verticalMarkerIndexes: selectors.getTimelineVerticalMarkerIndexes(state),
    };
  },
  component: withSize<Props>(Network),
});
<commit_msg>Make it possible to hover on small network markers<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 React, { PureComponent } from 'react';

import { Tooltip } from 'firefox-profiler/components/tooltip/Tooltip';
import { TooltipMarker } from 'firefox-profiler/components/tooltip/Marker';
import { withSize } from 'firefox-profiler/components/shared/WithSize';
import { VerticalIndicators } from './VerticalIndicators';

import {
  getCommittedRange,
  getZeroAt,
  getPageList,
  getPreviewSelection,
} from 'firefox-profiler/selectors/profile';
import { getThreadSelectors } from 'firefox-profiler/selectors/per-thread';
import {
  TRACK_NETWORK_ROW_HEIGHT,
  TRACK_NETWORK_ROW_REPEAT,
  TRACK_NETWORK_HEIGHT,
} from 'firefox-profiler/app-logic/constants';
import explicitConnect from 'firefox-profiler/utils/connect';
import { bisectionRight } from 'firefox-profiler/utils/bisect';

import type {
  CssPixels,
  ThreadIndex,
  PageList,
  Marker,
  MarkerIndex,
  MarkerTiming,
  Milliseconds,
} from 'firefox-profiler/types';

import type { SizeProps } from 'firefox-profiler/components/shared/WithSize';
import type { ConnectedProps } from 'firefox-profiler/utils/connect';

import './TrackNetwork.css';

/**
 * When adding properties to these props, please consider the comment above the component.
 */
type CanvasProps = {|
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +hoveredMarkerIndex: MarkerIndex | null,
  +width: CssPixels,
  +networkTiming: MarkerTiming[],
  +onHoveredMarkerChange: (
    hoveredMarkerIndex: MarkerIndex | null,
    mouseX?: CssPixels,
    mouseY?: CssPixels
  ) => void,
|};

/**
 * This component controls the rendering of the canvas. Every render call through
 * React triggers a new canvas render. Because of this, it's important to only pass
 * in the props that are needed for the canvas draw call.
 */
class NetworkCanvas extends PureComponent<CanvasProps> {
  _requestedAnimationFrame: boolean = false;
  _canvas = React.createRef<HTMLCanvasElement>();

  _hitTest(e: SyntheticMouseEvent<>): MarkerIndex | null {
    const { rangeStart, rangeEnd, networkTiming, width } = this.props;
    // React's Synthetic event doesn't have these properties, but the native event does.
    const { offsetX: x, offsetY: y } = e.nativeEvent;

    const row = Math.floor(y / TRACK_NETWORK_ROW_HEIGHT);
    const rangeLength = rangeEnd - rangeStart;
    const time = rangeStart + (x / width) * rangeLength;
    const minimumSize: CssPixels = 5;
    const minimumDuration: Milliseconds = (minimumSize / width) * rangeLength;

    // Row i matches network timing's rows i, i + TRACK_NETWORK_ROW_REPEAT, i +
    // TRACK_NETWORK_ROW_REPEAT * 2, etc
    // In each of these row, there can be either 0 or 1 marker that contains
    // this time. We want to keep the marker that's the closest to the time, and
    // these 2 variables will help us with that.
    let closestMarkerIndex = null;
    let closestMarkerStart = -Infinity;
    for (let i = row; i < networkTiming.length; i += TRACK_NETWORK_ROW_REPEAT) {
      const timingRow = networkTiming[i];

      // Bisection returns the index where we would insert the element.
      // Therefore the previous index is where the closest smaller start is,
      // that's the only one in this row that could contain this time.
      const indexInRow = bisectionRight(timingRow.start, time) - 1;

      if (indexInRow < 0) {
        // All markers on this row are after this time.
        continue;
      }

      const start = timingRow.start[indexInRow];
      let end = timingRow.end[indexInRow];

      // Make it possible to hit the small markers.
      if (end - start < minimumDuration) {
        end = start + minimumDuration;
      }

      if (end < time) {
        // The marker we found ends before this time.
        continue;
      }

      if (start > closestMarkerStart) {
        closestMarkerStart = start;
        closestMarkerIndex = timingRow.index[indexInRow];
      }
    }

    return closestMarkerIndex;
  }

  _onMouseLeave = () => {
    this.props.onHoveredMarkerChange(null);
  };

  _onMouseMove = (e: SyntheticMouseEvent<>) => {
    const hoveredMarkerIndex = this._hitTest(e);
    this.props.onHoveredMarkerChange(hoveredMarkerIndex, e.pageX, e.pageY);
  };

  _scheduleDraw() {
    if (!this._requestedAnimationFrame) {
      this._requestedAnimationFrame = true;
      window.requestAnimationFrame(() => {
        this._requestedAnimationFrame = false;
        const canvas = this._canvas.current;
        if (canvas) {
          this.drawCanvas(canvas);
        }
      });
    }
  }

  drawCanvas(canvas: HTMLCanvasElement) {
    const {
      rangeStart,
      rangeEnd,
      networkTiming,
      hoveredMarkerIndex,
      width: containerWidth,
    } = this.props;

    const NORMAL_STYLE = 'rgba(0, 127, 255, 0.3)';
    const HOVERED_STYLE = '#0069aa';
    const rangeLength = rangeEnd - rangeStart;

    const devicePixelRatio = window.devicePixelRatio;
    const rowHeight = TRACK_NETWORK_ROW_HEIGHT * devicePixelRatio;
    canvas.width = Math.round(containerWidth * devicePixelRatio);
    canvas.height = Math.round(TRACK_NETWORK_HEIGHT * devicePixelRatio);
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = NORMAL_STYLE;

    ctx.lineCap = 'round';
    ctx.lineWidth = rowHeight * 0.75;

    let hoveredPath = null;
    for (let rowIndex = 0; rowIndex < networkTiming.length; rowIndex++) {
      const timing = networkTiming[rowIndex];
      for (let timingIndex = 0; timingIndex < timing.length; timingIndex++) {
        const start =
          (canvas.width / rangeLength) *
          (timing.start[timingIndex] - rangeStart);
        const end =
          (canvas.width / rangeLength) * (timing.end[timingIndex] - rangeStart);
        const y =
          (rowIndex % TRACK_NETWORK_ROW_REPEAT) * rowHeight + rowHeight * 0.5;
        const path = new Path2D();
        path.moveTo(start, y);
        path.lineTo(end, y);
        if (timing.index[timingIndex] === hoveredMarkerIndex) {
          // Save the hovered path to draw it at the end, on top of everything else.
          hoveredPath = path;
        } else {
          ctx.stroke(path);
        }
      }
    }

    if (hoveredPath) {
      ctx.strokeStyle = HOVERED_STYLE;
      ctx.stroke(hoveredPath);
    }
  }

  render() {
    this._scheduleDraw();
    return (
      <canvas
        className="timelineTrackNetworkCanvas"
        ref={this._canvas}
        onMouseMove={this._onMouseMove}
        onMouseLeave={this._onMouseLeave}
      />
    );
  }
}

type OwnProps = {|
  +threadIndex: ThreadIndex,
|};

type StateProps = {|
  +pages: PageList | null,
  +rangeStart: Milliseconds,
  +rangeEnd: Milliseconds,
  +isModifyingSelection: boolean,
  +zeroAt: Milliseconds,
  +getMarker: MarkerIndex => Marker,
  +networkTiming: MarkerTiming[],
  +verticalMarkerIndexes: MarkerIndex[],
|};
type DispatchProps = {||};
type Props = {|
  ...ConnectedProps<OwnProps, StateProps, DispatchProps>,
  ...SizeProps,
|};
type State = {|
  +hoveredMarkerIndex: MarkerIndex | null,
  +mouseX: CssPixels,
  +mouseY: CssPixels,
|};

class Network extends PureComponent<Props, State> {
  state = { hoveredMarkerIndex: null, mouseX: 0, mouseY: 0 };

  _onHoveredMarkerChange = (
    hoveredMarkerIndex: MarkerIndex | null,
    mouseX?: CssPixels,
    mouseY?: CssPixels
  ) => {
    if (hoveredMarkerIndex === null) {
      if (!window.persistTooltips) {
        // This persistTooltips property is part of the web console API. It helps
        // in being able to inspect and debug tooltips.
        this.setState({
          hoveredMarkerIndex: null,
        });
      }
    } else {
      this.setState({ hoveredMarkerIndex, mouseX, mouseY });
    }
  };

  render() {
    const {
      pages,
      rangeStart,
      rangeEnd,
      getMarker,
      verticalMarkerIndexes,
      zeroAt,
      networkTiming,
      isModifyingSelection,
      threadIndex,
      width: containerWidth,
    } = this.props;
    const { hoveredMarkerIndex, mouseX, mouseY } = this.state;
    const hoveredMarker =
      hoveredMarkerIndex === null ? null : getMarker(hoveredMarkerIndex);
    const shouldShowTooltip = !isModifyingSelection;

    return (
      <div
        className="timelineTrackNetwork"
        style={{
          height: TRACK_NETWORK_HEIGHT,
        }}
      >
        <NetworkCanvas
          rangeStart={rangeStart}
          rangeEnd={rangeEnd}
          networkTiming={networkTiming}
          hoveredMarkerIndex={hoveredMarkerIndex}
          width={containerWidth}
          onHoveredMarkerChange={this._onHoveredMarkerChange}
        />
        <VerticalIndicators
          verticalMarkerIndexes={verticalMarkerIndexes}
          getMarker={getMarker}
          pages={pages}
          rangeStart={rangeStart}
          rangeEnd={rangeEnd}
          zeroAt={zeroAt}
          width={containerWidth}
        />
        {shouldShowTooltip && hoveredMarkerIndex !== null && hoveredMarker ? (
          <Tooltip mouseX={mouseX} mouseY={mouseY}>
            <TooltipMarker
              markerIndex={hoveredMarkerIndex}
              marker={hoveredMarker}
              threadsKey={threadIndex}
              restrictHeightWidth={true}
            />
          </Tooltip>
        ) : null}
      </div>
    );
  }
}

export const TrackNetwork = explicitConnect<
  OwnProps,
  StateProps,
  DispatchProps
>({
  mapStateToProps: (state, ownProps) => {
    const { threadIndex } = ownProps;
    const selectors = getThreadSelectors(threadIndex);
    const { start, end } = getCommittedRange(state);
    const networkTiming = selectors.getNetworkTrackTiming(state);
    return {
      getMarker: selectors.getMarkerGetter(state),
      pages: getPageList(state),
      networkTiming: networkTiming,
      rangeStart: start,
      rangeEnd: end,
      zeroAt: getZeroAt(state),
      isModifyingSelection: getPreviewSelection(state).isModifying,
      verticalMarkerIndexes: selectors.getTimelineVerticalMarkerIndexes(state),
    };
  },
  component: withSize<Props>(Network),
});
<|endoftext|>


<commit_before>const oAuthServerSchema = {
    name: '',
    oauth_endpoints: {
        authorize: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/authorize',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: [] // TODO: should be array?
        },
        token: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/token',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        },
        introspect: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/info',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        },
        revoke: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/revoke',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        }
    },
    oauth_client_endpoints: {
        create: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/clients/create',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            methods: ['POST'],
            hosts: []
        },
        remove: {
            preserve_host: false,
            listen_path: '',
            upstream_url: 'http://localhost:8000/clients/remove',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            methods: ['DELETE'],
            hosts: []
        }
    },
    cors_meta: {
        domains: ['*'],
        methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
        request_headers: ['Origin','Authorization','Content-Type'],
        exposed_headers: ['X-Debug-Token','X-Debug-Token-Link'],
        enabled: true
    },
    rate_limit: {
        limit: {
            value: 0,
            unit: '',
            units: ['S', 'M', 'H'],
            labels: ['Seconds', 'Minutes', 'Hours'],
        },
        enabled: false
    },
    token_strategy: {
        name: ['jwt', 'introspection'],
        // next when JWT is selected
        settings: [
            {
                alg: 'HS256',
                key: ''
            },
            {
                alg: 'RS256',
                key: ''
            },
            // for INTROSPECTION
        ],
        // next when INTROSPECTION is selected
        // settings: {
        //     use_aouth_header: boolen,
        //     auth_header_type: string,//no validation
        // },
    },
};

export default oAuthServerSchema;
<commit_msg>edit schema<commit_after>const oAuthServerSchema = {
    name: '',
    oauth_endpoints: {
        authorize: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: [] // TODO: should be array?
        },
        token: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        },
        introspect: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        },
        revoke: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            enable_load_balancing: false,
            methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
            hosts: []
        }
    },
    oauth_client_endpoints: {
        create: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            methods: ['POST'],
            hosts: []
        },
        remove: {
            preserve_host: false,
            listen_path: '',
            upstream_url: '',
            insecure_skip_verify: false,
            strip_path: false,
            append_path: false,
            methods: ['DELETE'],
            hosts: []
        }
    },
    cors_meta: {
        domains: ['*'],
        methods: ['ALL', 'CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'],
        request_headers: ['Origin','Authorization','Content-Type'],
        exposed_headers: ['X-Debug-Token','X-Debug-Token-Link'],
        enabled: true
    },
    rate_limit: {
        limit: {
            value: 0,
            unit: '',
            units: ['S', 'M', 'H'],
            labels: ['Seconds', 'Minutes', 'Hours'],
        },
        enabled: false
    },
    token_strategy: {
        name: ['jwt', 'introspection'],
        // next when JWT is selected
        settings: [
            {
                alg: 'HS256',
                key: ''
            },
            {
                alg: 'RS256',
                key: ''
            },
            // for INTROSPECTION
        ],
        // next when INTROSPECTION is selected
        // settings: {
        //     use_aouth_header: boolen,
        //     auth_header_type: string,//no validation
        // },
    },
};

export default oAuthServerSchema;
<|endoftext|>


<commit_before>'use strict';

const gulp = require('gulp');
const utils = require('fepper-utils');

gulp.task('help', cb => {
  let out = `
Use:
    fp <task> [<additional args>... [-d | --debug]]

Tasks:
    fp                  Launch Fepper and open it in a browser.
    fp data             Build data.json from underscore-prefixed .json files.
    fp frontend-copy    Copy assets, scripts, and styles to the backend.
    fp once             Do a one-off Fepper build to the public directory.
    fp restart          Restart after shutdown, but without opening the browser.
    fp static           Generate a static site from the 04-pages directory.
    fp syncback         Combine frontend-copy and template tasks.
    fp template         Translate templates in 03-templates for the backend and copy them there.
    fp ui:compile       Compile the UI React components.
    fp ui:help          Display UI tasks and descriptions.
    fp update           Update Fepper NPM, Fepper UI, and Fepper extensions.
    fp version          Print versions of Fepper NPM, Fepper CLI, and Fepper UI.

`;
  out +=
`Note: Windows users not using BASH must recognize \`fp\` as an alias for \`cscript .\\fepper.vbs\` or \`.\\fepper.ps1\`
`;

  utils.info(out);
  cb();
});
<commit_msg>fp help message update<commit_after>'use strict';

const gulp = require('gulp');
const utils = require('fepper-utils');

gulp.task('help', cb => {
  let out = `
Use:
    fp <task> [<additional args>... [-d | --debug]]

Tasks:
    fp                  Launch Fepper and open it in a browser.
    fp data             Build data.json from underscore-prefixed .json files.
    fp frontend-copy    Copy assets, scripts, and styles to the backend.
    fp once             Do a one-off Fepper build to the public directory.
    fp restart          Restart after shutdown, but without opening the browser.
    fp static           Generate a static site from the 04-pages directory.
    fp syncback         Combine frontend-copy and template tasks.
    fp template         Translate templates in 03-templates for the backend and copy them there.
    fp ui:compile       Compile the UI React components.
    fp ui:help          Display UI tasks and descriptions.
    fp update           Update Fepper distro, Fepper CLI, Fepper NPM, Fepper UI, and Fepper extensions.
    fp version          Print versions of Fepper CLI, Fepper NPM, and Fepper UI.
`;

  utils.info(out);
  cb();
});
<|endoftext|>


<commit_before>/**
 * Created by artem on 6/24/15.
 */
(function (angular) {
    'use strict';
    /**
     * Note that if you want custom X button for info window you need to add css
     * .gm-style-iw+div{ display:none }
     * where .gm-style-iw is a class of container element, and next div is close button
     */
    angular.module('LogicifyGMap')
        .directive('logicifyGmapControl',
        [
            '$compile',
            '$log',
            '$timeout',
            function ($compile, $log, $timeout) {
                return {
                    restrict: 'E',
                    require: '^logicifyGmap',
                    link: function (scope, iElement, iAttrs, ctrl) {
                        /*global google*/
                        var position = scope.$eval(iAttrs['controlPosition']);
                        var index = scope.$eval(iAttrs['controlIndex']);
                        var events = scope.$eval(iAttrs['events']);
                        var element = angular.element(iElement.html());

                        function attachListener(eventName, callback) {
                            google.maps.event.addDomListener(element[0], eventName, callback);
                        }

                        element[0].index = index || 0;
                        iElement.empty();
                        ctrl.$mapReady(function (map) {
                            if (!map.controls[position]) {
                                throw new Error('Position of control on the map is invalid. Please see google maps spec.');
                            }
                            map.controls[position].push(element[0]);
                            if (events != null) {
                                angular.forEach(events, function (value, key) {
                                    if (typeof value === 'function') {
                                        attachListener(key, value);
                                    }
                                });
                            }
                        });

                    }
                }
            }
        ]);
})(angular);
<commit_msg>innerHtml<commit_after>/**
 * Created by artem on 6/24/15.
 */
(function (angular) {
    'use strict';
    /**
     * Note that if you want custom X button for info window you need to add css
     * .gm-style-iw+div{ display:none }
     * where .gm-style-iw is a class of container element, and next div is close button
     */
    angular.module('LogicifyGMap')
        .directive('logicifyGmapControl',
        [
            '$compile',
            '$log',
            '$timeout',
            function ($compile, $log, $timeout) {
                return {
                    restrict: 'E',
                    require: '^logicifyGmap',
                    link: function (scope, iElement, iAttrs, ctrl) {
                        /*global google*/
                        var position = scope.$eval(iAttrs['controlPosition']);
                        var index = scope.$eval(iAttrs['controlIndex']);
                        var events = scope.$eval(iAttrs['events']);
                        var element = angular.element(iElement[0].innerHTML);

                        function attachListener(eventName, callback) {
                            google.maps.event.addDomListener(element[0], eventName, callback);
                        }

                        element[0].index = index || 0;
                        iElement.empty();
                        ctrl.$mapReady(function (map) {
                            if (!map.controls[position]) {
                                throw new Error('Position of control on the map is invalid. Please see google maps spec.');
                            }
                            map.controls[position].push(element[0]);
                            if (events != null) {
                                angular.forEach(events, function (value, key) {
                                    if (typeof value === 'function') {
                                        attachListener(key, value);
                                    }
                                });
                            }
                        });

                    }
                }
            }
        ]);
})(angular);
<|endoftext|>


<commit_before>function globalSwitch() {
    $('.switch--item').each(function() {
        $(this).off('click');
        $(this).on('click', function() {
            selectedText = $(this).text()
            selectedIndex = $(this).index()
            $(".switch--item").filter(function() { return ($(this).text() === selectedText) }).each(function() {
                $(this).addClass('selected');
                $(this).siblings().removeClass('selected');
                selectedContent = $(this).parent().siblings(".content").eq(selectedIndex)
                selectedContent.removeClass('hidden');
                selectedContent.siblings().addClass('hidden');
            });
        });
    });
}

$(globalSwitch);<commit_msg>Store refdoc preferred language<commit_after>function globalSwitch() {

    var SPRING_LANGUAGES = ["Java", "Kotlin"];
    var preferredLanguage = initPreferredLanguage();

    function initPreferredLanguage() {
        var lang = window.localStorage.getItem("preferred-spring-language");
        if (SPRING_LANGUAGES.indexOf(lang) === -1) {
            window.localStorage.setItem("preferred-spring-language", SPRING_LANGUAGES[0]);
            lang = SPRING_LANGUAGES[0];
        }
        return lang;
    }

    function switchItem(text, index) {
        if (SPRING_LANGUAGES.indexOf(text) !== -1) {
            window.localStorage.setItem("preferred-spring-language", text);
        }
        $(".switch--item").filter(function() { return ($(this).text() === text) }).each(function() {
            $(this).addClass('selected');
            $(this).siblings().removeClass('selected');
            var selectedContent = $(this).parent().siblings(".content").eq(index)
            selectedContent.removeClass('hidden');
            selectedContent.siblings().addClass('hidden');
        });
    }

    $('.switch--item').each(function() {
        $(this).off('click');
        $(this).on('click', function() {
            var selectedText = $(this).text()
            var selectedIndex = $(this).index()
            switchItem(selectedText, selectedIndex);
        });
    });

    var languageIndex = SPRING_LANGUAGES.indexOf(preferredLanguage);
    if (languageIndex != 0) {
        switchItem(preferredLanguage, languageIndex);
    }
}

$(globalSwitch);<|endoftext|>


<commit_before>/*
 * grunt-bower
 * https://github.com/curist/grunt-bower
 *
 * Copyright (c) 2012 curist
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  // Please see the grunt documentation for more information regarding task and
  // helper creation: https://github.com/gruntjs/grunt/blob/master/docs/toc.md

  // ==========================================================================
  // TASKS
  // ==========================================================================

  var task_name = 'bower';
  var task_desc = 'Copy bower installed components to dist folder.';
  var _ = grunt.utils ? grunt.utils._ : grunt.util._;
  var path = require('path');
  var bower = require('bower');
  var log = grunt.log.write;
  var helpers = require('./lib/helpers').init(grunt);

  grunt.registerMultiTask(task_name, task_desc, function() {
    var done = this.async();
    var targets = (this.file) ? [this.file] : this.files;
    var options = this.data.options || {};
    var stripAffix = options.stripJsAffix || options.stripAffix;

    bower.commands.list({paths: true, relative: false})
      .on('end',  function (data) {
        _(data).each(function(lib_path, lib_name) {
          var dest_file_path;
          var src_paths = helpers.getLibFilenames(
            lib_path,
            bower.config.directory,
            lib_name
          );

          try {
            if(options.ignorePackages) {
              if(!Array.isArray(options.ignorePackages)) {
                throw "`ignorePackages` is not an array";
              }
              if(options.ignorePackages.indexOf(lib_name) != -1) {
                return;
              }
            }

            if(src_paths.length == 0) {
              throw "no files";
            }

            targets.forEach(function(target) {
              var dest = target.dest || path.join('public', 'scripts' ,'vendor');
              var dest_file_name;

              var dests = _(Object.keys(target)).chain().filter(function(option) {
                return _(option).endsWith('_dest');
              }).map(function(dest_opt) {
                var ext_name = dest_opt.replace(/_dest$/, '');
                return [ext_name, target[dest_opt]];
              }).object().value();

              var package_dest = '';
              var package_dests = {};
              var package_opt = options.packageSpecific &&
                options.packageSpecific[lib_name];
              if(package_opt) {
                package_dest = package_opt.dest;
                package_dests = _(Object.keys(package_opt)).chain().filter(function(option) {
                  return _(option).endsWith('_dest');
                }).map(function(dest_opt) {
                  var ext_name = dest_opt.replace(/_dest$/, '');
                  return [ext_name, package_opt[dest_opt]];
                }).object().value();

                if(_(package_opt.files).isArray()) {
                  src_paths = _(package_opt.files).reduce(function(p, file) {
                    return p.concat(grunt.file.expand(path.join(bower.config.directory, lib_name, file)));
                  }, []);
                }
              }

              // check if we want to strip file type affix in lib_name
              var ext = src_paths[0].split('.').pop()
              if(stripAffix) {
                var regexp = new RegExp("\\W?" + ext + "$");
                dest_file_name = lib_name.replace(regexp, '') + '.' + ext;
              } else {
                dest_file_name = lib_name + '.' + ext;
              }

              if(src_paths.length == 1 && (!package_opt || !package_opt.files)) {
                var ext_name = dest_file_name.split('.').pop();
                dest_file_path = path.join(
                  package_dests[ext_name] || dests[ext_name] || package_dest || dest,
                  options.expand ? lib_name : '',
                  dest_file_name
                );
                grunt.file.copy(src_paths[0], dest_file_path);
                log(src_paths[0].cyan + ' copied.\n');

              } else {
                var expanded_dir = '', file_name, ext_name, dest_dir;
                var flatten = package_opt.keepExpandedHierarchy === false || options.keepExpandedHierarchy === false;
                src_paths.forEach(function(src_path) {
                  if (!flatten && expanded_dir && src_path.indexOf(expanded_dir) > -1)
                    file_name = src_path.replace(expanded_dir, '');
                  else
                    file_name = src_path.split(/[\\\/]/).pop();
                  ext_name = file_name.split('.').pop();
                  dest_dir = package_dests[ext_name] ||
                    dests[ext_name] || package_dest || dest;

                  dest_file_path = path.join(
                    dest_dir,
                    options.expand ? lib_name : '',
                    file_name
                  );
                  try{
                    if (file_name && grunt.file.isDir(src_path)) {
                      if (!flatten && !expanded_dir) {
                        expanded_dir = src_path;
                      } else if (!flatten && src_path.indexOf(expanded_dir) == -1) {
                        expanded_dir = '';
                      }
                    } else if (file_name) {
                      grunt.file.copy(src_path, dest_file_path);
                      log(dest_file_path.cyan + ' copied.\n');
                    }
                  } catch(e) {
                    log(('Fail to copy ').red +
                        src_path.yellow + (' for ').red +
                        lib_name.yellow + ('!\n').red);
                  }
                });
              }
            });
          } catch (err) {
            log(('Fail to copy lib file for ' + lib_name + '!\n').red);
          }
        });
        done();
      })
      .on('error', function (err) {
        grunt.fail.warn(err);
      });
  });
};
<commit_msg>Revert "Added support for path expansion, Added option : keepExpandedHierarchy"<commit_after>/*
 * grunt-bower
 * https://github.com/curist/grunt-bower
 *
 * Copyright (c) 2012 curist
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  // Please see the grunt documentation for more information regarding task and
  // helper creation: https://github.com/gruntjs/grunt/blob/master/docs/toc.md

  // ==========================================================================
  // TASKS
  // ==========================================================================

  var task_name = 'bower';
  var task_desc = 'Copy bower installed components to dist folder.';
  var _ = grunt.utils ? grunt.utils._ : grunt.util._;
  var path = require('path');
  var bower = require('bower');
  var log = grunt.log.write;
  var helpers = require('./lib/helpers').init(grunt);

  grunt.registerMultiTask(task_name, task_desc, function() {
    var done = this.async();
    var targets = (this.file) ? [this.file] : this.files;
    var options = this.data.options || {};
    var stripAffix = options.stripJsAffix || options.stripAffix;

    bower.commands.list({paths: true, relative: false})
      .on('end',  function (data) {
        _(data).each(function(lib_path, lib_name) {
          var dest_file_path;
          var src_paths = helpers.getLibFilenames(
            lib_path,
            bower.config.directory,
            lib_name
          );

          try {
            if(options.ignorePackages) {
              if(!Array.isArray(options.ignorePackages)) {
                throw "`ignorePackages` is not an array";
              }
              if(options.ignorePackages.indexOf(lib_name) != -1) {
                return;
              }
            }

            if(src_paths.length == 0) {
              throw "no files";
            }

            targets.forEach(function(target) {
              var dest = target.dest || path.join('public', 'scripts' ,'vendor');
              var dest_file_name;

              var dests = _(Object.keys(target)).chain().filter(function(option) {
                return _(option).endsWith('_dest');
              }).map(function(dest_opt) {
                var ext_name = dest_opt.replace(/_dest$/, '');
                return [ext_name, target[dest_opt]];
              }).object().value();

              var package_dest = '';
              var package_dests = {};
              var package_opt = options.packageSpecific &&
                options.packageSpecific[lib_name];
              if(package_opt) {
                package_dest = package_opt.dest;
                package_dests = _(Object.keys(package_opt)).chain().filter(function(option) {
                  return _(option).endsWith('_dest');
                }).map(function(dest_opt) {
                  var ext_name = dest_opt.replace(/_dest$/, '');
                  return [ext_name, package_opt[dest_opt]];
                }).object().value();

                if(_(package_opt.files).isArray()) {
                  src_paths = _(package_opt.files).map(function(file) {
                    return path.join(bower.config.directory, lib_name, file);
                  });
                }
              }

              // check if we want to strip file type affix in lib_name
              var ext = src_paths[0].split('.').pop()
              if(stripAffix) {
                var regexp = new RegExp("\\W?" + ext + "$");
                dest_file_name = lib_name.replace(regexp, '') + '.' + ext;
              } else {
                dest_file_name = lib_name + '.' + ext;
              }

              if(src_paths.length == 1 && (!package_opt || !package_opt.files)) {
                var ext_name = dest_file_name.split('.').pop();
                dest_file_path = path.join(
                  package_dests[ext_name] || dests[ext_name] || package_dest || dest,
                  options.expand ? lib_name : '',
                  dest_file_name
                );
                grunt.file.copy(src_paths[0], dest_file_path);
                log(src_paths[0].cyan + ' copied.\n');

              } else {
                src_paths.forEach(function(src_path) {
                  var file_name = src_path.split(/[\\\/]/).pop();
                  var ext_name = file_name.split('.').pop();
                  var dest_dir = package_dests[ext_name] ||
                    dests[ext_name] || package_dest || dest;

                  dest_file_path = path.join(
                    dest_dir,
                    options.expand ? lib_name : '',
                    file_name
                  );
                  try{
                    grunt.file.copy(src_path, dest_file_path);
                    log(src_path.cyan + ' copied.\n');
                  } catch(e) {
                    log(('Fail to copy ').red +
                        src_path.yellow + (' for ').red +
                        lib_name.yellow + ('!\n').red);
                  }
                });
              }
            });
          } catch (err) {
            log(('Fail to copy lib file for ' + lib_name + '!\n').red);
          }
        });
        done();
      })
      .on('error', function (err) {
        grunt.fail.warn(err);
      });
  });
};
<|endoftext|>


<commit_before>const inFileName = 'styles.less';
const outFileName = 'styles.css';
const lessOptions = {
    compress: false,
    relativeUrls: true
};

export function lessBuild() {
    const fs = require('fs');
    const path = require('path');
    const recursiveReadDir = require('node-dir');
    const mkdirp = require('mkdirp');
    const less = require('less');

    const inRootDir = path.resolve('src/');
    const outRootDir = path.resolve('build/');

    return new Promise((rootResolve, rootReject) => {
        recursiveReadDir.paths(inRootDir, (readDirErr, readDirResult) => {
            if (readDirErr) {
                return rootReject(readDirErr);
            }

            return Promise.all(
                readDirResult.files
                    .filter(inFile => path.extname(inFile) === path.extname(inFileName))
                    .map(inFile => {
                        const inDir = path.dirname(inFile);
                        const relativeInDir = path.relative(inRootDir, inDir);
                        const outDir = path.resolve(outRootDir, relativeInDir);
                        const outFile = path.resolve(outDir, outFileName);

                        return new Promise((resolve, reject) => {
                            mkdirp(outDir, (mkdirpErr) => {
                                if (mkdirpErr) {
                                    return reject(mkdirpErr);
                                }

                                fs.readFile(inFile, 'utf-8', (readFileErr, readFileResult) => {
                                    if (readFileErr) {
                                        return reject(readFileErr);
                                    }

                                    less.render(readFileResult, lessOptions, (lessErr, lessResult) => {
                                        if (lessErr) {
                                            return reject(lessErr);
                                        }

                                        fs.writeFile(outFile, lessResult.css, 'utf-8', (writeFileErr) => {
                                            if (writeFileErr) {
                                                return reject(writeFileErr);
                                            }

                                            resolve();
                                        });
                                    });
                                });
                            });
                        });
                    })
            ).then(rootResolve);
        });
    });
}
<commit_msg>:heavy_check_mark: fix error catching in tasks/build<commit_after>const inFileName = 'styles.less';
const outFileName = 'styles.css';
const lessOptions = {
    compress: false
};

export function lessBuild() {
    const fs = require('fs');
    const path = require('path');
    const recursiveReadDir = require('node-dir');
    const mkdirp = require('mkdirp');
    const less = require('less');

    const inRootDir = path.resolve('src/');
    const outRootDir = path.resolve('build/');

    return new Promise((rootResolve, rootReject) => {
        recursiveReadDir.paths(inRootDir, (readDirErr, readDirResult) => {
            if (readDirErr) {
                return rootReject(readDirErr);
            }

            return Promise.all(
                readDirResult.files
                    .filter(inFile => path.extname(inFile) === path.extname(inFileName))
                    .map(inFile => {
                        const inDir = path.dirname(inFile);
                        const relativeInDir = path.relative(inRootDir, inDir);
                        const outDir = path.resolve(outRootDir, relativeInDir);
                        const outFile = path.resolve(outDir, outFileName);

                        return new Promise((resolve, reject) => {
                            mkdirp(outDir, (mkdirpErr) => {
                                if (mkdirpErr) {
                                    return reject(mkdirpErr);
                                }

                                fs.readFile(inFile, 'utf-8', (readFileErr, readFileResult) => {
                                    if (readFileErr) {
                                        return reject(readFileErr);
                                    }

                                    less.render(readFileResult, lessOptions, (lessErr, lessResult) => {
                                        if (lessErr) {
                                            return reject(lessErr);
                                        }

                                        fs.writeFile(outFile, lessResult.css, 'utf-8', (writeFileErr) => {
                                            if (writeFileErr) {
                                                return reject(writeFileErr);
                                            }

                                            resolve();
                                        });
                                    });
                                });
                            });
                        });
                    })
            )
            .then(rootResolve)
            .catch(rootReject);
        });
    });
}
<|endoftext|>


<commit_before><commit_msg>Fix clean script (#74)<commit_after><|endoftext|>


<commit_before>/*
 * grunt-cover
 * https://github.com/grundjoseph/grunt-coverjs
 *
 * Copyright (c) 2012 Joe Grund
 * Licensed under the MIT license.
 */

module.exports = function (grunt) {
  'use strict';

  grunt.registerMultiTask('cover', 'Instruments JavaScript Files using coverjs.', function () {
    var path = require('path');

    var helpers = require('grunt-lib-contrib').init(grunt);

    var options = helpers.options(this, {
      basePath: false,
      flatten: false
    });

    // TODO: ditch this when grunt v0.4 is released
    this.files = this.files || helpers.normalizeMultiTaskFiles(this.data, this.target);

    var srcFiles;

    var basePath;

    var newFileDest;

    var instrumentedSrc;

    this.files.forEach(function (file) {
      file.dest = path.normalize(file.dest);
      srcFiles = grunt.file.expandFiles(file.src);

      if (srcFiles.length === 0) {
        grunt.log.error('No source files found');
        return;
      }

      srcFiles.forEach(function (srcFile) {
        instrumentedSrc = coverFile(srcFile);

        basePath = helpers.findBasePath(srcFiles, options.basePath);
        newFileDest = helpers.buildIndividualDest(file.dest, srcFile, basePath, options.flatten);

        grunt.file.write(newFileDest, instrumentedSrc || '');
        grunt.log.ok('Created: ' + newFileDest);
      });

    });

    function coverFile(srcFile) {
      var srcCode = grunt.file.read(srcFile);
      var Instrument = require('coverjs').Instrument;

      try {
        return new Instrument(srcCode, srcFile).instrument();
      } catch (e) {
        grunt.log.error(e);
        grunt.log.error('File could not be instrumented.');
      }
    }

  });
};
<commit_msg>Slight code cleanup.<commit_after>/*
 * grunt-coverjs
 * https://github.com/grundjoseph/grunt-coverjs
 *
 * Copyright (c) 2012 Joe Grund
 * Licensed under the MIT license.
 */

module.exports = function (grunt) {
  'use strict';

  grunt.registerMultiTask('cover', 'Instruments JavaScript Files using coverjs.', function () {
    var path = require('path');
    var helpers = require('grunt-lib-contrib').init(grunt);
    var _ = grunt.utils._;
    var Instrument = require('coverjs').Instrument;

    var options = helpers.options(this, {
      basePath: false,
      flatten: false
    });

    var srcFiles;
    var basePath;
    var instrumentedSrc;
    var newFileDest;

    //TODO: ditch this when grunt v0.4 is released
    this.files = this.files || helpers.normalizeMultiTaskFiles(this.data, this.target);

    /**
     * Instruments the given source file.
     *
     * If the instrument method throws an error this task will force an abort.
     *
     * @param srcFile
     * @return {String} Returns the instrumented source file as a string.
     */
    function coverFile(srcFile) {
      var srcCode = grunt.file.read(srcFile);
      Instrument = require('coverjs').Instrument;

      try {
        return new Instrument(srcCode, srcFile).instrument();
      } catch (e) {
        grunt.log.error(_.sprintf('File %s could not be instrumented.', srcFile));
        grunt.fatal(e, 3);
      }
    }

    this.files.forEach(function (file) {
      file.dest = path.normalize(file.dest);
      srcFiles = grunt.file.expandFiles(file.src);

      if (srcFiles.length === 0) {
        grunt.log.error('No source files found');
        return;
      }

      srcFiles.forEach(function (srcFile) {
        instrumentedSrc = coverFile(srcFile);

        basePath = helpers.findBasePath(srcFiles, options.basePath);
        newFileDest = helpers.buildIndividualDest(file.dest, srcFile, basePath, options.flatten);

        grunt.file.write(newFileDest, instrumentedSrc);
        grunt.log.ok(_.sprintf('Created: %s', newFileDest));
      });
    });
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-force
 * https://github.com/couchand/grunt-force
 *
 * Copyright (c) 2013 Andrew Couch
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  grunt.registerMultiTask('force', 'Interact with a Salesforce.com environment', function(sftask) {
    var done = this.async();
    var options = this.options({
      tests: '*Test*.cls'
    });
    var file_groups = this.files;
    var task;

    grunt.log.writeln('force plugin executing task '+sftask);

    if ( sftask === 'validate' ) {
      options.isCheck = true;
      task = deploy;
    }
    else if ( sftask === 'deploy' ) {
      task = deploy;
    }
    else if ( sftask === 'test' ) {
      task = test;
    }
    else {
      grunt.fail.warn('unknown task');
      return 42;
    }

    login(options).then(function(conn) {
      return task(conn, file_groups, options);
    }).then(done);
  });

  function login(options) {
    var sfdc = require('../lib/force-0.1.0.js');

    grunt.log.writeln('loading credentials from '+options.credentials);

    var credfile = grunt.file.read(options.credentials);
    var creds = JSON.parse(credfile);

    grunt.log.writeln('logging in to '+creds.login_url+' as '+creds.username);

    return sfdc.connect(creds).then(function(c) {
      grunt.log.writeln('logged in successfully');
      return c;
    },
    function(err) {
      grunt.log.writeln(err);
      grunt.fail.warn('authorization error');
    });
  }

  function test(conn, file_groups, options) {
    var promise = require('node-promise');
    var data = {};

    grunt.log.writeln('running tests');

    var testRuns = [];

    // Iterate over all specified file groups.
    file_groups.forEach(function(fileObj) {
      // The source files to be concatenated. The "nonull" option is used
      // to retain invalid files/patterns so they can be warned about.
      var files = grunt.file.expand({nonull: true}, fileObj.src);

      var test_files = grunt.file.match({ matchBase: true }, options.tests, files);

      test_files.map(function(filepath) {
        var class_name = filepath.match(/\/([^\/]*)\.cls$/)[1];

        testRuns.push(conn.test(class_name));
      });
    });

    return promise.allOrNone.apply(promise, testRuns).then(function(runs) {
      var success = true;
      runs.forEach(function(results) {
        results.forEach(function(res) {
          success = success && res.Outcome === 'Pass';
          var stack_trace = res.StackTrace && res.StackTrace.replace(/\n/g, "\n > ");
          var test_result = '' + res.ApexClass.Name + '.' + res.MethodName + ': ' + res.Outcome +
                            (res.Outcome !== 'Pass' ? ' - ' + res.Message + '.\n > ' + stack_trace : '');
          grunt.log.writeln(test_result);
        });
      });
      if ( success ) {
        grunt.log.writeln('All tests pass.');
      }
      else {
        grunt.fail.warn('test failures.');
      }
    });
  }

  function deploy(conn, file_groups, options) {
    var promise = require('node-promise');
    var data = {};

    grunt.log.writeln('looking for container');

    return conn.tooling.query('select Id from MetadataContainer where Name = \'' + options.container + "'").then(function(containers) {
      if ( containers && containers.length ) {
        grunt.log.writeln('found container');
        // found container, use it
        var now = new promise.Deferred();
        now.resolve( containers[0].Id );
        return now;
      }
      return conn.tooling.insert('MetadataContainer', { 'Name': options.container });
    }).then(function(new_id) {

      data.containerId = new_id;
      var junctionInserts = [];

      grunt.log.writeln('created MetadataContainer with Id '+new_id);
      grunt.log.writeln('creating junction objects now...');

      // Iterate over all specified file groups.
      file_groups.forEach(function(fileObj) {
        // The source files to be concatenated. The "nonull" option is used
        // to retain invalid files/patterns so they can be warned about.
        var files = grunt.file.expand({nonull: true}, fileObj.src);

        files.map(function(filepath) {
          var class_name = filepath.match(/\/([^\/]*)\.cls$/)[1];

          // Warn if a source file/pattern was invalid.
          if (!grunt.file.exists(filepath)) {
            grunt.log.error('Source file "' + filepath + '" not found.');
            return '';
          }

          grunt.log.writeln('adding class ' + class_name);

          junctionInserts.push(
            conn.query('select Id from ApexClass where Name = \''+class_name+"'").then(function(classes) {
              if ( classes && classes.length ) {
                var classId = classes[0].Id;
                grunt.log.writeln('found class '+class_name+' with id '+classId);
                return classId;
              }
              else {
                grunt.log.writeln('creating apex class '+class_name);
                return conn.tooling.insert('ApexClass',{
                  'Name': class_name,
                  'Body': 'public class ' + class_name + ' {}'
                }).then(function(new_id) {
                  grunt.log.writeln('successfully created new ApexClass '+class_name + ' ' + new_id);
                  return new_id;
                });
              }
            }).then(function(classId) {
              grunt.log.writeln('searching for classmember');
              return conn.tooling.query('select Id from ApexClassMember where MetadataContainerId = \'' +
                                  data.containerId + '\' and ContentEntityId = \'' + classId + "'").then(function(acms) {
                if ( acms && acms.length ) {
                  grunt.log.writeln('found classmember with id '+acms[0].Id);
                  var acmId = acms[0].Id;
                  return conn.tooling.update('ApexClassMember', acmId, {
                    'Body': grunt.file.read(filepath)
                  });
                }

                grunt.log.writeln('creating classmember');
                return conn.tooling.insert('ApexClassMember', {
                  'MetadataContainerId': data.containerId,
                  'ContentEntityId': classId,
                  'Body': grunt.file.read(filepath)
                }).then(function(new_id) {
                  if ( !new_id ) {
                    grunt.fail.fatal('error inserting junction object');
                  }
                  grunt.log.writeln('inserted acm for class '+class_name+' new id is '+new_id);
                  return new_id;
                }, function(err){
                  grunt.fail.fatal('error inserting junction object: '+err);
                });
              });
            })
          );
        });
      });

      grunt.log.writeln('added ' + junctionInserts.length + ' classes');
      grunt.log.writeln('waiting for all adds to complete');
      return promise.allOrNone.call(promise, junctionInserts);
    }).then(function(new_members) {
      grunt.log.writeln('added '+new_members.length);
      grunt.log.writeln('all adds complete');

      grunt.log.writeln('submitting deployment request');

      return conn.tooling.deploy(data.containerId, options.isCheck);
    }).then(function(results) {

      grunt.log.writeln( (options.isCheck?'validation':'deployment') + ' complete');
    }, function(e) {
      var errors = '', i, err;

      var compile = e.CompilerErrors && JSON.parse( e.CompilerErrors );

      if ( e.CompilerErrors && compile.length ) {
        for ( i = 0; i < compile.length; i++ ) {
          err = compile[i];
          errors += 'CompilerError: ' + err.extent + ' ' + err.name + ' (line ' + err.line + '): ' + err.problem + "\n";
        }
      }

      if ( e.ErrorMsg ) {
        errors = e.ErrorMsg + "\n" + errors;
      }

      grunt.fail.fatal( errors );
    });
  }

};
<commit_msg>task to get arbitrary rest resource<commit_after>/*
 * grunt-force
 * https://github.com/couchand/grunt-force
 *
 * Copyright (c) 2013 Andrew Couch
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  grunt.registerMultiTask('force', 'Interact with a Salesforce.com environment', function(sftask) {
    var done = this.async();
    var options = this.options({
      tests: '*Test*.cls',
      resource: '/' + this.args.slice(1).join('/')
    });
    var file_groups = this.files;
    var task;

    grunt.log.writeln('force plugin executing task '+sftask);

    if ( sftask === 'validate' ) {
      options.isCheck = true;
      task = deploy;
    }
    else if ( sftask === 'deploy' ) {
      task = deploy;
    }
    else if ( sftask === 'test' ) {
      task = test;
    }
    else if ( sftask === 'get' ) {
      task = getArbitrary;
    }
    else {
      grunt.fail.warn('unknown task');
      return 42;
    }

    login(options).then(function(conn) {
      return task(conn, file_groups, options);
    }).then(done);
  });

  function login(options) {
    var sfdc = require('../lib/force-0.1.0.js');

    grunt.log.writeln('loading credentials from '+options.credentials);

    var credfile = grunt.file.read(options.credentials);
    var creds = JSON.parse(credfile);

    grunt.log.writeln('logging in to '+creds.login_url+' as '+creds.username);

    return sfdc.connect(creds).then(function(c) {
      grunt.log.writeln('logged in successfully');
      return c;
    },
    function(err) {
      grunt.log.writeln(err);
      grunt.fail.warn('authorization error');
    });
  }

  function getArbitrary(conn, file_groups, options) {
    var promise = new require('node-promise').Deferred();

    grunt.log.writeln('getting resource ' + options.resource);
    conn.get(options.resource).then(function(res) {
      grunt.log.writeln(JSON.stringify(res, null, 2));

      promise.resolve();
    }, function(err) {
      grunt.fail.warn(err);
    });

    return promise;
  }

  function test(conn, file_groups, options) {
    var promise = require('node-promise');
    var data = {};

    grunt.log.writeln('running tests');

    var testRuns = [];

    // Iterate over all specified file groups.
    file_groups.forEach(function(fileObj) {
      // The source files to be concatenated. The "nonull" option is used
      // to retain invalid files/patterns so they can be warned about.
      var files = grunt.file.expand({nonull: true}, fileObj.src);

      var test_files = grunt.file.match({ matchBase: true }, options.tests, files);

      test_files.map(function(filepath) {
        var class_name = filepath.match(/\/([^\/]*)\.cls$/)[1];

        testRuns.push(conn.test(class_name));
      });
    });

    return promise.allOrNone.apply(promise, testRuns).then(function(runs) {
      var success = true;
      runs.forEach(function(results) {
        results.forEach(function(res) {
          success = success && res.Outcome === 'Pass';
          var stack_trace = res.StackTrace && res.StackTrace.replace(/\n/g, "\n > ");
          var test_result = '' + res.ApexClass.Name + '.' + res.MethodName + ': ' + res.Outcome +
                            (res.Outcome !== 'Pass' ? ' - ' + res.Message + '.\n > ' + stack_trace : '');
          grunt.log.writeln(test_result);
        });
      });
      if ( success ) {
        grunt.log.writeln('All tests pass.');
      }
      else {
        grunt.fail.warn('test failures.');
      }
    });
  }

  function deploy(conn, file_groups, options) {
    var promise = require('node-promise');
    var data = {};

    grunt.log.writeln('looking for container');

    return conn.tooling.query('select Id from MetadataContainer where Name = \'' + options.container + "'").then(function(containers) {
      if ( containers && containers.length ) {
        grunt.log.writeln('found container');
        // found container, use it
        var now = new promise.Deferred();
        now.resolve( containers[0].Id );
        return now;
      }
      return conn.tooling.insert('MetadataContainer', { 'Name': options.container });
    }).then(function(new_id) {

      data.containerId = new_id;
      var junctionInserts = [];

      grunt.log.writeln('created MetadataContainer with Id '+new_id);
      grunt.log.writeln('creating junction objects now...');

      // Iterate over all specified file groups.
      file_groups.forEach(function(fileObj) {
        // The source files to be concatenated. The "nonull" option is used
        // to retain invalid files/patterns so they can be warned about.
        var files = grunt.file.expand({nonull: true}, fileObj.src);

        files.map(function(filepath) {
          var class_name = filepath.match(/\/([^\/]*)\.cls$/)[1];

          // Warn if a source file/pattern was invalid.
          if (!grunt.file.exists(filepath)) {
            grunt.log.error('Source file "' + filepath + '" not found.');
            return '';
          }

          grunt.log.writeln('adding class ' + class_name);

          junctionInserts.push(
            conn.query('select Id from ApexClass where Name = \''+class_name+"'").then(function(classes) {
              if ( classes && classes.length ) {
                var classId = classes[0].Id;
                grunt.log.writeln('found class '+class_name+' with id '+classId);
                return classId;
              }
              else {
                grunt.log.writeln('creating apex class '+class_name);
                return conn.tooling.insert('ApexClass',{
                  'Name': class_name,
                  'Body': 'public class ' + class_name + ' {}'
                }).then(function(new_id) {
                  grunt.log.writeln('successfully created new ApexClass '+class_name + ' ' + new_id);
                  return new_id;
                });
              }
            }).then(function(classId) {
              grunt.log.writeln('searching for classmember');
              return conn.tooling.query('select Id from ApexClassMember where MetadataContainerId = \'' +
                                  data.containerId + '\' and ContentEntityId = \'' + classId + "'").then(function(acms) {
                if ( acms && acms.length ) {
                  grunt.log.writeln('found classmember with id '+acms[0].Id);
                  var acmId = acms[0].Id;
                  return conn.tooling.update('ApexClassMember', acmId, {
                    'Body': grunt.file.read(filepath)
                  });
                }

                grunt.log.writeln('creating classmember');
                return conn.tooling.insert('ApexClassMember', {
                  'MetadataContainerId': data.containerId,
                  'ContentEntityId': classId,
                  'Body': grunt.file.read(filepath)
                }).then(function(new_id) {
                  if ( !new_id ) {
                    grunt.fail.fatal('error inserting junction object');
                  }
                  grunt.log.writeln('inserted acm for class '+class_name+' new id is '+new_id);
                  return new_id;
                }, function(err){
                  grunt.fail.fatal('error inserting junction object: '+err);
                });
              });
            })
          );
        });
      });

      grunt.log.writeln('added ' + junctionInserts.length + ' classes');
      grunt.log.writeln('waiting for all adds to complete');
      return promise.allOrNone.call(promise, junctionInserts);
    }).then(function(new_members) {
      grunt.log.writeln('added '+new_members.length);
      grunt.log.writeln('all adds complete');

      grunt.log.writeln('submitting deployment request');

      return conn.tooling.deploy(data.containerId, options.isCheck);
    }).then(function(results) {

      grunt.log.writeln( (options.isCheck?'validation':'deployment') + ' complete');
    }, function(e) {
      var errors = '', i, err;

      var compile = e.CompilerErrors && JSON.parse( e.CompilerErrors );

      if ( e.CompilerErrors && compile.length ) {
        for ( i = 0; i < compile.length; i++ ) {
          err = compile[i];
          errors += 'CompilerError: ' + err.extent + ' ' + err.name + ' (line ' + err.line + '): ' + err.problem + "\n";
        }
      }

      if ( e.ErrorMsg ) {
        errors = e.ErrorMsg + "\n" + errors;
      }

      grunt.fail.fatal( errors );
    });
  }

};
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var elastic = require('elasticsearch');
var async = require('async');

module.exports = function(grunt) {
  grunt.registerMultiTask("elasticsearchbulk", "Grunt task for working with the elasticsearch bulk api", function() {
    var done = this.async();
    var options = this.options();
    var files = this.filesSrc;

   	var defaults = {
   		elastic : {
	    host: 'http://127.0.0.1:9200',
	    client: {
	      apiVersion: "1.0",
	      log: "warning"
	    }
	  }
	};

	var options = _.extend(defaults, options);
	var client = new elastic.Client(options);

	async.eachSeries(files, function(file, callback){

  		grunt.log.ok('starting elastic search bulk for ', file);

    	var data = grunt.file.readJSON(file);

    	client.bulk({ body: data }, function(err){
    		if (err) {
      			grunt.fail.warn('Unable to connect to elastic search');
    		}

			if (!err) {
				grunt.log.ok('elastic search bulk run complete.');
			}

			callback();
		});
  	},
    function(err){
    	if (err) done(false);
    	done();
  	});
  });
};<commit_msg>adding timeout, does not complete in time when running within multiple grunt tasks<commit_after>'use strict';

var _ = require('lodash');
var elastic = require('elasticsearch');
var async = require('async');

module.exports = function(grunt) {
  grunt.registerMultiTask("elasticsearchbulk", "Grunt task for working with the elasticsearch bulk api", function() {
    var done = this.async();
    var options = this.options();
    var files = this.filesSrc;

   	var defaults = {
   		elastic : {
	    host: 'http://127.0.0.1:9200',
	    client: {
	      apiVersion: "1.0",
	      log: "warning"
	    }
	  }
	};

	var options = _.extend(defaults, options);
	var client = new elastic.Client(options);

	async.eachSeries(files, function(file, callback){

  		grunt.log.ok('starting elastic search bulk for ', file);

    	var data = grunt.file.readJSON(file);

    	client.bulk({ body: data }, function(err){
    		if (err) {
      			grunt.fail.warn('Unable to connect to elastic search');
    		}

			if (!err) {
				grunt.log.ok('elastic search bulk run complete.');
			}

			callback();
		});
  	},
    function(err){
    	setInterval(function(){
    		if (err) return done(false);
    		done();
    	}, 1000);
  	});
  });
};<|endoftext|>