<commit_before>//const defaultOuterColor = 'rgba(253, 246, 227, 0.66)';
const defaultOuterColor = '#fdf6e3';
const defaultInnerColor = '#ffffff';
const defaultPerpPixLen = 20;

// Construct style function for a linear measurement.
//
// If the feature has a length, it is used as a label.
//
// Options may have the following properies:
//
//  innerColor: inner colour of line
//  outerColor: outer colour of line
//  lengthUnit: unit used for displaying length
//  labelFunc: function called with feature, start and end point. Returns label.
//
export function linearMeasurementStyle(options) {
  options = Object.assign({
    innerColor: defaultInnerColor,
    outerColor: defaultOuterColor,
    labelFunc: () => '',
    textShadowSize: 8,
  }, options);

  return (feature, resolution) => {
    // resolution is "projection units per pixel"
    let perpPixLen = defaultPerpPixLen, perpLen = resolution * perpPixLen;

    let outerStrokeStyle = new ol.style.Stroke({
      color: options.innerColor, width: 2 });
    let innerStrokeStyle = new ol.style.Stroke({
      color: options.outerColor, width: 4 });

    let styles = [
      new ol.style.Style({ stroke: outerStrokeStyle, zIndex: 100 }),
      new ol.style.Style({ stroke: innerStrokeStyle, zIndex: 90 }),
    ];

    let geometry = feature.getGeometry();

    if(geometry.getType() === 'LineString') {
      geometry.forEachSegment((start, end) => {
        let dx = end[0] - start[0], dy = end[1] - start[1];
        let sense = dx > 0 ? 1 : -1;
        let label = options.labelFunc(feature, start, end);

        let len = Math.sqrt(dx*dx + dy*dy);
        let perpDX = perpLen * -dy / len, perpDY = perpLen * dx / len;

        let perpGeom = new ol.geom.MultiLineString([
          [
            [start[0] - perpDX, start[1] - perpDY],
            [start[0] + perpDX, start[1] + perpDY],
          ],
          [
            [end[0] - perpDX, end[1] - perpDY],
            [end[0] + perpDX, end[1] + perpDY],
          ],
        ]);

        styles = styles.concat([
          new ol.style.Style({
            geometry: perpGeom,
            stroke: innerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: perpGeom,
            stroke: outerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([
                0.5 * (end[0] + start[0]) + sense * 0.5 * perpDX,
                0.5 * (end[1] + start[1]) + sense * 0.5 * perpDY,
            ]),
            text: new ol.style.Text({
              text: label,
              rotation: Math.atan2(-sense * dy, Math.abs(dx)),
              offsetX: 0, offsetY: 0,
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
        ]);
      });
    }

    return styles;
  };
}

// Much like linearMeasurementStyle but has two labels.
export function circularMeasurementStyle(options) {
  options = Object.assign({
    innerColor: defaultInnerColor,
    outerColor: defaultOuterColor,
    aboveLabelFunc: () => '',
    belowLabelFunc: () => '',
    textShadowSize: 8,
  }, options);

  return (feature, resolution) => {
    // resolution is "projection units per pixel"
    let perpPixLen = defaultPerpPixLen, perpLen = resolution * perpPixLen;

    let innerStrokeStyle = new ol.style.Stroke({
      color: options.innerColor, width: 2 });
    let outerStrokeStyle = new ol.style.Stroke({
      color: options.outerColor, width: 4 });

    let styles = [ ];

    let geometry = feature.getGeometry();
    let label = '';

    if(geometry.getType() === 'LineString') {
      geometry.forEachSegment((start, end) => {
        let dx = end[0] - start[0], dy = end[1] - start[1];
        let sense = dx > 0 ? 1 : -1;

        let len = Math.sqrt(dx*dx + dy*dy);
        let cx = 0.5 * (end[0] + start[0]), cy = 0.5 * (end[1] + start[1]);

        let circleCentre = new ol.geom.Point([cx, cy]);
        let diameterGeom = new ol.geom.LineString([
          [ cx - 0.5*len, cy ], [ cx + 0.5*len, cy ]
        ]);
        let circleGeom = new ol.geom.Circle([cx, cy], 0.5*len);

        styles = styles.concat([
          new ol.style.Style({
            geometry: circleGeom,
            stroke: outerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: circleGeom,
            stroke: innerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: diameterGeom,
            stroke: outerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: diameterGeom,
            stroke: innerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([cx, cy + 0.5 * perpLen]),
            text: new ol.style.Text({
              text: options.aboveLabelFunc(feature, start, end),
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([cx, cy - 0.5 * perpLen]),
            text: new ol.style.Text({
              text: options.belowLabelFunc(feature, start, end),
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
        ]);
      });
    }

    return styles;
  };
}

// Construct a neutral background image source for maps.
export function createNeutralBackgroundSource() {
  return new ol.source.ImageCanvas({
    canvasFunction(extent, resolution, pxRato, imSize, proj) {
      let w = imSize[0], h = imSize[1];
      let canvasElem = document.createElement('canvas');
      canvasElem.width = w; canvasElem.height = h;
      let ctx = canvasElem.getContext('2d');
      ctx.fillStyle = '#bbb';
      ctx.fillRect(0, 0, w, h);
      return canvasElem;
    },
  });
}
<commit_msg>make sure to require openlayers in map-utils.js<commit_after>import ol from 'openlayers';

//const defaultOuterColor = 'rgba(253, 246, 227, 0.66)';
const defaultOuterColor = '#fdf6e3';
const defaultInnerColor = '#ffffff';
const defaultPerpPixLen = 20;

// Construct style function for a linear measurement.
//
// If the feature has a length, it is used as a label.
//
// Options may have the following properies:
//
//  innerColor: inner colour of line
//  outerColor: outer colour of line
//  lengthUnit: unit used for displaying length
//  labelFunc: function called with feature, start and end point. Returns label.
//
export function linearMeasurementStyle(options) {
  options = Object.assign({
    innerColor: defaultInnerColor,
    outerColor: defaultOuterColor,
    labelFunc: () => '',
    textShadowSize: 8,
  }, options);

  return (feature, resolution) => {
    // resolution is "projection units per pixel"
    let perpPixLen = defaultPerpPixLen, perpLen = resolution * perpPixLen;

    let outerStrokeStyle = new ol.style.Stroke({
      color: options.innerColor, width: 2 });
    let innerStrokeStyle = new ol.style.Stroke({
      color: options.outerColor, width: 4 });

    let styles = [
      new ol.style.Style({ stroke: outerStrokeStyle, zIndex: 100 }),
      new ol.style.Style({ stroke: innerStrokeStyle, zIndex: 90 }),
    ];

    let geometry = feature.getGeometry();

    if(geometry.getType() === 'LineString') {
      geometry.forEachSegment((start, end) => {
        let dx = end[0] - start[0], dy = end[1] - start[1];
        let sense = dx > 0 ? 1 : -1;
        let label = options.labelFunc(feature, start, end);

        let len = Math.sqrt(dx*dx + dy*dy);
        let perpDX = perpLen * -dy / len, perpDY = perpLen * dx / len;

        let perpGeom = new ol.geom.MultiLineString([
          [
            [start[0] - perpDX, start[1] - perpDY],
            [start[0] + perpDX, start[1] + perpDY],
          ],
          [
            [end[0] - perpDX, end[1] - perpDY],
            [end[0] + perpDX, end[1] + perpDY],
          ],
        ]);

        styles = styles.concat([
          new ol.style.Style({
            geometry: perpGeom,
            stroke: innerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: perpGeom,
            stroke: outerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([
                0.5 * (end[0] + start[0]) + sense * 0.5 * perpDX,
                0.5 * (end[1] + start[1]) + sense * 0.5 * perpDY,
            ]),
            text: new ol.style.Text({
              text: label,
              rotation: Math.atan2(-sense * dy, Math.abs(dx)),
              offsetX: 0, offsetY: 0,
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
        ]);
      });
    }

    return styles;
  };
}

// Much like linearMeasurementStyle but has two labels.
export function circularMeasurementStyle(options) {
  options = Object.assign({
    innerColor: defaultInnerColor,
    outerColor: defaultOuterColor,
    aboveLabelFunc: () => '',
    belowLabelFunc: () => '',
    textShadowSize: 8,
  }, options);

  return (feature, resolution) => {
    // resolution is "projection units per pixel"
    let perpPixLen = defaultPerpPixLen, perpLen = resolution * perpPixLen;

    let innerStrokeStyle = new ol.style.Stroke({
      color: options.innerColor, width: 2 });
    let outerStrokeStyle = new ol.style.Stroke({
      color: options.outerColor, width: 4 });

    let styles = [ ];

    let geometry = feature.getGeometry();
    let label = '';

    if(geometry.getType() === 'LineString') {
      geometry.forEachSegment((start, end) => {
        let dx = end[0] - start[0], dy = end[1] - start[1];
        let sense = dx > 0 ? 1 : -1;

        let len = Math.sqrt(dx*dx + dy*dy);
        let cx = 0.5 * (end[0] + start[0]), cy = 0.5 * (end[1] + start[1]);

        let circleCentre = new ol.geom.Point([cx, cy]);
        let diameterGeom = new ol.geom.LineString([
          [ cx - 0.5*len, cy ], [ cx + 0.5*len, cy ]
        ]);
        let circleGeom = new ol.geom.Circle([cx, cy], 0.5*len);

        styles = styles.concat([
          new ol.style.Style({
            geometry: circleGeom,
            stroke: outerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: circleGeom,
            stroke: innerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: diameterGeom,
            stroke: outerStrokeStyle,
            zIndex: 90,
          }),
          new ol.style.Style({
            geometry: diameterGeom,
            stroke: innerStrokeStyle,
            zIndex: 100,
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([cx, cy + 0.5 * perpLen]),
            text: new ol.style.Text({
              text: options.aboveLabelFunc(feature, start, end),
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
          new ol.style.Style({
            geometry: new ol.geom.Point([cx, cy - 0.5 * perpLen]),
            text: new ol.style.Text({
              text: options.belowLabelFunc(feature, start, end),
              font: '15px sans-serif',
              fill: new ol.style.Fill({ color: options.innerColor }),
              stroke: new ol.style.Stroke({
                color: options.outerColor, width: options.textShadowSize }),
            }),
          }),
        ]);
      });
    }

    return styles;
  };
}

// Construct a neutral background image source for maps.
export function createNeutralBackgroundSource() {
  return new ol.source.ImageCanvas({
    canvasFunction(extent, resolution, pxRato, imSize, proj) {
      let w = imSize[0], h = imSize[1];
      let canvasElem = document.createElement('canvas');
      canvasElem.width = w; canvasElem.height = h;
      let ctx = canvasElem.getContext('2d');
      ctx.fillStyle = '#bbb';
      ctx.fillRect(0, 0, w, h);
      return canvasElem;
    },
  });
}
<|endoftext|>


<commit_before>import glob from '../global'
import { deselect } from '../node/index'

/**
 * @name image
 * @param {requestCallback} cb - The callback.
 * @param {string} [type] - Type of image, default png, others : jpeg, gif..
 * @param {string} [bg] - Color of mind map background.
 * @desc Set image settings and pass its data URL in a callback function.
*/
export function image( cb, type, bg ) {
    deselect()
    dataURI( uri => {
        let image = new Image()
        image.src = uri
        image.onload = function() {
            let canvas = document.createElement('canvas'),
                context = canvas.getContext('2d')

            canvas.width = image.width
            canvas.height = image.height
            context.drawImage( image, 0, 0 )

            context.globalCompositeOperation = 'destination-over'
            context.fillStyle = bg || '#ffffff'
            context.fillRect( 0, 0, canvas.width, canvas.height )

            if ( typeof type === 'string' ) type = 'image/' + type
            cb( canvas.toDataURL( type ) )
        }
    })
}

/**
 * @name dataURI
 * @param {requestCallback} cb - A callback with uri as parameter
 * @desc Translate the mind map svg in data URI.
*/
function dataURI( cb ) {
    let element = glob.svg.mmp.node(),
        clone = element.cloneNode( true ),
        svg = document.createElementNS('http://www.w3.org/2000/svg','svg'),
        box = element.getBBox(),
        css = cssRules( element )
    const
        xmlns = "http://www.w3.org/2000/xmlns/", padding = 15,
        x = box.x - padding, y = box.y - padding,
        w = box.width + padding*2, h = box.height + padding*2

    svg.setAttributeNS( xmlns, "xmlns", "http://www.w3.org/2000/svg")
    svg.setAttributeNS( xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink")
    svg.setAttribute("version", "1.1")
    svg.setAttribute("width", w )
    svg.setAttribute("height", h )
    svg.setAttribute("viewBox", [ x, y, w, h ].join(" ") )

    // If there is css, insert it
    if ( css !== '' ) {
        let style = document.createElement('style'),
            defs = document.createElement('defs')

        style.setAttribute('type', 'text/css')
        style.innerHTML = "<![CDATA[\n" + css + "\n]]>"
        defs.appendChild( style )
        svg.appendChild( defs )
    }

    clone.setAttribute('transform', 'translate(0,0)')
    svg.appendChild( clone )

    checkImages( clone, function() {
        let uri = window.btoa( reEncode( svg.outerHTML ) )
        cb( 'data:image/svg+xml;base64,' + uri )
    })
}

/**
 * @name cssRules
 * @param {Object} element - A dom element.
 * @return {string} css - Css rules.
 * @desc Return the css rules of an element.
*/
function cssRules( element ) {
    let css = "", sheets = document.styleSheets
    for ( let i = 0; i < sheets.length; i++ ) {
        let rules = sheets[i].cssRules
        for ( let j = 0; j < rules.length; j++ ) {
            let rule = rules[j], fontFace = rule.cssText.match(/^@font-face/)
            if ( element.querySelector( rule.selectorText ) || fontFace )
                css += rule.cssText
        }
    }
    return css
}

/**
 * @name reEncode
 * @param {string} data - Original data.
 * @return {string} data - Data encoded.
 * @desc Encode data.
*/
function reEncode( data ) {
    data = encodeURIComponent( data )
    data = data.replace( /%([0-9A-F]{2})/g, ( match, p1 ) => {
        const c = String.fromCharCode('0x'+p1)
        return c === '%' ? '%25' : c
    })
    return decodeURIComponent( data )
}

/**
 * @name checkImages
 * @param {Object} element - The DOM element to check.
 * @return {requestCallback} cb - A callback to execute after check.
 * @desc If there are images in the map convert their href in dataURL.
*/
function checkImages( element, cb ) {
    let images = element.querySelectorAll('image'),
        l = images.length, counter = l

    if ( l > 0 ) for ( let image of images ) {

        let canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            img = new Image(),
            href = image.getAttribute('href')

        img.crossOrigin = "Anonymous"
        img.src = href
        img.onload = function() {
            canvas.width = this.width
            canvas.height = this.height
            ctx.drawImage( this, 0, 0 )
            image.setAttribute("href", canvas.toDataURL('image/png') )
            counter--
            if ( counter === 0 ) cb()
        }
        img.onerror = function() {
            counter--
            if ( counter === 0 ) cb()
        }

    } else cb()
}
<commit_msg>[fix] bug in function to create image of map<commit_after>import glob from '../global'
import { deselect } from '../node/index'

/**
 * @name image
 * @param {requestCallback} cb - The callback.
 * @param {string} [type] - Type of image, default png, others : jpeg, gif..
 * @param {string} [bg] - Color of mind map background.
 * @desc Set image settings and pass its data URL in a callback function.
*/
export function image( cb, type, bg ) {
    deselect()
    dataURI( uri => {
        let image = new Image()
        image.src = uri
        image.onload = function() {
            let canvas = document.createElement('canvas'),
                context = canvas.getContext('2d')

            canvas.width = image.width
            canvas.height = image.height
            context.drawImage( image, 0, 0 )

            context.globalCompositeOperation = 'destination-over'
            context.fillStyle = bg || '#ffffff'
            context.fillRect( 0, 0, canvas.width, canvas.height )

            if ( typeof type === 'string' ) type = 'image/' + type
            cb( canvas.toDataURL( type ) )
        }
    })
}

/**
 * @name dataURI
 * @param {requestCallback} cb - A callback with uri as parameter
 * @desc Translate the mind map svg in data URI.
*/
function dataURI( cb ) {
    let element = glob.svg.mmp.node(),
        clone = element.cloneNode( true ),
        svg = document.createElementNS('http://www.w3.org/2000/svg','svg'),
        box = element.getBBox(),
        css = cssRules( element )
    const
        xmlns = "http://www.w3.org/2000/xmlns/", padding = 15,
        x = box.x - padding, y = box.y - padding,
        w = box.width + padding*2, h = box.height + padding*2

    svg.setAttributeNS( xmlns, "xmlns", "http://www.w3.org/2000/svg")
    svg.setAttributeNS( xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink")
    svg.setAttribute("version", "1.1")
    svg.setAttribute("width", w )
    svg.setAttribute("height", h )
    svg.setAttribute("viewBox", [ x, y, w, h ].join(" ") )

    // If there is css, insert it
    if ( css !== '' ) {
        let style = document.createElement('style'),
            defs = document.createElement('defs')

        style.setAttribute('type', 'text/css')
        style.innerHTML = "<![CDATA[\n" + css + "\n]]>"
        defs.appendChild( style )
        svg.appendChild( defs )
    }

    clone.setAttribute('transform', 'translate(0,0)')
    svg.appendChild( clone )

    checkImages( clone, function() {
        let uri = window.btoa( reEncode( svg.outerHTML ) )
        cb( 'data:image/svg+xml;base64,' + uri )
    })
}

/**
 * @name cssRules
 * @param {Object} element - A dom element.
 * @return {string} css - Css rules.
 * @desc Return the css rules of an element.
*/
function cssRules( element ) {
    let css = "", sheets = document.styleSheets
    for ( let i = 0; i < sheets.length; i++ ) {
        let rules = sheets[i].cssRules
        if ( rules ) {
            for ( let j = 0; j < rules.length; j++ ) {
                let rule = rules[j], fontFace = rule.cssText.match(/^@font-face/)
                if ( element.querySelector( rule.selectorText ) || fontFace )
                    css += rule.cssText
            }
        }
    }
    return css
}

/**
 * @name reEncode
 * @param {string} data - Original data.
 * @return {string} data - Data encoded.
 * @desc Encode data.
*/
function reEncode( data ) {
    data = encodeURIComponent( data )
    data = data.replace( /%([0-9A-F]{2})/g, ( match, p1 ) => {
        const c = String.fromCharCode('0x'+p1)
        return c === '%' ? '%25' : c
    })
    return decodeURIComponent( data )
}

/**
 * @name checkImages
 * @param {Object} element - The DOM element to check.
 * @return {requestCallback} cb - A callback to execute after check.
 * @desc If there are images in the map convert their href in dataURL.
*/
function checkImages( element, cb ) {
    let images = element.querySelectorAll('image'),
        l = images.length, counter = l

    if ( l > 0 ) for ( let image of images ) {

        let canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            img = new Image(),
            href = image.getAttribute('href')

        img.crossOrigin = "Anonymous"
        img.src = href
        img.onload = function() {
            canvas.width = this.width
            canvas.height = this.height
            ctx.drawImage( this, 0, 0 )
            image.setAttribute("href", canvas.toDataURL('image/png') )
            counter--
            if ( counter === 0 ) cb()
        }
        img.onerror = function() {
            counter--
            if ( counter === 0 ) cb()
        }

    } else cb()
}
<|endoftext|>


<commit_before>import React from 'react'
import {Autocomplete} from 'pui-react-autocomplete'
import {Iconography} from 'pui-react-iconography'
import {Collapse} from 'pui-react-collapse'

const styleItems = [
  {text: 'Icons', link: 'icons'},
  {text: 'Colors', link: 'colors'},
  {text: 'Grid', link: 'grid'},
]

const componentItems = [
  {text: 'Alerts', link: 'alerts'},
  {text: 'Alignment', link: 'alignment'},
  {text: 'Autocomplete', link: 'autocomplete'},
  {text: 'Buttons', link: 'buttons'},
  {text: 'Collapse', link: 'collapse'},
  {text: 'Copy To Clipboard', link: 'copy_to_clipboard'},
  {text: 'Dividers', link: 'dividers'},
  {text: 'Dropdowns', link: 'dropdowns'},
  {text: 'Ellipsis', link: 'ellipsis'},
  {text: 'Expander', link: 'expander'},
  {text: 'Forms', link: 'forms'},
  {text: 'Grids', link: 'grids'},
  {text: 'Hoverable', link: 'hoverable'},
  {text: 'Images', link: 'images'},
  {text: 'Labels', link: 'labels'},
  {text: 'Lists', link: 'lists'},
  {text: 'Media', link: 'media'},
  {text: 'Modals', link: 'modals'},
  {text: 'Notifications', link: 'notifications'},
  {text: 'Pagination', link: 'pagination'},
  {text: 'Panels', link: 'panels'},
  {text: 'Panes', link: 'panes'},
  {text: 'Portals', link: 'portals'},
  {text: 'Select', link: 'select'},
  {text: 'Svg', link: 'svg'},
  {text: 'Tables', link: 'tables'},
  {text: 'Tabs', link: 'tabs'},
  {text: 'Tooltips', link: 'tooltips'},
]

const searchItems = componentItems.concat(styleItems)

const ContentLink = ({onClick, link, text}) => {text}

export default class Sidebar extends React.PureComponent {
  handleClick(event) {
    event.preventDefault()
    this.props.updateContent(event.target.href)
  }

  handlePick(event) {
    const link = searchItems.find(i => i.text == event.value).link;
    this.props.updateContent(link)
  }

  render() {
    const onInitializeItems = callback => callback(searchItems.map(item => item.text))
    const SearchBar = () => <Autocomplete onInitializeItems={onInitializeItems}
                                          placeholder="Search"
                                          onPick={this.handlePick.bind(this)}/>

    const styles = styleItems
      .map((style, i) => <ContentLink key={i}
                                      onClick={this.handleClick.bind(this)}
                                      link={style.link}
                                      text={style.text}/>)

    const components = componentItems
      .map((component, i) => <ContentLink key={i}
                                          onClick={this.handleClick.bind(this)}
                                          link={component.link}
                                          text={component.text}/>)

    return

        <Icon className="sidebar--icon" src="pivotal_ui_white"/>

Pivotal UI

      <SearchBar/>

        <ContentLink onClick={this.handleClick.bind(this)} link="getstarted" text="Get Started"/>
        <Collapse className="sidebar--item" header="Styles">
          {styles}
        </Collapse>
        <Collapse className="sidebar--item" header="Components">
          {components}
        </Collapse>
        <ContentLink onClick={this.handleClick.bind(this)} link="upgradeguide" text="Upgrade Guide"/>
        <ContentLink onClick={this.handleClick.bind(this)} link="contribute" text="Contribute"/>
        <ContentLink onClick={this.handleClick.bind(this)} link="downloads" text="Downloads"/>
        Github

  }
}<commit_msg>Fix github link<commit_after>import React from 'react'
import {Autocomplete} from 'pui-react-autocomplete'
import {Iconography} from 'pui-react-iconography'
import {Collapse} from 'pui-react-collapse'

const styleItems = [
  {text: 'Icons', link: 'icons'},
  {text: 'Colors', link: 'colors'},
  {text: 'Grid', link: 'grid'},
]

const componentItems = [
  {text: 'Alerts', link: 'alerts'},
  {text: 'Alignment', link: 'alignment'},
  {text: 'Autocomplete', link: 'autocomplete'},
  {text: 'Buttons', link: 'buttons'},
  {text: 'Collapse', link: 'collapse'},
  {text: 'Copy To Clipboard', link: 'copy_to_clipboard'},
  {text: 'Dividers', link: 'dividers'},
  {text: 'Dropdowns', link: 'dropdowns'},
  {text: 'Ellipsis', link: 'ellipsis'},
  {text: 'Expander', link: 'expander'},
  {text: 'Forms', link: 'forms'},
  {text: 'Grids', link: 'grids'},
  {text: 'Hoverable', link: 'hoverable'},
  {text: 'Images', link: 'images'},
  {text: 'Labels', link: 'labels'},
  {text: 'Lists', link: 'lists'},
  {text: 'Media', link: 'media'},
  {text: 'Modals', link: 'modals'},
  {text: 'Notifications', link: 'notifications'},
  {text: 'Pagination', link: 'pagination'},
  {text: 'Panels', link: 'panels'},
  {text: 'Panes', link: 'panes'},
  {text: 'Portals', link: 'portals'},
  {text: 'Select', link: 'select'},
  {text: 'Svg', link: 'svg'},
  {text: 'Tables', link: 'tables'},
  {text: 'Tabs', link: 'tabs'},
  {text: 'Tooltips', link: 'tooltips'},
]

const searchItems = componentItems.concat(styleItems)

const ContentLink = ({onClick, link, text}) => {text}

export default class Sidebar extends React.PureComponent {
  handleClick(event) {
    event.preventDefault()
    this.props.updateContent(event.target.href)
  }

  handlePick(event) {
    const link = searchItems.find(i => i.text == event.value).link;
    this.props.updateContent(link)
  }

  render() {
    const onInitializeItems = callback => callback(searchItems.map(item => item.text))
    const SearchBar = () => <Autocomplete onInitializeItems={onInitializeItems}
                                          placeholder="Search"
                                          onPick={this.handlePick.bind(this)}/>

    const styles = styleItems
      .map((style, i) => <ContentLink key={i}
                                      onClick={this.handleClick.bind(this)}
                                      link={style.link}
                                      text={style.text}/>)

    const components = componentItems
      .map((component, i) => <ContentLink key={i}
                                          onClick={this.handleClick.bind(this)}
                                          link={component.link}
                                          text={component.text}/>)

    return

        <Icon className="sidebar--icon" src="pivotal_ui_white"/>

Pivotal UI

      <SearchBar/>

        <ContentLink onClick={this.handleClick.bind(this)} link="getstarted" text="Get Started"/>
        <Collapse className="sidebar--item" header="Styles">
          {styles}
        </Collapse>
        <Collapse className="sidebar--item" header="Components">
          {components}
        </Collapse>
        <ContentLink onClick={this.handleClick.bind(this)} link="upgradeguide" text="Upgrade Guide"/>
        <ContentLink onClick={this.handleClick.bind(this)} link="contribute" text="Contribute"/>
        <ContentLink onClick={this.handleClick.bind(this)} link="downloads" text="Downloads"/>
        Github

  }
}<|endoftext|>


<commit_before>'use strict';
// define attributes that are not supposed to be transferred
// const attributesExceptions = [
//     'name',
//     'label',
//     'tabindex',
//     'placeholder',
//     'autofocus',
//     'autocomplete',
//     'autovalidate'
// ];
class MaterialInput extends HTMLElement {

    constructor() {
        super(); // always call super() first in the ctor. This also calls the extended class' ctor.
    }

    createdCallback() {
        var value = '';
        // set value of material-input
        Object.defineProperty( this, "value", {
            configurable: true,
            enumerable: true,
            get: function(){
                return value;
            },
            set: function(newValue){
                value = !newValue ? '' : newValue;
                this._value(value);
            }
        });

        this.createShadowRoot().innerHTML = `

                <input class="material-input__input" tabindex="-1" />

        `;
        this.attributesExceptions = [
            'name',
            'label',
            'tabindex',
            'placeholder',
            'autofocus',
            'autocomplete',
            'autovalidate'
        ];
        // set tab index to make element focussable
        this.setAttribute('tabindex',0);
        // shim shadowDOM styling
        if(WebComponents !== undefined && WebComponents.flags.shadow === true){
            WebComponents.ShadowCSS.shimStyling( this.shadowRoot, 'material-input' )
        }
        // add hidden input
        this.insertAdjacentHTML('afterend','<input tabindex="-1" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: '+(this.offsetTop + this.offsetHeight)+'px; left: '+this.offsetLeft+'px;" name="'+this.getAttribute('name')+'"/>');
        this.$hiddenInput = document.querySelector('input[name='+this.getAttribute('name')+']');
        // elements
        this.$container = this.shadowRoot.querySelector('.material-input__container');
        this.$input = this.$container.querySelector('.material-input__input');
        this.$label = this.$container.querySelector('.material-input__label');
        this.$message = this.$container.querySelector('.material-input__message');
        // add events
        this._addEvents();
        // transfer attribtues to input & hiddenInput
        this._transferAttributes();
        // set value, label, etc.
        this._setValue(this.getAttribute('value'));
        this._setLabel(this.getAttribute('label'));
        this._setPlaceholder(this.getAttribute('placeholder'));
        this._setValid();
        // remove no-animation loading class
        setTimeout(function(){
            this.$container.classList.remove('no-animation');
        }.bind(this),100);
    }
    /**
     * when an attribute is changed
     */
    attributeChangedCallback(attrName, oldVal, newVal) {
        // define callbacks
        var callbacks = {
            'valid': this._setValid,
            'value': this._setValue,
            'label': this._setLabel,
            'placeholder': this._setPlaceholder
        };
        // call callback if it exists
        if(callbacks.hasOwnProperty(attrName)) {
            callbacks[attrName].call(this, newVal, oldVal);
        }
        else{
            // if other attributes are updated, transfer updates to hidden input field
            this._transferAttribute(attrName, newVal, this.attributesExceptions);
        }
    }
    /**
     * set the custom validity of the input
     */
    setCustomValidity(msg){
        this.$input.setCustomValidity(msg);
        this.$hiddenInput.setCustomValidity(msg)
    }
    /**
     * add events for all items
     */
    _addEvents(){
        // on focuse pass to input
        this.addEventListener('focus', function(){
            this.$input.focus();
        });
        // set validation status when hiddenInput is invalid
        this.$hiddenInput.addEventListener('invalid', function(){
            this._setValid(false);
        }.bind(this));
        // pass on value when user enters content
        this.$input.addEventListener('keydown', function(){
            this._value(this.$input.value);
        }.bind(this));
        // pass in value and validate when user exits input field
        this.$input.addEventListener('blur', function(){
            this._value(this.$input.value);
            // check if is valid
            if(this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false)){
                this._setValid(this.$input.validity.valid);
            }
        }.bind(this));
        // if autovalidate is set to true, validate on key event
        if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
            this.$input.addEventListener('keydown', function(){
                // check if is valid
                if(this.$input.value !== '' && (this.$input.validity.valid === true)){
                    this._setValid(true);
                }
            }.bind(this));
        }
    }
    /**
     * set value
     */
    _setValue(newValue){
        this.value = newValue;
    }
    /**
     * set field to valid or invalid
     */
    _setValid(validity = undefined){
        // valid is not set
        if(!this.hasAttribute('valid') ){
            this.valid = undefined;
            this.$container.classList.remove('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is true
        if(this.getAttribute('valid') === 'true' || this.getAttribute('valid') === true || validity === true || validity === 'true'){
            this.valid = true;
            this.$container.classList.add('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is false
        if(this.getAttribute('valid') === 'false' || this.getAttribute('valid') === false || validity === false || validity === 'false') {
            this.valid = false;
            this.$container.classList.add('invalid');
            this.$container.classList.remove('valid');
        }
    }
    /**
     * transfer attributes to input
     */
    _transferAttributes(){
        for(var key of Object.keys(this.attributes)){
            if (this.attributes.hasOwnProperty(key)) {
                this._transferAttribute(this.attributes[key].name, this.attributes[key].value, this.attributesExceptions);
            }
        }
    }
    /**
     * transfer attribute to input
     */
    _transferAttribute(attrName, val, attributesExceptions){
        if(attributesExceptions.indexOf(attrName) === -1){
            this.$hiddenInput.setAttribute(attrName,val);
            this.$input.setAttribute(attrName,val);
        }
    }
    /**
     * update value and toggle is-empty class
     */
    _value(val){
        // set value of hidden input for form submission
        this.$hiddenInput.value = val;
        this.$input.value = val;
        // set state depending on value
        this._toggle(this.$container, 'is-empty', val === '');
    }
    /**
     * add label to material-input
     */
    _setLabel(label){
        if(label !== undefined && label !== null){
            return this.$label.innerHTML = label;
        }
        this.$label.innerHTML = '';
    }
    /**
     * set placeholder and add label-always-floats class
     */
    _setPlaceholder(placeholder){
        if(placeholder !== null && placeholder !== undefined){
            this.$input.setAttribute('placeholder', placeholder);
            this.$container.classList.add('label-always-floats');
            return;
        }
        this.$input.removeAttribute('placeholder');
        this.$container.classList.remove('label-always-floats');
    }
    /**
     * since classList.toggle with a second param is not supported in IE11 and below
     */
    _toggle(el, cls, condition){
        if(condition === true){
            el.classList.add(cls);
        }else{
            el.classList.remove(cls);
        }
    }
}

document.registerElement('material-input', MaterialInput);
<commit_msg>bugfix<commit_after>'use strict';
// define attributes that are not supposed to be transferred
// const attributesExceptions = [
//     'name',
//     'label',
//     'tabindex',
//     'placeholder',
//     'autofocus',
//     'autocomplete',
//     'autovalidate'
// ];
class MaterialInput extends HTMLElement {

    constructor() {
        super(); // always call super() first in the ctor. This also calls the extended class' ctor.
    }

    createdCallback() {
        var value = '';
        // set value of material-input
        Object.defineProperty( this, "value", {
            configurable: true,
            enumerable: true,
            get: function(){
                return value;
            },
            set: function(newValue){
                value = !newValue ? '' : newValue;
                this._value(value);
            }
        });

        this.createShadowRoot().innerHTML = `

                <input class="material-input__input" tabindex="-1" />

        `;
        this.attributesExceptions = [
            'name',
            'label',
            'tabindex',
            'placeholder',
            'autofocus',
            'autocomplete',
            'autovalidate'
        ];
        // set tab index to make element focussable
        this.setAttribute('tabindex',0);
        // shim shadowDOM styling
        if(WebComponents !== undefined && WebComponents.flags.shadow === true){
            WebComponents.ShadowCSS.shimStyling( this.shadowRoot, 'material-input' )
        }
        // add hidden input
        this.insertAdjacentHTML('afterend','<input tabindex="-1" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: '+(this.offsetTop + this.offsetHeight)+'px; left: '+this.offsetLeft+'px;" name="'+this.getAttribute('name')+'"/>');
        this.$hiddenInput = document.querySelector('input[name='+this.getAttribute('name')+']');
        // elements
        this.$container = this.shadowRoot.querySelector('.material-input__container');
        this.$input = this.$container.querySelector('.material-input__input');
        this.$label = this.$container.querySelector('.material-input__label');
        this.$message = this.$container.querySelector('.material-input__message');
        // add events
        this._addEvents();
        // transfer attribtues to input & hiddenInput
        this._transferAttributes();
        // set value, label, etc.
        this._setValue(this.getAttribute('value'));
        this.$input.value = this.value;
        this._setLabel(this.getAttribute('label'));
        this._setPlaceholder(this.getAttribute('placeholder'));
        this._setValid();
        // remove no-animation loading class
        setTimeout(function(){
            this.$container.classList.remove('no-animation');
        }.bind(this),100);
    }
    /**
     * when an attribute is changed
     */
    attributeChangedCallback(attrName, oldVal, newVal) {
        // define callbacks
        var callbacks = {
            'valid': this._setValid,
            'value': this._setValue,
            'label': this._setLabel,
            'placeholder': this._setPlaceholder
        };
        // call callback if it exists
        if(callbacks.hasOwnProperty(attrName)) {
            callbacks[attrName].call(this, newVal, oldVal);
        }
        else{
            // if other attributes are updated, transfer updates to hidden input field
            this._transferAttribute(attrName, newVal, this.attributesExceptions);
        }
    }
    /**
     * set the custom validity of the input
     */
    setCustomValidity(msg){
        this.$input.setCustomValidity(msg);
        this.$hiddenInput.setCustomValidity(msg)
    }
    /**
     * add events for all items
     */
    _addEvents(){
        // on focuse pass to input
        this.addEventListener('focus', function(){
            this.$input.focus();
        });
        // set validation status when hiddenInput is invalid
        this.$hiddenInput.addEventListener('invalid', function(){
            this._setValid(false);
        }.bind(this));
        // pass on value when user enters content
        this.$input.addEventListener('keydown', function(){
            this._value(this.$input.value);
        }.bind(this));
        // pass in value and validate when user exits input field
        this.$input.addEventListener('blur', function(){
            this._value(this.$input.value);
            // check if is valid
            if(this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false)){
                this._setValid(this.$input.validity.valid);
            }
        }.bind(this));
        // if autovalidate is set to true, validate on key event
        if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){
            this.$input.addEventListener('keydown', function(){
                // check if is valid
                if(this.$input.value !== '' && (this.$input.validity.valid === true)){
                    this._setValid(true);
                }
            }.bind(this));
        }
    }
    /**
     * set value
     */
    _setValue(newValue){
        this.value = newValue;
    }
    /**
     * set field to valid or invalid
     */
    _setValid(validity = undefined){
        // valid is not set
        if(!this.hasAttribute('valid') ){
            this.valid = undefined;
            this.$container.classList.remove('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is true
        if(this.getAttribute('valid') === 'true' || this.getAttribute('valid') === true || validity === true || validity === 'true'){
            this.valid = true;
            this.$container.classList.add('valid');
            this.$container.classList.remove('invalid');
        }
        // valid is false
        if(this.getAttribute('valid') === 'false' || this.getAttribute('valid') === false || validity === false || validity === 'false') {
            this.valid = false;
            this.$container.classList.add('invalid');
            this.$container.classList.remove('valid');
        }
    }
    /**
     * transfer attributes to input
     */
    _transferAttributes(){
        for(var key of Object.keys(this.attributes)){
            if (this.attributes.hasOwnProperty(key)) {
                this._transferAttribute(this.attributes[key].name, this.attributes[key].value, this.attributesExceptions);
            }
        }
    }
    /**
     * transfer attribute to input
     */
    _transferAttribute(attrName, val, attributesExceptions){
        if(attributesExceptions.indexOf(attrName) === -1){
            this.$hiddenInput.setAttribute(attrName,val);
            this.$input.setAttribute(attrName,val);
        }
    }
    /**
     * update value and toggle is-empty class
     */
    _value(val){
        // set value of hidden input for form submission
        this.$hiddenInput.value = val;
        // set state depending on value
        this._toggle(this.$container, 'is-empty', val === '');
    }
    /**
     * add label to material-input
     */
    _setLabel(label){
        if(label !== undefined && label !== null){
            return this.$label.innerHTML = label;
        }
        this.$label.innerHTML = '';
    }
    /**
     * set placeholder and add label-always-floats class
     */
    _setPlaceholder(placeholder){
        if(placeholder !== null && placeholder !== undefined){
            this.$input.setAttribute('placeholder', placeholder);
            this.$container.classList.add('label-always-floats');
            return;
        }
        this.$input.removeAttribute('placeholder');
        this.$container.classList.remove('label-always-floats');
    }
    /**
     * since classList.toggle with a second param is not supported in IE11 and below
     */
    _toggle(el, cls, condition){
        if(condition === true){
            el.classList.add(cls);
        }else{
            el.classList.remove(cls);
        }
    }
}

document.registerElement('material-input', MaterialInput);
<|endoftext|>


<commit_before>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * 'Help' menu plugin
 * This creates the 'Help' menu.
 */
define(function(require, exports, module) {
  'use strict';
  var menuItems = [
    {
      name: "Tutorial",
      sortorder: 100,
      action: function() {
        bkHelper.openWindow(bkHelper.getBaseUrl() + '/open?uri=config/tutorial.bkr');
      },
      tooltip: "Open the tutorial notebook"
    },
    {
      name: "About Beaker",
      sortorder: 120,
      action: function() {
        bkHelper.showModalDialog(undefined, "app/template/about.html");
      },
      tooltip: "Basic information about this application"
    },
    {
      name: "Keyboard shortcuts",
      sortorder: 110,
      action: function() {
        bkHelper.openStaticWindow('keyboardshortcuts.html');
      },
      tooltip: "Show keyboard shortcuts"
    },
    {
      name: "Report a bug or feature request",
      sortorder: 130,
      action: function() {
        if (bkHelper.isElectron) {
          var Shell = bkHelper.Electron.Shell;
          Shell.openExternal("https://github.com/twosigma/beaker-notebook/issues/new?title=\[Native Beaker\]");
        } else {
          window.open("https://github.com/twosigma/beaker-notebook/issues/new");
        }
      },
      tooltip: "Log an issue in GitHub"
    }
  ];
  var menuItemPromise = bkHelper.newPromise({
    parent: "Help",
    id: "help-menu",
    items: menuItems
  });
  exports.getMenuItems = function() {
    return menuItemPromise;
  };
});
<commit_msg>Refactor report bug action<commit_after>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * 'Help' menu plugin
 * This creates the 'Help' menu.
 */
define(function(require, exports, module) {
  'use strict';
  var menuItems = [
    {
      name: "Tutorial",
      sortorder: 100,
      action: function() {
        bkHelper.openWindow(bkHelper.getBaseUrl() + '/open?uri=config/tutorial.bkr');
      },
      tooltip: "Open the tutorial notebook"
    },
    {
      name: "About Beaker",
      sortorder: 120,
      action: function() {
        bkHelper.showModalDialog(undefined, "app/template/about.html");
      },
      tooltip: "Basic information about this application"
    },
    {
      name: "Keyboard shortcuts",
      sortorder: 110,
      action: function() {
        bkHelper.openStaticWindow('keyboardshortcuts.html');
      },
      tooltip: "Show keyboard shortcuts"
    },
    {
      name: "Report a bug or feature request",
      sortorder: 130,
      action: function() {
        var url = 'https://github.com/twosigma/beaker-notebook/issues/new';
        if (bkHelper.isElectron) {
          var Shell = bkHelper.Electron.Shell;
          Shell.openExternal(url + '?title=\[Native Beaker\]');
        } else {
          window.open(url);
        }
      },
      tooltip: "Log an issue in GitHub"
    }
  ];
  var menuItemPromise = bkHelper.newPromise({
    parent: "Help",
    id: "help-menu",
    items: menuItems
  });
  exports.getMenuItems = function() {
    return menuItemPromise;
  };
});
<|endoftext|>


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

const parseInput = (input, config) => new Promise((resolve, reject) => {

    let parser = () => null;

    if (!config.parser) {

        parser = require('doxdox-parser-dox');

    }

    fs.stat(input, (err, stats) => {

        if (err) {

            return reject(err);

        }

        if (stats.isFile()) {

            fs.readFile(input, 'utf8', (err, data) => {

                if (err) {

                    return reject(err);

                }

                return resolve({
                    'methods': parser(data, {'file': input}),
                    'name': input.replace(process.cwd(), '')
                });

            });

        }

        return false;

    });

});

const parseInputs = (inputs, config) => new Promise(resolve => {

    let layout = () => null;

    if (!config.layout || config.layout === 'markdown') {

        layout = require('doxdox-plugin-markdown');

    }

    if (inputs.length) {

        Promise.all(inputs.map(input => parseInput(input, config)))
            .then(files => layout(Object.assign({
                files
            }, config)))
            .then(resolve);

    }

});

module.exports = {
    parseInputs
};
<commit_msg>Changed what data gets sent to parser.<commit_after>const fs = require('fs');

const parseInput = (input, config) => new Promise((resolve, reject) => {

    let parser = () => null;

    if (!config.parser) {

        parser = require('doxdox-parser-dox');

    }

    fs.stat(input, (err, stats) => {

        if (err) {

            return reject(err);

        }

        if (stats.isFile()) {

            fs.readFile(input, 'utf8', (err, data) => {

                if (err) {

                    return reject(err);

                }

                return resolve({
                    'methods': parser(data, input),
                    'name': input.replace(process.cwd(), '')
                });

            });

        }

        return false;

    });

});

const parseInputs = (inputs, config) => new Promise(resolve => {

    let layout = () => null;

    if (!config.layout || config.layout === 'markdown') {

        layout = require('doxdox-plugin-markdown');

    }

    if (inputs.length) {

        Promise.all(inputs.map(input => parseInput(input, config)))
            .then(files => layout(Object.assign({
                files
            }, config)))
            .then(resolve);

    }

});

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


<commit_before>var path = require('path');

// ---

var providers = require(path.join(__dirname, 'providers.js'));

// ---

function status(provider, name, node, manifest) {
	provider.status(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		console.log(name + ':', state);
	});
}

function boot(provider, name, node, manifest) {
	provider.boot(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		exports.status(provider, name, node, manifest);
	});
}

function halt(provier, name, node, manifest) {
	provider.halt(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		exports.status(provider, name, node, manifest);
	});
}

// ---

function launch(manifest, plugins, provider, action) {
	(plugins || []).forEach(function (plugin) {
		plugin.vortex(manifest, provider, action);
	});

	if (!(action in exports) || action == 'launch') {
		throw new Error('action ' + action + ' not recognized');
	}

	if (!manifest.hasOwnProperty('nodes')) {
		return;
	}

	var map = {};

	Object.keys(manifest.nodes).forEach(function (name) {
		var node = manifest.nodes[name];
		var nodeProvider = provider;

		if (!nodeProvider) {
			nodeProvider = node.hasOwnProperty('default_provider') ? node.default_provider : null;

			if (nodeProvider) {
				nodeProvider = providers.instance(nodeProvider, manifest);
			}
		}

		if (!nodeProvider) {
			nodeProvider = manifest.hasOwnProperty('default_provider') ? manifest.default_provider : null;

			if (nodeProvider) {
				nodeProvider = providers.instance(nodeProvider, manifest);
			}
		}

		if (!nodeProvider) {
			nodeProvider = providers.instance('VirtualBox', manifest);
		}

		map[name] = {
			nodeProvider: nodeProvider,
			node: node
		};
	});

	Object.keys(map).forEach(function (name) {
		exports[action](map[name].nodeProvider, name, map[name].node, manifest);
	});
}

// ---

exports.status = status;
exports.boot = boot;
exports.halt = halt;
exports.launch = launch;
<commit_msg>Fixed typo.<commit_after>var path = require('path');

// ---

var providers = require(path.join(__dirname, 'providers.js'));

// ---

function status(provider, name, node, manifest) {
	provider.status(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		console.log(name + ':', state);
	});
}

function boot(provider, name, node, manifest) {
	provider.boot(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		exports.status(provider, name, node, manifest);
	});
}

function halt(provider, name, node, manifest) {
	provider.halt(name, node, function (err, state) {
		if (err) {
			return console.error(err.message);
		}

		exports.status(provider, name, node, manifest);
	});
}

// ---

function launch(manifest, plugins, provider, action) {
	(plugins || []).forEach(function (plugin) {
		plugin.vortex(manifest, provider, action);
	});

	if (!(action in exports) || action == 'launch') {
		throw new Error('action ' + action + ' not recognized');
	}

	if (!manifest.hasOwnProperty('nodes')) {
		return;
	}

	var map = {};

	Object.keys(manifest.nodes).forEach(function (name) {
		var node = manifest.nodes[name];
		var nodeProvider = provider;

		if (!nodeProvider) {
			nodeProvider = node.hasOwnProperty('default_provider') ? node.default_provider : null;

			if (nodeProvider) {
				nodeProvider = providers.instance(nodeProvider, manifest);
			}
		}

		if (!nodeProvider) {
			nodeProvider = manifest.hasOwnProperty('default_provider') ? manifest.default_provider : null;

			if (nodeProvider) {
				nodeProvider = providers.instance(nodeProvider, manifest);
			}
		}

		if (!nodeProvider) {
			nodeProvider = providers.instance('VirtualBox', manifest);
		}

		map[name] = {
			nodeProvider: nodeProvider,
			node: node
		};
	});

	Object.keys(map).forEach(function (name) {
		exports[action](map[name].nodeProvider, name, map[name].node, manifest);
	});
}

// ---

exports.status = status;
exports.boot = boot;
exports.halt = halt;
exports.launch = launch;
<|endoftext|>


<commit_before>var
  redis = require("redis")
  , _ = require('underscore')._
  , neuron = require('neuron')
  , zlib = require('zlib')
  , winston = require('./logging').winston
  , oauth = require("./oauth")
  , oauth2 = require('./oauth2')
;

require('date-utils');

// Connect to redis
var client = redis.createClient(GLOBAL.config.redis.port, GLOBAL.config.redis.address);
if (GLOBAL.config.redis.auth) {
	client.auth(GLOBAL.config.redis.auth, function (err) {
		if (err) {
			// handle err;
		}
	});
}

//
// ## Recursive function to remove unwanted elements from API response
//
var filter = function( source, map) {
	if (_.isArray(source)) {
		_.each(source, function(item, index) { filter( item, map[0]); });
	} else {
		if (_.isString(source) || map === true || _.isUndefined(map)) return 0;
		_.each(source, function(obj, key, source) {
			if (_.isUndefined(map[key])) {
				delete source[key];
			} else {
				filter( obj, map[key]);
			}
		});
	};
}

//
// ## Perform scheduled refresh
//
exports.refresh = function(api, key, bid, bundle) {

	winston.info('exports.refresh: ' + api);
	// We're forcing a refresh of the content so run the api.code
	api.resource( api.params, api.credentials, function( err, res ) {
		if ( err ) {

			// We got an error so set our output object to be the error and expire immediately
			api.expires = ( new Date() );
  			var tout = {
  				expires: api.expires,
  				result: err,
  				iid: bid+key,
  				cname: key,
  				scheduled: true
  			};

		} else {

  			winston.event('Get data for ' + bid + ' from ' + key + ', ' + res.size);

  			// Filter the response
  			if (_.has(api.filter)) {
	  			filter ( res, api.filter );
  			}

			// Perform cleanup function on API response
  			if (_.has(api, 'cleanup')) {
	  			res = api.cleanup(res);
  			}

			// Build the stored response
  			api.expires = ( new Date() ).addSeconds( api.cacheduration );
  			bundle[key] = api;

  			var tout = {
  				expires: api.expires,
  				result: res,
  				iid: api.iid,
  				cname: key,
  				scheduled: true
  			};

	   		// Save response to Redis
	   		client.set(bid+key, JSON.stringify(tout));

	   		// Update the bundle
	   		client.del('bid'+bid);
		}
  	});

}

//
// ## Retrieve the requested bundle
//
exports.fulfill = function ( myRes, ip, bid, callback, gzip, override ) {

	winston.info('exports.fulfill: ' + bid);

	var bundle = GLOBAL.bundles[bid];

	if (!callback && bundle && bundle.callback) {
		callback = bundle.callback;
	}

	// If the user requested a bundle that is not defined
	if (typeof bundle === 'undefined') {
		myRes.writeHead(404);
		myRes.end();
		return false;
	}

	// Count the number of queries in this bundle so we know when we are ready to respond
	var queriesInThisBundle = _.size(bundle),
		thisResponse = {};

	// cleanup is not an API request
	if(_.has(bundle, 'cleanup')) {
		queriesInThisBundle--;
	}

	// callback is not an API request
	if(_.has(bundle, 'callback')) {
		queriesInThisBundle--;
	}

	// If override was not passed
	if( _.isUndefined( override )) {

		// Retrieve bundle response from Redis
		client.get('bid'+bid, function ( err, doc ) {

			if ( err || doc === null ) {
				// There was an error so force refresh on bundle
				exports.fulfill( myRes, ip, bid, callback, gzip, true );

			} else {

				jDoc = JSON.parse( doc );

				// If there is a valid expiration date for the bundle
				jDoc.expires = new Date(jDoc.expires);
				if ( 'expires' in jDoc && _.isDate(jDoc.expires) ) {
					jDoc.secleft = jDoc.expires.getSecondsBetween( new Date() ) * -1;
				} else {
					// This should never happen
					jDoc.secleft = 0;
				}

				if (Number(jDoc.secleft) < 0 ) {

					// The bundle has expired. Force a refresh
					exports.fulfill( myRes, ip, bid, callback, gzip, true );

				} else {

					//Respond with the cached data
					jDoc.fromcache = true;
					var responseType = callback ? 'application/javascript' : 'application/json';
					var responseHeaders = {'Content-Type': responseType, 'max-age': jDoc.secleft, 'cache-control': 'public, max-age='+jDoc.secleft+', no-transform', "Expires": jDoc.expires };

					doc = JSON.stringify(jDoc);

					// If a callback name was passed, use it. Otherwise, just output the object
					var tbuf = new Buffer(  ( !callback ) ? doc : callback + '('  + doc +  ');');

					if (gzip) {

						zlib.gzip(tbuf.toString(), function(err, zbuf) {
						  if (!err) {
						    winston.event('Send gzipped response for ' + bid +', ' + zbuf.toString().length + ', ' + ip);
						    myRes.writeHead(200, {'Content-Type': responseType, 'max-age': jDoc.secleft, 'cache-control': 'public, max-age='+jDoc.secleft+', no-transform', "Expires": jDoc.expires, 'content-encoding': 'gzip' });
						    myRes.end(zbuf);
						  }
						});
					} else {
						myRes.writeHead(200, responseHeaders);
						winston.event('Send response for ' + bid +', ' + doc.length + ', ' + ip);
						myRes.end(tbuf);
					}

				}
			}
		});

	} else {

		// ### Override was passed so we are forcing a refresh on the bundle
		var manager = new neuron.JobManager();

		manager.on('empty', function (job) {

			winston.info('manager:empty');

			if (job.name === 'finishRequest' && queriesInThisBundle === 0) {
				var done = true;

				_.each(manager.job, function(job, key) {
					if (_.size(job.waiting)  !== 0 || _.size(job.running) !== 0 || job.queue.length !== 0) {
        				done = false;
        			}
        		});

        		if (done) manager.enqueue('composeResponse', bid);
			}
		});

		manager.addJob('fulfillPart', {
			work: function(api, bid, key, override, cachedPart) {

				winston.info('manager:fulfillPart: ' + bid + '.' + key + ', override: '+override);

				var self = this;

				if ( _.isUndefined( override ) ) {

					// Load the cached api response from Redis
			  		client.get(bid+key, function (err, doc) {
			  			if (err || doc === null){
			  				self.finished = true;
			  				manager.enqueue('fulfillPart', api, bid, key, true );
			  			} else {

				  			doc = JSON.parse( doc );
				  			doc.expires = new Date(doc.expires);
				  			if ( ('expires' in doc) && _.isDate(doc.expires) ) {
				  				var secleft = doc.expires.getSecondsBetween( new Date() ) * -1;
				  			}
				  			if (secleft < 0) {
				  				self.finished = true;
				  				manager.enqueue('fulfillPart', api, bid, key, true, doc );
				  			} else {
				  				doc.fromcache = true;
				  				manager.enqueue('finishRequest', doc );
				  				self.finished = true;
				  			}
				  		}
				  	});

				} else {

					if (_.has( api, 'auth')) {

						winston.info('Bundle uses auth type ' + api.auth.type);
						// If the API request object has an auth scheme defined
						if (api.auth.type == 'oauth') {
							oauth.authorize (api, bid, key, function( result, authParams ) {
								if (result === true) {
									api.params = _.extend(api.params, authParams);
									manager.enqueue('startRequest', api, key, cachedPart, bid);
								} else {
									manager.enqueue('finishRequest', result );
								}
								self.finished = true;
							});
						} else if (api.auth.type == 'oauth2') {
							oauth2.authorize (api, bid, key, function( result, authParams ) {
								if (result === true) {
									api.params = _.extend(api.params, authParams);
									manager.enqueue('startRequest', api, key, cachedPart, bid);
								} else {
									manager.enqueue('finishRequest', result );
								}
								self.finished = true;
							});
						} else {
							winston.error('auth type ' + api.auth.type + ' not recognized');
						}
					} else {
						// Authentication is not needed
						self.finished = true;
						manager.enqueue('startRequest', api, key, cachedPart, bid);
					}

				}

			}
		});

		manager.addJob('startRequest', {
			work: function( api, key, cachedPart, bid ) {

				winston.info('manager:startRequest: ' + key);

				var self = this;

				if (_.has( api, 'timeout') && _.isObject(cachedPart)) {
					self.timeout = setTimeout(function(self) {
						if(_.isObject(cachedPart)) {
							cachedPart.timeout = true;
							cachedPart.fromcache = true;
						} else {
							cachedPart= {
								"cname": key,
								"timeout" : true,
								"fromcache" : false
							};
						}
						manager.enqueue('finishRequest', cachedPart );
						self.finished = true;
					}, api.timeout, self)
				}

				api.resource( api.params, api.credentials, function( err, res ) {
		  			clearTimeout(self.timeout)
		  			delete self.timeout;

		  			if ( err ) {

		  				api.expires = ( new Date() );
			  			tout = {};
			  			tout.cname = key;
			  			tout.expires = api.expires;
			  			tout.result =  _.isUndefined(cachedPart) ? {} : cachedPart;
			  			tout.fromcache = true;
			  			tout.err = err;
			  			winston.error('Problem retrieving data for ' + bid + ' from ' + key + ': ' + JSON.stringify(err));

		  			} else {

			  			winston.event('Get data for ' + bid + ' from ' + key + ', ' + res.size);

			  			// Filter the response
			  			if (_.has(api, 'filter')) {
				  			filter ( res, api.filter );
			  			}

			  			// Perform cleanup function on API response
			  			if (_.has(api, 'cleanup')) {
				  			res = api.cleanup(res);
			  			}

			  			// Build the stored response
			  			api.expires = ( new Date() ).addSeconds( api.cacheduration );
			  			bundle[key] = api;
			  			//client.set('bundle'+bid, JSON.stringify(bundle));
			  			var tout = {
			  				expires: api.expires,
			  				result: res,
			  				iid: api.iid,
			  				cname: key
			  			};

				   		// Save response to Redis
				   		client.set(bid+key, JSON.stringify(tout));
				   	}
					manager.enqueue('finishRequest', tout );
	  				self.finished = true;
			  	});
			}
		})

		manager.addJob('finishRequest', {
			work: function(apiResponse) {

				winston.info('manager:finishRequest');

				queriesInThisBundle--;

				if (_.has(apiResponse, 'redirect')) thisResponse.redirect = apiResponse.redirect;
				thisResponse[apiResponse.cname] = apiResponse;

			  	this.finished = true;
			}
		});

		manager.addJob('composeResponse', {
			work: function() {

				winston.info('manager:composeResponse');

				// Update the expiration date on the bundle
				var tout = {
					expires: _.min( thisResponse, function( val ) { return val.expires } ).expires,
				};

				if (_.has( thisResponse, 'redirect')) tout.redirect = thisResponse.redirect;

				// Insert api responses into bundle
				_.each( thisResponse, function( val, idx ) {
					tout[val.cname] = val;
				});

				// Perform cleanup function on bundle
			  	if (_.has(bundle, 'cleanup')) {
				  	tout = bundle.cleanup(tout);
				}

				// Save cached bundle in Redis
				client.set('bid'+bid, JSON.stringify(tout));

				// Determine the seconds left before expiry
				if ( 'expires' in tout && _.isDate(tout.expires) ) {
					tout.secleft = tout.expires.getSecondsBetween( new Date() ) * -1;
				} else {
					tout.secleft = 3600;
				}

				manager.enqueue('sendResponse', tout);
				this.finished = true;

			}
		});

		manager.addJob('sendResponse', {
			work: function(doc) {

				winston.info('manager:sendResponse');

				if (_.has(doc, 'redirect')) {
					myRes.statusCode = 302;
					myRes.setHeader("Location", doc.redirect);
					myRes.end();
					this.finished = true;
				} else {

					// Send the results
					var responseType = callback ? 'application/javascript' : 'application/json';
					var responseHeaders = {'Content-Type': responseType, 'max-age': doc.secleft, 'cache-control': 'public, max-age='+doc.secleft+', no-transform', "Expires": doc.expires };

					var responseType = callback ? 'application/javascript' : 'application/json';
					var responseHeaders = {'Content-Type': responseType, 'max-age': doc.secleft, 'cache-control': 'public, max-age='+doc.secleft+', no-transform', "Expires": doc.expires };

					// If a callback name was passed, use it. Otherwise, just output the object
					var tbuf = new Buffer(  ( !callback ) ? JSON.stringify( doc ) : callback + '('  + JSON.stringify( doc ) +  ');');

					if (gzip) {

						zlib.gzip(tbuf.toString(), function(err, zbuf) {
						  if (!err) {
						    winston.event('Send gzipped response for ' + bid +', ' + zbuf.toString().length + ', ' + ip);
						    myRes.writeHead(200, {'Content-Type': responseType, 'max-age': doc.secleft, 'cache-control': 'public, max-age='+doc.secleft+', no-transform', "Expires": doc.expires, 'content-encoding': 'gzip' });
						    myRes.end(zbuf);
						    this.finished = true;
						  }
						});
					} else {
						winston.event('Send response for ' + bid +', ' + tbuf.length + ', ' + ip);
						myRes.writeHead(200, responseHeaders);
						myRes.end(tbuf);
						this.finished = true;
					}
				}
			}
		});

		_.each( bundle, function( api, key ) {
			if (key !== 'cleanup' && key !== 'callback') {
				if (_.isUndefined(api, 'credentials')) {
					api.credentials = {};
				}
				manager.enqueue('fulfillPart', api, bid, key);
			}
		});

	}
}<commit_msg>Fix filter and add vary: Accept_Encoding to header<commit_after>var
  redis = require("redis")
  , _ = require('underscore')._
  , neuron = require('neuron')
  , zlib = require('zlib')
  , winston = require('./logging').winston
  , oauth = require("./oauth")
  , oauth2 = require('./oauth2')
;

require('date-utils');

// Connect to redis
var client = redis.createClient(GLOBAL.config.redis.port, GLOBAL.config.redis.address);
if (GLOBAL.config.redis.auth) {
	client.auth(GLOBAL.config.redis.auth, function (err) {
		if (err) {
			// handle err;
		}
	});
}

//
// ## Recursive function to remove unwanted elements from API response
//
var filter = function( source, map) {
	if (_.isArray(source)) {
		_.each(source, function(item, index) { filter( item, map[0]); });
	} else {
		if (_.isString(source) || map === true || _.isUndefined(map)) return 0;
		_.each(source, function(obj, key, source) {
			if (_.isUndefined(map[key])) {
				delete source[key];
			} else {
				filter( obj, map[key]);
			}
		});
	};
}

//
// ## Perform scheduled refresh
//
exports.refresh = function(api, key, bid, bundle) {

	winston.info('exports.refresh: ' + api);
	// We're forcing a refresh of the content so run the api.code
	api.resource( api.params, api.credentials, function( err, res ) {
		if ( err ) {

			// We got an error so set our output object to be the error and expire immediately
			api.expires = ( new Date() );
  			var tout = {
  				expires: api.expires,
  				result: err,
  				iid: bid+key,
  				cname: key,
  				scheduled: true
  			};

		} else {

  			winston.event('Get data for ' + bid + ' from ' + key + ', ' + res.size);

  			// Perform cleanup function on API response
  			if (_.has(api, 'cleanup')) {
	  			res = api.cleanup(res);
  			}

			// Filter the response
  			if (_.has(api, 'filter')) {
	  			filter ( res, api.filter );
  			}

			// Build the stored response
  			api.expires = ( new Date() ).addSeconds( api.cacheduration );
  			bundle[key] = api;

  			var tout = {
  				expires: api.expires,
  				result: res,
  				iid: api.iid,
  				cname: key,
  				scheduled: true
  			};

	   		// Save response to Redis
	   		client.set(bid+key, JSON.stringify(tout));

	   		// Update the bundle
	   		client.del('bid'+bid);
		}
  	});

}

//
// ## Retrieve the requested bundle
//
exports.fulfill = function ( myRes, ip, bid, callback, gzip, override ) {

	winston.info('exports.fulfill: ' + bid);

	var bundle = GLOBAL.bundles[bid];

	if (!callback && bundle && bundle.callback) {
		callback = bundle.callback;
	}

	// If the user requested a bundle that is not defined
	if (typeof bundle === 'undefined') {
		myRes.writeHead(404);
		myRes.end();
		return false;
	}

	// Count the number of queries in this bundle so we know when we are ready to respond
	var queriesInThisBundle = _.size(bundle),
		thisResponse = {};

	// cleanup is not an API request
	if(_.has(bundle, 'cleanup')) {
		queriesInThisBundle--;
	}

	// callback is not an API request
	if(_.has(bundle, 'callback')) {
		queriesInThisBundle--;
	}

	// If override was not passed
	if( _.isUndefined( override )) {

		// Retrieve bundle response from Redis
		client.get('bid'+bid, function ( err, doc ) {

			if ( err || doc === null ) {
				// There was an error so force refresh on bundle
				exports.fulfill( myRes, ip, bid, callback, gzip, true );

			} else {

				jDoc = JSON.parse( doc );

				// If there is a valid expiration date for the bundle
				jDoc.expires = new Date(jDoc.expires);
				if ( 'expires' in jDoc && _.isDate(jDoc.expires) ) {
					jDoc.secleft = jDoc.expires.getSecondsBetween( new Date() ) * -1;
				} else {
					// This should never happen
					jDoc.secleft = 0;
				}

				if (Number(jDoc.secleft) < 0 ) {

					// The bundle has expired. Force a refresh
					exports.fulfill( myRes, ip, bid, callback, gzip, true );

				} else {

					//Respond with the cached data
					jDoc.fromcache = true;
					var responseType = callback ? 'application/javascript' : 'application/json';
					var responseHeaders = {'Content-Type': responseType, 'vary': 'Accept-Encoding', 'max-age': jDoc.secleft, 'cache-control': 'public, max-age='+jDoc.secleft+', no-transform', "Expires": jDoc.expires };
					doc = JSON.stringify(jDoc);

					// If a callback name was passed, use it. Otherwise, just output the object
					var tbuf = new Buffer(  ( !callback ) ? doc : callback + '('  + doc +  ');');

					if (gzip) {

						zlib.gzip(tbuf.toString(), function(err, zbuf) {
						  if (!err) {
						    winston.event('Send gzipped response for ' + bid +', ' + zbuf.toString().length + ', ' + ip);
						    myRes.writeHead(200, {'Content-Type': responseType, 'vary': 'Accept-Encoding', 'max-age': jDoc.secleft, 'cache-control': 'public, max-age='+jDoc.secleft+', no-transform', "Expires": jDoc.expires, 'content-encoding': 'gzip' });
						    myRes.end(zbuf);
						  }
						});
					} else {
						myRes.writeHead(200, responseHeaders);
						winston.event('Send response for ' + bid +', ' + doc.length + ', ' + ip);
						myRes.end(tbuf);
					}

				}
			}
		});

	} else {

		// ### Override was passed so we are forcing a refresh on the bundle
		var manager = new neuron.JobManager();

		manager.on('empty', function (job) {

			winston.info('manager:empty');

			if (job.name === 'finishRequest' && queriesInThisBundle === 0) {
				var done = true;

				_.each(manager.job, function(job, key) {
					if (_.size(job.waiting)  !== 0 || _.size(job.running) !== 0 || job.queue.length !== 0) {
        				done = false;
        			}
        		});

        		if (done) manager.enqueue('composeResponse', bid);
			}
		});

		manager.addJob('fulfillPart', {
			work: function(api, bid, key, override, cachedPart) {

				winston.info('manager:fulfillPart: ' + bid + '.' + key + ', override: '+override);

				var self = this;

				if ( _.isUndefined( override ) ) {

					// Load the cached api response from Redis
			  		client.get(bid+key, function (err, doc) {
			  			if (err || doc === null){
			  				self.finished = true;
			  				manager.enqueue('fulfillPart', api, bid, key, true );
			  			} else {

				  			doc = JSON.parse( doc );
				  			doc.expires = new Date(doc.expires);
				  			if ( ('expires' in doc) && _.isDate(doc.expires) ) {
				  				var secleft = doc.expires.getSecondsBetween( new Date() ) * -1;
				  			}
				  			if (secleft < 0) {
				  				self.finished = true;
				  				manager.enqueue('fulfillPart', api, bid, key, true, doc );
				  			} else {
				  				doc.fromcache = true;
				  				manager.enqueue('finishRequest', doc );
				  				self.finished = true;
				  			}
				  		}
				  	});

				} else {

					if (_.has( api, 'auth')) {

						winston.info('Bundle uses auth type ' + api.auth.type);
						// If the API request object has an auth scheme defined
						if (api.auth.type == 'oauth') {
							oauth.authorize (api, bid, key, function( result, authParams ) {
								if (result === true) {
									api.params = _.extend(api.params, authParams);
									manager.enqueue('startRequest', api, key, cachedPart, bid);
								} else {
									manager.enqueue('finishRequest', result );
								}
								self.finished = true;
							});
						} else if (api.auth.type == 'oauth2') {
							oauth2.authorize (api, bid, key, function( result, authParams ) {
								if (result === true) {
									api.params = _.extend(api.params, authParams);
									manager.enqueue('startRequest', api, key, cachedPart, bid);
								} else {
									manager.enqueue('finishRequest', result );
								}
								self.finished = true;
							});
						} else {
							winston.error('auth type ' + api.auth.type + ' not recognized');
						}
					} else {
						// Authentication is not needed
						self.finished = true;
						manager.enqueue('startRequest', api, key, cachedPart, bid);
					}

				}

			}
		});

		manager.addJob('startRequest', {
			work: function( api, key, cachedPart, bid ) {

				winston.info('manager:startRequest: ' + key);

				var self = this;

				if (_.has( api, 'timeout') && _.isObject(cachedPart)) {
					self.timeout = setTimeout(function(self) {
						if(_.isObject(cachedPart)) {
							cachedPart.timeout = true;
							cachedPart.fromcache = true;
						} else {
							cachedPart= {
								"cname": key,
								"timeout" : true,
								"fromcache" : false
							};
						}
						manager.enqueue('finishRequest', cachedPart );
						self.finished = true;
					}, api.timeout, self)
				}

				api.resource( api.params, api.credentials, function( err, res ) {
		  			clearTimeout(self.timeout)
		  			delete self.timeout;

		  			if ( err ) {

		  				api.expires = ( new Date() );
			  			tout = {};
			  			tout.cname = key;
			  			tout.expires = api.expires;
			  			tout.result =  _.isUndefined(cachedPart) ? {} : cachedPart;
			  			tout.fromcache = true;
			  			tout.err = err;
			  			winston.error('Problem retrieving data for ' + bid + ' from ' + key + ': ' + JSON.stringify(err));

		  			} else {

			  			winston.event('Get data for ' + bid + ' from ' + key + ', ' + res.size);

			  			// Perform cleanup function on API response
			  			if (_.has(api, 'cleanup')) {
				  			res = api.cleanup(res);
			  			}

			  			// Filter the response
			  			if (_.has(api, 'filter')) {
				  			filter ( res, api.filter );
			  			}

			  			// Build the stored response
			  			api.expires = ( new Date() ).addSeconds( api.cacheduration );
			  			bundle[key] = api;
			  			//client.set('bundle'+bid, JSON.stringify(bundle));
			  			var tout = {
			  				expires: api.expires,
			  				result: res,
			  				iid: api.iid,
			  				cname: key
			  			};

				   		// Save response to Redis
				   		client.set(bid+key, JSON.stringify(tout));
				   	}
					manager.enqueue('finishRequest', tout );
	  				self.finished = true;
			  	});
			}
		})

		manager.addJob('finishRequest', {
			work: function(apiResponse) {

				winston.info('manager:finishRequest');

				queriesInThisBundle--;

				if (_.has(apiResponse, 'redirect')) thisResponse.redirect = apiResponse.redirect;
				thisResponse[apiResponse.cname] = apiResponse;

			  	this.finished = true;
			}
		});

		manager.addJob('composeResponse', {
			work: function() {

				winston.info('manager:composeResponse');

				// Update the expiration date on the bundle
				var tout = {
					expires: _.min( thisResponse, function( val ) { return val.expires } ).expires,
				};

				if (_.has( thisResponse, 'redirect')) tout.redirect = thisResponse.redirect;

				// Insert api responses into bundle
				_.each( thisResponse, function( val, idx ) {
					tout[val.cname] = val;
				});

				// Perform cleanup function on bundle
			  	if (_.has(bundle, 'cleanup')) {
				  	tout = bundle.cleanup(tout);
				}

				// Save cached bundle in Redis
				client.set('bid'+bid, JSON.stringify(tout));

				// Determine the seconds left before expiry
				if ( 'expires' in tout && _.isDate(tout.expires) ) {
					tout.secleft = tout.expires.getSecondsBetween( new Date() ) * -1;
				} else {
					tout.secleft = 3600;
				}

				manager.enqueue('sendResponse', tout);
				this.finished = true;

			}
		});

		manager.addJob('sendResponse', {
			work: function(doc) {

				winston.info('manager:sendResponse');

				if (_.has(doc, 'redirect')) {
					myRes.statusCode = 302;
					myRes.setHeader("Location", doc.redirect);
					myRes.end();
					this.finished = true;
				} else {

					// Send the results

					var responseType = callback ? 'application/javascript' : 'application/json';
					var responseHeaders = {'Content-Type': responseType, 'vary': 'Accept-Encoding', 'max-age': doc.secleft, 'cache-control': 'public, max-age='+doc.secleft+', no-transform', "Expires": doc.expires };

					// If a callback name was passed, use it. Otherwise, just output the object
					var tbuf = new Buffer(  ( !callback ) ? JSON.stringify( doc ) : callback + '('  + JSON.stringify( doc ) +  ');');

					if (gzip) {

						zlib.gzip(tbuf.toString(), function(err, zbuf) {
						  if (!err) {
						    winston.event('Send gzipped response for ' + bid +', ' + zbuf.toString().length + ', ' + ip);
						    myRes.writeHead(200, {'Content-Type': responseType, 'max-age': doc.secleft, 'cache-control': 'public, max-age='+doc.secleft+', no-transform', "Expires": doc.expires, 'content-encoding': 'gzip' });
						    myRes.end(zbuf);
						    this.finished = true;
						  }
						});
					} else {
						winston.event('Send response for ' + bid +', ' + tbuf.length + ', ' + ip);
						myRes.writeHead(200, responseHeaders);
						myRes.end(tbuf);
						this.finished = true;
					}
				}
			}
		});

		_.each( bundle, function( api, key ) {
			if (key !== 'cleanup' && key !== 'callback') {
				if (_.isUndefined(api, 'credentials')) {
					api.credentials = {};
				}
				manager.enqueue('fulfillPart', api, bid, key);
			}
		});

	}
}<|endoftext|>


<commit_before>/**
 * Module for interfacing with the Kalabox container system implementation, and
 * the provider of the container system.
 *
 * NOTE: The container system and the provider are not always the same. For
 * example using docker on a Mac would use docker as the container system,
 * however it would be provided by boot2docker.
 * @module kbox.engine
 */

'use strict';

var core = require('./core.js');
var util = require('./util.js');
var helpers = util.helpers;

var provider = require('./engine/provider.js');
exports.provider = provider;

var engine = null;

var makeProviderError = function(msg) {
  return new Error(
    ['Provider "' + engine.getProviderName() + '"', msg].join(' ')
  );
};

var isProviderInstalled = false;
var verifyProviderInstalled = function(callback) {
  if (isProviderInstalled) {
    callback(null);
  } else {
    provider.isInstalled(function(err, isInstalled) {
      if (err) {
        callback(err);
      } else if (!isInstalled) {
        callback(makeProviderError('is NOT installed!'));
      } else {
        isProviderInstalled = true;
        callback(null);
      }
    });
  }
};

var isProviderUp = false;
var verifyProviderUp = function(callback) {
  if (isProviderUp) {
    callback(null);
  } else {
    provider.isUp(function(err, isUp) {
      if (err) {
        callback(err);
      } else if (!isUp) {
        callback(makeProviderError('is NOT up!'));
      } else {
        isProviderUp = true;
        callback(null);
      }
    });
  }
};

var engineHasBeenInit = false;
var verifyProviderIsReady = function(callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      verifyProviderUp(function(err) {
        if (err) {
          callback(err);
        } else {
          if (!engineHasBeenInit) {
            provider.engineConfig(function(err, config) {
              if (err) {
                callback(err);
              }
              else {
                engine.init(config);
                engineHasBeenInit = true;
                callback(null);
              }
            });
          }
          else {
            callback(null);
          }
        }
      });
    }
  });
};

/**
 * Returns the name of the configured provider.
 * @returns {string} - Name of the configured provider.
 */
exports.getProviderName = function() {
  return provider.name;
};

/**
 * Retrieves the state of the engine being up.
 * @arg {function} callback
 * @arg {error} callback.error
 * @arg {boolean} callback.isUp - Boolean representing the state of the engine being up.
 * @example
 * kbox.engine.isUp(function(err, isUp) {
 *   if (err) {
 *     throw err;
 *   } else if (isUp) {
 *     console.log('It is up!');
 *   }
 * });
 */
exports.isUp = function(callback) {
  provider.isUp(callback);
};

/**
 * Initializes kalabox engine.
 * @arg {string} globalConfig - Kalabox global config to initialize with.
 * @example
 * var globalConfig = kbox.core.config.getGlobalConfig();
 * kbox.engine.init(globalConfig);
 */
exports.init = function(globalConfig) {
  if (!engine) {
    engine = require('./engine/' + globalConfig.engine + '.js');
    core.deps.register('providerModule', engine.getProviderModule());
  }
};

/**
 * Gets the engine provider into the state it needs to be in to do
 * engine things.
 * @arg {int} attempts - The amount of times to try to start the engine.
 * @arg {function} callback - Callback called when the engine has started.
 * @arg {error} callback.error
 */
exports.up = function(attempts, callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      //provider.up(attempts, callback);
      provider.up(callback);
    }
  });
};

/**
 * Turns the engine off.
 * @arg {int} attempts - The amount of times to try to stop the engine.
 * @arg {function} callback - Callback called when the engine has been stopped.
 * @arg {error} callback.error
 */
exports.down = function(attempts, callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      //provider.down(attempts, callback);
      provider.down(callback);
    }
  });
};

/**
 * Lists installed containers.
 * @arg {string} appName [optional] - App name to filter containers by.
 * @arg {function} callback - Callback called when the engine query completes.
 * @arg {error} callback.error
 * @arg {array} callback.containers - An array of container objects.
 * @example
 * kbox.engine.list(function(err, containers) {
 *   for (var i = 0; i < containers.length; ++i) {
 *     console.log(containers[i].name);
 *   }
 * });
 */
exports.list = function(appName, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.list(appName, callback);
    }
  });
};

/**
 * Inspects a container and returns details.
 * @arg {string} containerId - Id of the container to inspect.
 * @arg {function} callback - Callback called when the container has been inspected.
 * @arg {error} callback.error
 * @arg {object} callback.data - Object containing the containers inspected data.
 */
exports.inspect = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.getEnsure(cid, 'inspect', function(err, container) {
        if (err) {
          callback(err);
        } else {
          engine.inspect(container, callback);
        }
      });
    }
  });
};

/**
 * Creates a container.
 * @arg {object} startOptions [optional] - Object containing the create options for the container.
 * @arg {function} callback - Callback called when the container has been created.
 * @arg {error} callback.error
 * @arg {object} callback.data - Object containing the created containers information.
 */
// @todo: find a good way to document createOptions, and possibly not make them docker specific.
exports.create = function(createOptions, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.create(createOptions, callback);
    }
  });
};

/**
 * Starts a container.
 * @arg {string} containerId - Id of the container to start.
 * @arg {object} startOptions [optional] - Object containing the start options for the container.
 * @arg {function} callback - Callback called when the container has been started.
 * @arg {error} callback.error
 * @todo Find a good way to document startOptions, and possibly not make them docker specific.
 */
exports.start = function(cid, startOptions, callback) {
  if (typeof startOptions === 'function' && callback === undefined) {
    callback = startOptions;
    startOptions = null;
  }
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.start(cid, startOptions, function(err) {
        // @todo: This short delay is to give redis a chance to get into a good state.
        // A better check would be bounded polling of redis until it is in a good state.
        setTimeout(function() {
          callback(err);
        }, 100);
      });
    }
  });
};

/**
 * Stops a container.
 * @arg {string} containerId - Id of the container to stop.
 * @arg {function} callback - Callback called when the container has been stopped.
 * @arg {error} callback.error
 * @example
 * kbox.engine.stop(containerId, function(err) {
 *   if (err) {
 *     throw err;
 *   }
 *   console.log('Container stopped!');
 * });
 */
exports.stop = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.stop(cid, callback);
    }
  });
};

/**
 * Removes a container from the engine.
 * @arg {string} containerId - Id of the container to remove.
 * @arg {function} callback - Callback called when the container has been removed.
 * @arg {error} callback.error
 */
exports.remove = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.remove(cid, callback);
    }
  });
};

/**
 * Pulls or builds the image for a container.
 * @arg {object} image - Image object to pull or build.
 * @arg {function} callback - Callback called when the image has been built.
 * @arg {error} callback.error
 */
exports.build = function(image, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      var fn = (image.build) ? 'build' : 'pull';
      engine[fn](image, callback);
    }
  });
};
<commit_msg>Increasing delay after starting a container from 100->3000 ms<commit_after>/**
 * Module for interfacing with the Kalabox container system implementation, and
 * the provider of the container system.
 *
 * NOTE: The container system and the provider are not always the same. For
 * example using docker on a Mac would use docker as the container system,
 * however it would be provided by boot2docker.
 * @module kbox.engine
 */

'use strict';

var core = require('./core.js');
var util = require('./util.js');
var helpers = util.helpers;

var provider = require('./engine/provider.js');
exports.provider = provider;

var engine = null;

var makeProviderError = function(msg) {
  return new Error(
    ['Provider "' + engine.getProviderName() + '"', msg].join(' ')
  );
};

var isProviderInstalled = false;
var verifyProviderInstalled = function(callback) {
  if (isProviderInstalled) {
    callback(null);
  } else {
    provider.isInstalled(function(err, isInstalled) {
      if (err) {
        callback(err);
      } else if (!isInstalled) {
        callback(makeProviderError('is NOT installed!'));
      } else {
        isProviderInstalled = true;
        callback(null);
      }
    });
  }
};

var isProviderUp = false;
var verifyProviderUp = function(callback) {
  if (isProviderUp) {
    callback(null);
  } else {
    provider.isUp(function(err, isUp) {
      if (err) {
        callback(err);
      } else if (!isUp) {
        callback(makeProviderError('is NOT up!'));
      } else {
        isProviderUp = true;
        callback(null);
      }
    });
  }
};

var engineHasBeenInit = false;
var verifyProviderIsReady = function(callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      verifyProviderUp(function(err) {
        if (err) {
          callback(err);
        } else {
          if (!engineHasBeenInit) {
            provider.engineConfig(function(err, config) {
              if (err) {
                callback(err);
              }
              else {
                engine.init(config);
                engineHasBeenInit = true;
                callback(null);
              }
            });
          }
          else {
            callback(null);
          }
        }
      });
    }
  });
};

/**
 * Returns the name of the configured provider.
 * @returns {string} - Name of the configured provider.
 */
exports.getProviderName = function() {
  return provider.name;
};

/**
 * Retrieves the state of the engine being up.
 * @arg {function} callback
 * @arg {error} callback.error
 * @arg {boolean} callback.isUp - Boolean representing the state of the engine being up.
 * @example
 * kbox.engine.isUp(function(err, isUp) {
 *   if (err) {
 *     throw err;
 *   } else if (isUp) {
 *     console.log('It is up!');
 *   }
 * });
 */
exports.isUp = function(callback) {
  provider.isUp(callback);
};

/**
 * Initializes kalabox engine.
 * @arg {string} globalConfig - Kalabox global config to initialize with.
 * @example
 * var globalConfig = kbox.core.config.getGlobalConfig();
 * kbox.engine.init(globalConfig);
 */
exports.init = function(globalConfig) {
  if (!engine) {
    engine = require('./engine/' + globalConfig.engine + '.js');
    core.deps.register('providerModule', engine.getProviderModule());
  }
};

/**
 * Gets the engine provider into the state it needs to be in to do
 * engine things.
 * @arg {int} attempts - The amount of times to try to start the engine.
 * @arg {function} callback - Callback called when the engine has started.
 * @arg {error} callback.error
 */
exports.up = function(attempts, callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      //provider.up(attempts, callback);
      provider.up(callback);
    }
  });
};

/**
 * Turns the engine off.
 * @arg {int} attempts - The amount of times to try to stop the engine.
 * @arg {function} callback - Callback called when the engine has been stopped.
 * @arg {error} callback.error
 */
exports.down = function(attempts, callback) {
  verifyProviderInstalled(function(err) {
    if (err) {
      callback(err);
    } else {
      //provider.down(attempts, callback);
      provider.down(callback);
    }
  });
};

/**
 * Lists installed containers.
 * @arg {string} appName [optional] - App name to filter containers by.
 * @arg {function} callback - Callback called when the engine query completes.
 * @arg {error} callback.error
 * @arg {array} callback.containers - An array of container objects.
 * @example
 * kbox.engine.list(function(err, containers) {
 *   for (var i = 0; i < containers.length; ++i) {
 *     console.log(containers[i].name);
 *   }
 * });
 */
exports.list = function(appName, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.list(appName, callback);
    }
  });
};

/**
 * Inspects a container and returns details.
 * @arg {string} containerId - Id of the container to inspect.
 * @arg {function} callback - Callback called when the container has been inspected.
 * @arg {error} callback.error
 * @arg {object} callback.data - Object containing the containers inspected data.
 */
exports.inspect = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.getEnsure(cid, 'inspect', function(err, container) {
        if (err) {
          callback(err);
        } else {
          engine.inspect(container, callback);
        }
      });
    }
  });
};

/**
 * Creates a container.
 * @arg {object} startOptions [optional] - Object containing the create options for the container.
 * @arg {function} callback - Callback called when the container has been created.
 * @arg {error} callback.error
 * @arg {object} callback.data - Object containing the created containers information.
 */
// @todo: find a good way to document createOptions, and possibly not make them docker specific.
exports.create = function(createOptions, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.create(createOptions, callback);
    }
  });
};

/**
 * Starts a container.
 * @arg {string} containerId - Id of the container to start.
 * @arg {object} startOptions [optional] - Object containing the start options for the container.
 * @arg {function} callback - Callback called when the container has been started.
 * @arg {error} callback.error
 * @todo Find a good way to document startOptions, and possibly not make them docker specific.
 */
exports.start = function(cid, startOptions, callback) {
  if (typeof startOptions === 'function' && callback === undefined) {
    callback = startOptions;
    startOptions = null;
  }
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.start(cid, startOptions, function(err) {
        // @todo: This short delay is to give redis a chance to get into a good state.
        // A better check would be bounded polling of redis until it is in a good state.
        setTimeout(function() {
          callback(err);
        }, 3000);
      });
    }
  });
};

/**
 * Stops a container.
 * @arg {string} containerId - Id of the container to stop.
 * @arg {function} callback - Callback called when the container has been stopped.
 * @arg {error} callback.error
 * @example
 * kbox.engine.stop(containerId, function(err) {
 *   if (err) {
 *     throw err;
 *   }
 *   console.log('Container stopped!');
 * });
 */
exports.stop = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.stop(cid, callback);
    }
  });
};

/**
 * Removes a container from the engine.
 * @arg {string} containerId - Id of the container to remove.
 * @arg {function} callback - Callback called when the container has been removed.
 * @arg {error} callback.error
 */
exports.remove = function(cid, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      engine.remove(cid, callback);
    }
  });
};

/**
 * Pulls or builds the image for a container.
 * @arg {object} image - Image object to pull or build.
 * @arg {function} callback - Callback called when the image has been built.
 * @arg {error} callback.error
 */
exports.build = function(image, callback) {
  verifyProviderIsReady(function(err) {
    if (err) {
      callback(err);
    } else {
      var fn = (image.build) ? 'build' : 'pull';
      engine[fn](image, callback);
    }
  });
};
<|endoftext|>