<commit_before>/**
 * Copyright 2015-2016, Google, Inc.
 * 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.
 */

'use strict';

const config = require('../config/config');
const images = require('../lib/images');
const Manifest = require('../models/manifest');
const Pwa = require('../models/pwa');
const dataFetcher = require('../lib/data-fetcher');
const lighthouseLib = require('../lib/lighthouse');
const db = require('../lib/model-' + config.get('DATA_BACKEND'));
const gcloud = require('google-cloud');
const ds = gcloud.datastore({
  projectId: config.get('GCLOUD_PROJECT')
});

const ENTITY_NAME = 'PWA';
const E_MANIFEST_ERROR = exports.E_MANIFEST_ERROR = 2;
const SORT_TYPE_MAP = new Map([
  ['name', {field: 'manifest.name'}],
  ['newest', {field: 'created', config: {descending: true}}]
]);

exports.list = function(numResults, pageToken, sort) {
  return new Promise((resolve, reject) => {
    let sortType = SORT_TYPE_MAP.get('name');
    if (sort) {
      sortType = SORT_TYPE_MAP.get(sort) || sortType;
    }
    db.list(ENTITY_NAME, numResults, pageToken, sortType)
      .then(result => {
        const pwas = result.entities.map(pwa => {
          return Object.assign(new Pwa(), pwa);
        });
        resolve({
          pwas: pwas,
          hasMore: result.hasMore
        });
      })
      .catch(err => {
        reject(err);
      });
  });
};

exports.find = function(key) {
  return new Promise((resolve, reject) => {
    db.read(ENTITY_NAME, key)
      .then(pwa => {
        const pwaInstance = Object.assign(new Pwa(), pwa);
        resolve(pwaInstance);
      })
      .catch(err => {
        reject(err);
      });
  });
};

exports.findByManifestUrl = function(manifestUrl) {
  return new Promise((resolve, reject) => {
    const query = ds.createQuery(ENTITY_NAME).filter('manifestUrl', manifestUrl);
    ds.runQuery(query, (err, pwas) => {
      if (err) {
        return reject(err);
      }

      if (pwas.length === 0) {
        return resolve(null);
      }

      pwas[0].data.id = pwas[0].key.id;
      var pwa = Object.assign(new Pwa(), pwas[0].data);
      return resolve(pwa);
    });
  });
};

exports.save = function(pwa) {
  return new Promise((resolve, reject) => {
    // TODO: Check manifestUrl with regexp
    if (!pwa.manifestUrl) {
      return reject('Missing manifestUrl');
    }

    if (!pwa.user.id) {
      return reject('Missing user information');
    }

    this.findByManifestUrl(pwa.manifestUrl)
      .then(existingPwa => {
        pwa = existingPwa || pwa;
        pwa.updated = new Date();
        return Manifest.fetch(pwa.manifestUrl);
      })
      .then(manifest => {
        pwa.manifest = manifest;
        return updateMetadataDescription(pwa);
      })
      .then(description => {
        pwa.metaDescription = description;
        return db.update(ENTITY_NAME, pwa.id, pwa);
      })
      .then(savedPwa => {
        updateIcon(savedPwa, pwa.manifest);
        lighthouseLib.fetchAndSave(savedPwa.id)
          .then(lighthouse => {
            savedPwa.lighthouseScore = lighthouse.lighthouseInfo.totalScore;
            db.update(ENTITY_NAME, savedPwa.id, savedPwa);
          });
        return resolve(savedPwa);
      })
      .catch(err => {
        console.log(err);
        return reject(E_MANIFEST_ERROR);
      });
  });
};

/**
 * Reading Description from Meta if not present in the Manifest
 */
function updateMetadataDescription(pwa) {
  return new Promise((resolve, reject) => {
    if (!pwa.description && pwa.absoluteStartUrl) {
      dataFetcher.fetchMetadataDescription(pwa.absoluteStartUrl)
        .then(description => {
          resolve(description);
        })
        .catch(err => {
          console.log(err);
          resolve(null);
        });
    } else {
      resolve(null);
    }
  });
}

function updateIcon(pwa, manifest) {
  const url = manifest.getBestIconUrl();
  const extension = url.substring(url.lastIndexOf('.'), url.length);
  const bucketFileName = pwa.id + extension;

  images.fetchAndSave(url, bucketFileName)
    .then(savedUrl => {
      pwa.iconUrl = savedUrl;
      return db.update(ENTITY_NAME, pwa.id, pwa);
    })
    .then(pwa => {
      console.log('Updated PWA Image: ', pwa.id);
    })
    .catch(err => {
      console.log(err);
    });
}

exports.delete = function(key) {
  return db.delete(ENTITY_NAME, key);
};
<commit_msg>Add sort by lighthouse score<commit_after>/**
 * Copyright 2015-2016, Google, Inc.
 * 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.
 */

'use strict';

const config = require('../config/config');
const images = require('../lib/images');
const Manifest = require('../models/manifest');
const Pwa = require('../models/pwa');
const dataFetcher = require('../lib/data-fetcher');
const lighthouseLib = require('../lib/lighthouse');
const db = require('../lib/model-' + config.get('DATA_BACKEND'));
const gcloud = require('google-cloud');
const ds = gcloud.datastore({
  projectId: config.get('GCLOUD_PROJECT')
});

const ENTITY_NAME = 'PWA';
const E_MANIFEST_ERROR = exports.E_MANIFEST_ERROR = 2;
const SORT_TYPE_MAP = new Map([
  ['name', {field: 'manifest.name'}],
  ['newest', {field: 'created', config: {descending: true}}],
  ['score', {field: 'lighthouseScore', config: {descending: true}}]
]);

exports.list = function(numResults, pageToken, sort) {
  return new Promise((resolve, reject) => {
    let sortType = SORT_TYPE_MAP.get('name');
    if (sort) {
      sortType = SORT_TYPE_MAP.get(sort) || sortType;
    }
    db.list(ENTITY_NAME, numResults, pageToken, sortType)
      .then(result => {
        const pwas = result.entities.map(pwa => {
          return Object.assign(new Pwa(), pwa);
        });
        resolve({
          pwas: pwas,
          hasMore: result.hasMore
        });
      })
      .catch(err => {
        reject(err);
      });
  });
};

exports.find = function(key) {
  return new Promise((resolve, reject) => {
    db.read(ENTITY_NAME, key)
      .then(pwa => {
        const pwaInstance = Object.assign(new Pwa(), pwa);
        resolve(pwaInstance);
      })
      .catch(err => {
        reject(err);
      });
  });
};

exports.findByManifestUrl = function(manifestUrl) {
  return new Promise((resolve, reject) => {
    const query = ds.createQuery(ENTITY_NAME).filter('manifestUrl', manifestUrl);
    ds.runQuery(query, (err, pwas) => {
      if (err) {
        return reject(err);
      }

      if (pwas.length === 0) {
        return resolve(null);
      }

      pwas[0].data.id = pwas[0].key.id;
      var pwa = Object.assign(new Pwa(), pwas[0].data);
      return resolve(pwa);
    });
  });
};

exports.save = function(pwa) {
  return new Promise((resolve, reject) => {
    // TODO: Check manifestUrl with regexp
    if (!pwa.manifestUrl) {
      return reject('Missing manifestUrl');
    }

    if (!pwa.user.id) {
      return reject('Missing user information');
    }

    this.findByManifestUrl(pwa.manifestUrl)
      .then(existingPwa => {
        pwa = existingPwa || pwa;
        pwa.updated = new Date();
        return Manifest.fetch(pwa.manifestUrl);
      })
      .then(manifest => {
        pwa.manifest = manifest;
        return updateMetadataDescription(pwa);
      })
      .then(description => {
        pwa.metaDescription = description;
        return db.update(ENTITY_NAME, pwa.id, pwa);
      })
      .then(savedPwa => {
        updateIcon(savedPwa, pwa.manifest);
        lighthouseLib.fetchAndSave(savedPwa.id)
          .then(lighthouse => {
            savedPwa.lighthouseScore = lighthouse.lighthouseInfo.totalScore;
            db.update(ENTITY_NAME, savedPwa.id, savedPwa);
          });
        return resolve(savedPwa);
      })
      .catch(err => {
        console.log(err);
        return reject(E_MANIFEST_ERROR);
      });
  });
};

/**
 * Reading Description from Meta if not present in the Manifest
 */
function updateMetadataDescription(pwa) {
  return new Promise((resolve, reject) => {
    if (!pwa.description && pwa.absoluteStartUrl) {
      dataFetcher.fetchMetadataDescription(pwa.absoluteStartUrl)
        .then(description => {
          resolve(description);
        })
        .catch(err => {
          console.log(err);
          resolve(null);
        });
    } else {
      resolve(null);
    }
  });
}

function updateIcon(pwa, manifest) {
  const url = manifest.getBestIconUrl();
  const extension = url.substring(url.lastIndexOf('.'), url.length);
  const bucketFileName = pwa.id + extension;

  images.fetchAndSave(url, bucketFileName)
    .then(savedUrl => {
      pwa.iconUrl = savedUrl;
      return db.update(ENTITY_NAME, pwa.id, pwa);
    })
    .then(pwa => {
      console.log('Updated PWA Image: ', pwa.id);
    })
    .catch(err => {
      console.log(err);
    });
}

exports.delete = function(key) {
  return db.delete(ENTITY_NAME, key);
};
<|endoftext|>


<commit_before>import $ from 'jquery';
import joint from 'jointjs';
import GeneratorFactory from "../generator/GeneratorFactory";
// DEBUG
// import {validator} from '../generator/temporary/old/validator'

/**
 * linkTypeSelector provides functions for creating and managing the context menu to choose a link type from.
 *
 * @returns {{init: Function, focusElement: Function, lostFocus: Function, canSetLink: Function, replaceLink: Function}}
 */
export default (function linkTypeSelector() {
    'use strict';

    let _menu = null;
    let _focusedElement = null;
    let _graph = null;
    let _paper = null;
    let _linkID = null;
    let _canSetLink = false;

    let init;
    let createMenu;
    let itemMouseUp;
    let destroyMenu;
    let showMenu;
    let focusElement;
    let lostFocus;
    let canSetLink;
    let replaceLink;
    let registerListeners;
    let handleAddedCell;
    let handleRemovedCell;
    let getConnectionCount;
    let connectionDefinitionGenerator;

    const removeFocus = () => {
        if (_focusedElement) {
            joint.V(_paper.findViewByModel(_focusedElement).el).removeClass('linking-allowed');
            joint.V(_paper.findViewByModel(_focusedElement).el).removeClass('linking-unallowed');
            _focusedElement = null;
        }
    };

    /**
     * Has to be called once before using the other methods!
     *
     * @param graph
     */
    init = function init(graph, paper) {
        _graph = graph;
        _paper = paper;
        connectionDefinitionGenerator = GeneratorFactory.connectionDefinition;
        registerListeners();
    };

    /**
     * Creates the context menu with the specified elements.
     *
     * @param {array} elements - An array of strings which will be the contents of the context menu.
     */
    createMenu = function createMenu(elements) {

        if (_menu) {
            destroyMenu();
        }

        let menu = '';
        elements = elements || [];

        menu += '
';

        if (elements.length > 0) {

            elements.forEach(function (name, index) {
                menu += '
';
                menu += name;
                menu += '
';
            });
            joint.V(_paper.findViewByModel(_focusedElement).el).addClass('linking-allowed');

        } else {
            menu += '
';
            menu += 'These elements are not linkable!';
            menu += '
';
            joint.V(_paper.findViewByModel(_focusedElement).el).addClass('linking-unallowed');
            canSetLink(false);
        }

        menu += '
';

        $('#contextMenuContainer').html(menu);

        // set handler
        elements.forEach(function (name, index) {
            let item = $('#menuItem' + index);

            item.mouseup(function () {
                itemMouseUp(name);
            });

            item.mouseenter(function () {
                canSetLink(true);
                // item.css('background', '#27ae60');
            });

            item.mouseleave(function () {
                // item.css('background', '#2c3e50');
            });
        });

        _menu = $('#contextMenu');

        _menu.mouseleave(function () {
            canSetLink(false);
        });

        $('body').mouseup(function () {
            removeFocus();
            destroyMenu();
        });

    };

    /**
     * Is called on mouseup on a link type in the context menu.
     *
     * @param linkName
     */
    itemMouseUp = function itemMouseUp(linkName) {
        let link = _graph.getCell(_linkID);
        link.attributes.subtype = linkName;
        destroyMenu();
    };

    /**
     * Is called when user is creating a link and is hovering over an element.
     *
     * @param {string} linkID - ID of the link that the user is just creating.
     * @param {object} eventTargetModel - The element over which the mouse is hovering.
     * @param {int} x - X-coordinate of the mouse.
     * @param {int} y - Y-coordinate of the mouse.
     */
    focusElement = function focusElement(linkID, eventTargetModel, x, y) {
        let link;
        let eventSourceID;
        let eventSourceType;
        let menuList;

        removeFocus();
        if (!eventTargetModel) {
            return;
        }

        _linkID = linkID;
        _focusedElement = eventTargetModel;

        link = _graph.getCell(linkID);

        if (link.attributes.source.id) {

            // event target === link target
            eventSourceID = link.attributes.source.id;
            eventSourceType = _graph.getCell(eventSourceID).attributes.nodeName;

        } else if (link.attributes.target.id) {

            // event target === link source
            eventSourceID = link.attributes.target.id;
            eventSourceType = _graph.getCell(eventSourceID).attributes.nodeName;

        }

        let eventTarget = eventTargetModel.attributes.nodeName;

        menuList = GeneratorFactory.validator.getValidEdges(eventSourceType, eventTarget);

        //DEBUG
        // let menuList_OLD = validator.getValidEdges(eventSourceType, eventTarget);

        createMenu(menuList);
        showMenu(x, y);
    };

    /**
     * Is called when the focus for the element is lost (not hovering over the element anymore).
     *
     * @param newFocusedElement
     */
    lostFocus = function lostFocus(newFocusedElement) {
        let element = $(newFocusedElement);

        if (element.hasClass('contextItem') || element.hasClass('menuItem') || element.hasClass('menuTable')) {
            return;
        }
        removeFocus();
        destroyMenu();
    };

    /**
     * Destroys the current context menu.
     */
    destroyMenu = function destroyMenu() {
        if (_menu) {
            _menu.remove();
            _menu = null;
            $('#contextMenuContainer').empty();
        }
    };

    /**
     * Shows the current context menu near the specified coordinates.
     * @param {int} x
     * @param {int} y
     */
    showMenu = function showMenu(x, y) {
        y -= 20;
        _menu.css('top', y + 'px');
        _menu.css('left', x + 'px');
        _menu.css('display', 'inline');
    };

    /**
     * Getter and setter for _canSetLink.
     * @param {boolean} [canSet]
     * @returns {boolean}
     */
    canSetLink = function canSetLink(canSet) {
        if (typeof canSet === 'boolean') {
            _canSetLink = canSet;
        }

        return _canSetLink;
    };

    /**
     * Once the link is drawn, its type is some default link type,
     * so it has to be replaced with a link of the correct type.
     * Also check if bounds of link source/target are violated through
     * the added link. Highlighting is added if this is the case
     * @param {object} link - The link that has just been drawn.
     * @returns {object} The new link.

     */

    replaceLink = function replaceLink(link) {
        let cell = _graph.getCell(_linkID);

        //DEBUG
        // let edgeData_OLD = validator.getEdgeData(link.attributes.subtype);

        let edgeData = GeneratorFactory.validator.getEdgeData(link.attributes.subtype);

        let edgeType = edgeData.type;
        let targetId = link.attributes.target.id;
        let sourceId = link.attributes.source.id;

        let maxInputs = null;
        let maxOutputs = null;
        let minInputs = null;
        let minOutputs = null;

            //DEBUG
            // let maxInputs_OLD = validator.inputMatrix[edgeData.to][edgeData.type].upperBound;
            // let maxOutputs_OLD = validator.outputMatrix[edgeData.from][edgeData.type].upperBound;
            // let minInputs_OLD = validator.inputMatrix[edgeData.to][edgeData.type].lowerBound;
            // let minOutputs_OLD = validator.outputMatrix[edgeData.from][edgeData.type].lowerBound;

            let result = GeneratorFactory.validator.getAllRefBounds();
            console.log(result);
            maxInputs = result[link.attributes.subtype].targetUpperBounds;
            maxOutputs = result[link.attributes.subtype].sourceUpperBounds;
            minInputs = result[link.attributes.subtype].targetLowerBounds;
            minOutputs = result[link.attributes.subtype].sourceLowerBounds;

        let targetMaxReached = false;
        let sourceMaxReached = false;

        link.prop('placings', connectionDefinitionGenerator.getPlacings(edgeData.style));
        // link.prop('placings', getPlacings(edgeData.style));
        link.prop('sourceAttribute', edgeData.from);
        link.prop('targetAttribute', edgeData.to);
        // edge type needs to be set before getting connection count
        // otherwise current link is ignored
        link.prop('mReference', edgeType);
        // link.prop('labels', getLabels(edgeData.style));
        link.prop('labels', connectionDefinitionGenerator.getLabels(edgeData.style));

        let ingoingTargetCount = getConnectionCount(targetId, edgeType, {inbound: true});
        let outgoingSourceCount = getConnectionCount(sourceId, edgeType, {outbound: true});

        if (maxInputs != -1 && ingoingTargetCount > maxInputs) {
            targetMaxReached = true;
            joint.V(_paper.findViewByModel(targetId).el).addClass('invalid-edges');
        }
        if (minInputs != 0 && minInputs <= ingoingTargetCount && !targetMaxReached) {
            joint.V(_paper.findViewByModel(targetId).el).removeClass('invalid-edges');
        }
        if (maxOutputs != -1 && outgoingSourceCount > maxOutputs) {
            sourceMaxReached = true;
            joint.V(_paper.findViewByModel(sourceId).el).addClass('invalid-edges');
        }
        if (minOutputs != 0 && minOutputs <= outgoingSourceCount && !sourceMaxReached) {
            joint.V(_paper.findViewByModel(sourceId).el).removeClass('invalid-edges');
        }
        let clone = link.clone();
        clone.attributes.attrs = connectionDefinitionGenerator.getConnectionStyle(edgeData.style);
        // clone.attributes.attrs = getConnectionStyle(edgeData.style);

        console.log(clone.attributes.attrs);
        console.log(clone.attributes.attrsxx);
        clone.prop('styleSet', true);
        link.remove();
        _graph.addCell(clone);
        destroyMenu();
        return cell;
    };

    /**
     * register listeners for added and removed cells
     */
    registerListeners = function () {
        _graph.on('add', handleAddedCell);
        _graph.on('remove', handleRemovedCell);
    };

    /**
     * Checks if the elements lower bounds are violated, if this is the case a highlighting is added
     * @param cell the newly added cell
     */
    handleAddedCell = function (cell) {
        if (cell.isLink()) return;
        let inputs = [];
        let outputs = [];
        let inoutMatrix = GeneratorFactory.validator.inputMatrix(cell.attributes.nodeName);

        //DEBUG
        // let inputMatrix_OLD = validator.inputMatrix[cell.attributes.mClass];
        // let outputMatrix_OLD = validator.outputMatrix[cell.attributes.mClass];

        for (let inEdge in inoutMatrix) {
            if (Object.prototype.hasOwnProperty.call(inoutMatrix, inEdge)) {
                if (inoutMatrix[inEdge].sourceLowerBounds > 0) {
                    inputs.push({'mReferenceName': inEdge, 'lowerBound': inoutMatrix[inEdge].sourceLowerBounds});
                }
            }
        }

        for (let outEdge in inoutMatrix) {
            if (Object.prototype.hasOwnProperty.call(inoutMatrix, outEdge)) {
                if (inoutMatrix[outEdge].targetLowerBounds > 0) {
                    outputs.push({'mReferenceName': outEdge, 'lowerBound': inoutMatrix[outEdge].targetLowerBounds});
                }
            }
        }

        if (inputs.length != 0 || outputs.length != 0) {
            joint.V(_paper.findViewByModel(cell).el).addClass('invalid-edges');
        }
    };

    /**
     * Checks if bounds are violated through a removed link,
     * highlighting is added if this is the case
     * @param link the removed link
     */
    handleRemovedCell = function (link) {
        // check if style is set, otherwise the removed dummy link will influence the counters
        // this also ignores all Elements
        if (!link.attributes.styleSet) return;

        //DEBUG
        // let edgeType_OLD = validator.getEdgeData(link.attributes.subtype).type;

        let result = GeneratorFactory.validator.getAllRefBounds();
        let edgeType = GeneratorFactory.validator.getEdgeData(link.attributes.subtype).type;

        let sourceMClass = _graph.getCell(link.attributes.source.id).attributes.mClass;
        let targetMClass = _graph.getCell(link.attributes.target.id).attributes.mClass;
        let minInputs = null;
        let minOutputs = null;
        let maxInputs = null;
        let maxOutputs = null;

            //DEBUG
            // let minInputs_OLD = validator.inputMatrix[targetMClass][edgeType].lowerBound;
            // let minOutputs_OLD = validator.outputMatrix[sourceMClass][edgeType].lowerBound;
            // let maxInputs_OLD = validator.inOutMatrix[targetMClass][edgeType].upperBound;
            // let maxOutputs_OLD = validator.outputMatrix[sourceMClass][edgeType].upperBound;

            maxInputs = result[link.attributes.subtype].targetUpperBounds;
            maxOutputs = result[link.attributes.subtype].sourceUpperBounds;
            minInputs = result[link.attributes.subtype].targetLowerBounds;
            minOutputs = result[link.attributes.subtype].sourceLowerBounds;

        let ingoingTargetCount = getConnectionCount(link.attributes.target.id, edgeType, {inbound: true});
        let outgoingSourceCount = getConnectionCount(link.attributes.source.id, edgeType, {outbound: true});
        let minInUnderstepped = false;
        let minOutUnderstepped = false;

        if (minInputs > ingoingTargetCount) {
            minInUnderstepped = true;
            joint.V(_paper.findViewByModel(link.attributes.target.id).el).addClass('invalid-edges');
        }

        if (maxInputs != -1 && maxInputs >= ingoingTargetCount && !minInUnderstepped) {
            joint.V(_paper.findViewByModel(link.attributes.target.id).el).removeClass('invalid-edges');
        }

        if (minOutputs > outgoingSourceCount) {
            minOutUnderstepped = true;
            joint.V(_paper.findViewByModel(link.attributes.source.id).el).addClass('invalid-edges');
        }
        if (maxOutputs != -1 && maxOutputs >= outgoingSourceCount && !minOutUnderstepped) {
            joint.V(_paper.findViewByModel(link.attributes.source.id).el).removeClass('invalid-edges');
        }

    };

    /**
     *
     * @param cellId id of a cell
     * @param edgeType type of the edge
     * @param opt indicates whether outbound or inbound links are counted
     * @returns {number} the connection count
     */
    getConnectionCount = function (cellId, edgeType, opt) {
        let links = _graph.getConnectedLinks(_graph.getCell(cellId), opt);
        let count = 0;
        links.forEach(function (l) {
            if (l.attributes.mReference === edgeType) {
                count += 1;
            }
        });
        return count
    };

    /**
     * Provide publicly avaliable functions und variables.
     */
    return {
        init: init,
        focusElement: focusElement,
        lostFocus: lostFocus,
        canSetLink: canSetLink,
        replaceLink: replaceLink
    };
})();<commit_msg>#125 - handleAddedCell adapt to V2  works now correctly - replaceLink adapt to V2  works now correctly - handleRemovedCell adapt to V2  works now correctly<commit_after>import $ from 'jquery';
import joint from 'jointjs';
import GeneratorFactory from "../generator/GeneratorFactory";
// DEBUG
// import {validator} from '../generator/temporary/old/validator'

/**
 * linkTypeSelector provides functions for creating and managing the context menu to choose a link type from.
 *
 * @returns {{init: Function, focusElement: Function, lostFocus: Function, canSetLink: Function, replaceLink: Function}}
 */
export default (function linkTypeSelector() {
    'use strict';

    let _menu = null;
    let _focusedElement = null;
    let _graph = null;
    let _paper = null;
    let _linkID = null;
    let _canSetLink = false;

    let init;
    let createMenu;
    let itemMouseUp;
    let destroyMenu;
    let showMenu;
    let focusElement;
    let lostFocus;
    let canSetLink;
    let replaceLink;
    let registerListeners;
    let handleAddedCell;
    let handleRemovedCell;
    let getConnectionCount;
    let connectionDefinitionGenerator;

    const removeFocus = () => {
        if (_focusedElement) {
            joint.V(_paper.findViewByModel(_focusedElement).el).removeClass('linking-allowed');
            joint.V(_paper.findViewByModel(_focusedElement).el).removeClass('linking-unallowed');
            _focusedElement = null;
        }
    };

    /**
     * Has to be called once before using the other methods!
     *
     * @param graph
     */
    init = function init(graph, paper) {
        _graph = graph;
        _paper = paper;
        connectionDefinitionGenerator = GeneratorFactory.connectionDefinition;
        registerListeners();
    };

    /**
     * Creates the context menu with the specified elements.
     *
     * @param {array} elements - An array of strings which will be the contents of the context menu.
     */
    createMenu = function createMenu(elements) {

        if (_menu) {
            destroyMenu();
        }

        let menu = '';
        elements = elements || [];

        menu += '
';

        if (elements.length > 0) {

            elements.forEach(function (name, index) {
                menu += '
';
                menu += name;
                menu += '
';
            });
            joint.V(_paper.findViewByModel(_focusedElement).el).addClass('linking-allowed');

        } else {
            menu += '
';
            menu += 'These elements are not linkable!';
            menu += '
';
            joint.V(_paper.findViewByModel(_focusedElement).el).addClass('linking-unallowed');
            canSetLink(false);
        }

        menu += '
';

        $('#contextMenuContainer').html(menu);

        // set handler
        elements.forEach(function (name, index) {
            let item = $('#menuItem' + index);

            item.mouseup(function () {
                itemMouseUp(name);
            });

            item.mouseenter(function () {
                canSetLink(true);
                // item.css('background', '#27ae60');
            });

            item.mouseleave(function () {
                // item.css('background', '#2c3e50');
            });
        });

        _menu = $('#contextMenu');

        _menu.mouseleave(function () {
            canSetLink(false);
        });

        $('body').mouseup(function () {
            removeFocus();
            destroyMenu();
        });

    };

    /**
     * Is called on mouseup on a link type in the context menu.
     *
     * @param linkName
     */
    itemMouseUp = function itemMouseUp(linkName) {
        let link = _graph.getCell(_linkID);
        link.attributes.subtype = linkName;
        destroyMenu();
    };

    /**
     * Is called when user is creating a link and is hovering over an element.
     *
     * @param {string} linkID - ID of the link that the user is just creating.
     * @param {object} eventTargetModel - The element over which the mouse is hovering.
     * @param {int} x - X-coordinate of the mouse.
     * @param {int} y - Y-coordinate of the mouse.
     */
    focusElement = function focusElement(linkID, eventTargetModel, x, y) {
        let link;
        let eventSourceID;
        let eventSourceType;
        let menuList;

        removeFocus();
        if (!eventTargetModel) {
            return;
        }

        _linkID = linkID;
        _focusedElement = eventTargetModel;

        link = _graph.getCell(linkID);

        if (link.attributes.source.id) {

            // event target === link target
            eventSourceID = link.attributes.source.id;
            eventSourceType = _graph.getCell(eventSourceID).attributes.nodeName;

        } else if (link.attributes.target.id) {

            // event target === link source
            eventSourceID = link.attributes.target.id;
            eventSourceType = _graph.getCell(eventSourceID).attributes.nodeName;

        }

        let eventTarget = eventTargetModel.attributes.nodeName;

        menuList = GeneratorFactory.validator.getValidEdges(eventSourceType, eventTarget);

        //DEBUG
        // let menuList_OLD = validator.getValidEdges(eventSourceType, eventTarget);

        createMenu(menuList);
        showMenu(x, y);
    };

    /**
     * Is called when the focus for the element is lost (not hovering over the element anymore).
     *
     * @param newFocusedElement
     */
    lostFocus = function lostFocus(newFocusedElement) {
        let element = $(newFocusedElement);

        if (element.hasClass('contextItem') || element.hasClass('menuItem') || element.hasClass('menuTable')) {
            return;
        }
        removeFocus();
        destroyMenu();
    };

    /**
     * Destroys the current context menu.
     */
    destroyMenu = function destroyMenu() {
        if (_menu) {
            _menu.remove();
            _menu = null;
            $('#contextMenuContainer').empty();
        }
    };

    /**
     * Shows the current context menu near the specified coordinates.
     * @param {int} x
     * @param {int} y
     */
    showMenu = function showMenu(x, y) {
        y -= 20;
        _menu.css('top', y + 'px');
        _menu.css('left', x + 'px');
        _menu.css('display', 'inline');
    };

    /**
     * Getter and setter for _canSetLink.
     * @param {boolean} [canSet]
     * @returns {boolean}
     */
    canSetLink = function canSetLink(canSet) {
        if (typeof canSet === 'boolean') {
            _canSetLink = canSet;
        }

        return _canSetLink;
    };

    /**
     * Once the link is drawn, its type is some default link type,
     * so it has to be replaced with a link of the correct type.
     * Also check if bounds of link source/target are violated through
     * the added link. Highlighting is added if this is the case
     * @param {object} link - The link that has just been drawn.
     * @returns {object} The new link.

     */

    replaceLink = function replaceLink(link) {
        let cell = _graph.getCell(_linkID);

        let edgeData = GeneratorFactory.validator.getEdgeData(link.attributes.subtype);

        let edgeType = edgeData.type;
        let targetId = link.attributes.target.id;
        let sourceId = link.attributes.source.id;

        let sourceMClass = _graph.getCell(sourceId);
        let targetMClass = _graph.getCell(targetId);

        let sourceresult = GeneratorFactory.validator.inputMatrix(sourceMClass.attributes.nodeName);
        let targetresult = GeneratorFactory.validator.inputMatrix(targetMClass.attributes.nodeName);

        link.prop('placings', connectionDefinitionGenerator.getPlacings(edgeData.style));
        // link.prop('placings', getPlacings(edgeData.style));
        link.prop('sourceAttribute', edgeData.from);
        link.prop('targetAttribute', edgeData.to);
        // edge type needs to be set before getting connection count
        // otherwise current link is ignored
        link.prop('mReference', edgeType);
        // link.prop('labels', getLabels(edgeData.style));
        link.prop('labels', connectionDefinitionGenerator.getLabels(edgeData.style));

        let sourceTypes = Object.keys(sourceresult);
        let targetTypes = Object.keys(targetresult);

        var sourceSollWert = sourceTypes.reduce((result,type) => {
            result[type] = sourceresult[type];
            return result;
        },{});

        var sourceIstWert = sourceTypes.reduce((result,type) => {
            result[type] = getNowConnectionCount(sourceId,type);// type in und outs IST
            return result;
        },{});

        var targetSollWert = targetTypes.reduce((result,type) => {
            result[type] = targetresult[type];
            return result;
        },{});

        var targetIstWert = targetTypes.reduce((result,type) => {
            result[type] = getNowConnectionCount(targetId,type);
            return result;
        },{});

        let validSourceNode = checkValidmbo(sourceSollWert,sourceIstWert, sourceTypes);
        let validTargetNode = checkValidmbo(targetSollWert,targetIstWert, targetTypes);

        setPaperElementValidation(validSourceNode,sourceId, _paper);
        setPaperElementValidation(validTargetNode,targetId, _paper);

        let clone = link.clone();
        clone.attributes.attrs = connectionDefinitionGenerator.getConnectionStyle(edgeData.style);
        // clone.attributes.attrs = getConnectionStyle(edgeData.style);

        console.log(clone.attributes.attrs);
        clone.prop('styleSet', true);
        link.remove();
        _graph.addCell(clone);
        destroyMenu();
        return cell;
    };

    function setPaperElementValidation(validNodeResults, id, paper){
        if(checkAllTrue(validNodeResults)) {
            joint.V(paper.findViewByModel(id).el).removeClass('invalid-edges');
        }
    }

    function checkAllTrue(validCheckResult) {
       return !Object.values(validCheckResult).includes(false);
    }

    function isInfinity(num) {
        if(num === -1){
            return Infinity;
        }
        return num;
    }

    function isBetween(n, a, b) {
        a = isInfinity(a);
        b = isInfinity(b);

        return n >= a && n <=b;
    }

    function checkValidmbo(soll, ist, types) {
        return types.reduce((result,type) => {
            result[type] = isBetween(ist[type].outgoing,soll[type].sourceLowerBounds,soll[type].sourceUpperBounds);
                //&& isBetween(ist[type].ingoing,soll[type].targetLowerBounds,soll[type].targetUpperBounds);
            return result;
        },{});
    }

    function getNowConnectionCount(id, edgeType){

    let ingoingCount = getConnectionCount(id, edgeType.charAt(0).toUpperCase()+edgeType.slice(1),  {inbound: true});
    let outgoingCount = getConnectionCount(id, edgeType.charAt(0).toUpperCase()+edgeType.slice(1), {outbound: true});

    return {[edgeType]: ""+edgeType,"outgoing": ""+outgoingCount,"ingoing":  ""+ingoingCount};
    }

    /**
     * register listeners for added and removed cells
     */
    registerListeners = function () {
        _graph.on('add', handleAddedCell);
        _graph.on('remove', handleRemovedCell);
    };

    /**
     * Checks if the elements lower bounds are violated, if this is the case a highlighting is added
     * @param cell the newly added cell
     */
    handleAddedCell = function (cell) {
        if (cell.isLink()) return;
        let inoutMatrix = GeneratorFactory.validator.inputMatrix(cell.attributes.nodeName);
        checkTypeBounds(inoutMatrix, cell);
    };

    function checkTypeBounds(inoutMatrix, cell) {
        let inputs = [];
        let outputs = [];

        for (let inEdge in inoutMatrix) {
            if (Object.prototype.hasOwnProperty.call(inoutMatrix, inEdge)) {
                if (inoutMatrix[inEdge].sourceLowerBounds > 0) {
                    inputs.push({'mReferenceName': inEdge, 'lowerBound': inoutMatrix[inEdge].sourceLowerBounds});
                }
            }
        }

        for (let outEdge in inoutMatrix) {
            if (Object.prototype.hasOwnProperty.call(inoutMatrix, outEdge)) {
                if (inoutMatrix[outEdge].targetLowerBounds > 0) {
                    outputs.push({'mReferenceName': outEdge, 'lowerBound': inoutMatrix[outEdge].targetLowerBounds});
                }
            }
        }

        if (inputs.length != 0 || outputs.length != 0) {
            joint.V(_paper.findViewByModel(cell).el).addClass('invalid-edges');
        }
    }

    /**
     * Checks if bounds are violated through a removed link,
     * highlighting is added if this is the case
     * @param link the removed link
     */
    handleRemovedCell = function (link) {
        // check if style is set, otherwise the removed dummy link will influence the counters
        // this also ignores all Elements
        if (!link.attributes.styleSet) return;

        let edgeData = GeneratorFactory.validator.getEdgeData(link.attributes.subtype);
        let targetId = link.attributes.target.id;
        let sourceId = link.attributes.source.id;

        let sourceMClass = _graph.getCell(link.attributes.source.id);
        let targetMClass = _graph.getCell(link.attributes.target.id);

        let sourceresult = GeneratorFactory.validator.inputMatrix(sourceMClass.attributes.nodeName);
        let targetresult = GeneratorFactory.validator.inputMatrix(targetMClass.attributes.nodeName);

        let minInputs = null;
        let minOutputs = null;
        let maxInputs = null;
        let maxOutputs = null;

        let sourceinoutMatrix = GeneratorFactory.validator.inputMatrix(sourceMClass.attributes.nodeName);
        checkTypeBounds(sourceinoutMatrix, sourceMClass);

        let targetinoutMatrix = GeneratorFactory.validator.inputMatrix(targetMClass.attributes.nodeName);
        checkTypeBounds(targetinoutMatrix, sourceMClass);

        /*maxInputs = result[link.attributes.subtype].targetUpperBounds;
        maxOutputs = result[link.attributes.subtype].sourceUpperBounds;
        minInputs = result[link.attributes.subtype].targetLowerBounds;
        minOutputs = result[link.attributes.subtype].sourceLowerBounds;*/

        /*let ingoingTargetCount = getConnectionCount(link.attributes.target.id, edgeType, {inbound: true});
        let outgoingSourceCount = getConnectionCount(link.attributes.source.id, edgeType, {outbound: true});
        let minInUnderstepped = false;
        let minOutUnderstepped = false;

        if (minInputs > ingoingTargetCount) {
            minInUnderstepped = true;
            joint.V(_paper.findViewByModel(link.attributes.target.id).el).addClass('invalid-edges');
        }

        if (maxInputs != -1 && maxInputs >= ingoingTargetCount && !minInUnderstepped) {
            joint.V(_paper.findViewByModel(link.attributes.target.id).el).removeClass('invalid-edges');
        }

        if (minOutputs > outgoingSourceCount) {
            minOutUnderstepped = true;
            joint.V(_paper.findViewByModel(link.attributes.source.id).el).addClass('invalid-edges');
        }
        if (maxOutputs != -1 && maxOutputs >= outgoingSourceCount && !minOutUnderstepped) {
            joint.V(_paper.findViewByModel(link.attributes.source.id).el).removeClass('invalid-edges');
        }*/

        let sourceTypes = Object.keys(sourceresult);
        let targetTypes = Object.keys(targetresult);

        var sourceSollWert = sourceTypes.reduce((result,type) => {
            result[type] = sourceresult[type];
            return result;
        },{});

        var sourceIstWert = sourceTypes.reduce((result,type) => {
            result[type] = getNowConnectionCount(sourceId,type);// type in und outs IST
            return result;
        },{});

        var targetSollWert = targetTypes.reduce((result,type) => {
            result[type] = targetresult[type];
            return result;
        },{});

        var targetIstWert = targetTypes.reduce((result,type) => {
            result[type] = getNowConnectionCount(targetId,type);
            return result;
        },{});

        let validSourceNode = checkValidmbo(sourceSollWert,sourceIstWert, sourceTypes);
        let validTargetNode = checkValidmbo(targetSollWert,targetIstWert, targetTypes);

        setPaperElementValidation(validSourceNode,sourceId, _paper);
        setPaperElementValidation(validTargetNode,targetId, _paper);

    };

    /**
     *
     * @param cellId id of a cell
     * @param edgeType type of the edge
     * @param opt indicates whether outbound or inbound links are counted
     * @returns {number} the connection count
     */
    getConnectionCount = function (cellId, edgeType, opt) {
        let links = _graph.getConnectedLinks(_graph.getCell(cellId), opt);
        let count = 0;
        links.forEach(function (l) {
            if (l.attributes.mReference === edgeType) {
                count += 1;
            }
        });
        return count
    };

    /**
     * Provide publicly avaliable functions und variables.
     */
    return {
        init: init,
        focusElement: focusElement,
        lostFocus: lostFocus,
        canSetLink: canSetLink,
        replaceLink: replaceLink
    };
})();<|endoftext|>


<commit_before>"use strict";

var redis = require('redis'), clients;
var Statsd = require('node-statsd').StatsD;

function Run() {

  var self = this;

  var node_f, Node, node, def, opts;

  // worker node to run
  node_f = process.argv[2];
  this.def = def = JSON.parse(process.argv[3]);
  this.opts = opts = JSON.parse(process.argv[4]);

  if (typeof opts.redis === 'undefined') {
    opts.redis = {
      host: '127.0.0.1',
      port: 6379
    };
  }

  clients = {
    sub: redis.createClient(opts.redis),
    pop: redis.createClient(opts.redis),
    out: redis.createClient(opts.redis)
  };

  // dummy for when statsd not enabled
  var stat = function(key){};

  var statsdClient;
  var statsKey;

  // if enabled, configure statsd
  if ( typeof opts.statsd !== 'undefined' ) {

    statsKey = opts.statsd.prefix ? (opts.statsd.prefix + '.') : '';
    statsKey += def.key;

    statsdClient = new Statsd(
      opts.statsd.host,
      opts.statsd.port
    );

    stat = function(key){
      statsdClient.increment(statsKey + '.' + key);
    };

  }

  Node = require(node_f);
  node = new Node(def);

  if ( def.output === undefined ) {
    def.output = [];
  }

  if ( def.outputs === undefined ) {
    def.outputs = {};
  }

  // connected pipes will be subscribed to this
  node.on('message', function(data){

    var outlet = data.outlet || false;
    var msg = data.message;

    if ( ! outlet ) {
      // no outlet specified? send it out the default
      self.def.output.forEach( function(x){
        self.output(clients, x, msg);
      });
      stat('output');
    } else if (self.def.outputs[outlet] !== undefined) {
      // outlet specified and exists, send it out that one
      self.output(clients, self.def.outputs[outlet], msg);
      stat('outputs.' + outlet);
    }

  });

  // receive command from controlling process
  process.on('message', function(data){
    if ( data === 'STOP' ) {
      for(var i in clients){
        clients[i].quit();
      }
      node.stop( function() {
        process.exit();
      });
    }
  });

  node.on('count', function(counts){
    clients.out.publish('stats:' + def.key, JSON.stringify(counts));
  });

  // pipes will push to this redis channel. process must execute the
  // callback to signal it's ready to process another message

  this.brpop = function() {
    var self = this;
    clients.pop.brpop(this.lkeys, function(err, reply){
      var msg = JSON.parse(reply[1]);
      node.process(msg, function(err){
        stat('input');
        //stat('inputs.' + channel);
        self.brpop();
      });
    });
  };

  // set up pipes them run the node

  var run = function() {
    node.run(function(err){
      process.send('STARTED');
    });
  };

  if ( def.input === undefined ) {
    return run();
  }

  clients.sub.on('message', function(channel, msg){
    msg = JSON.parse(msg);
    node.process(msg, function(err, res){
      stat('input');
      stat('inputs.' + channel);
    });
  });

  var remaining = 0;
  var complete = function(){
    remaining --;
    if (remaining === 0) {
      run();
    }
  };

  if ( ! Array.isArray(def.input)) {
    def.input = [def.input];
  }

  // subscribe to fan-out pipes (redis pubsub)

  this.lkeys = [];
  def.input.forEach(function(x){
    if ( typeof x.mode === 'undefined' ) {
      // redis takes time to subscribe
      remaining ++;
      clients.sub.subscribe(x.key, complete);
      return;
    }

    // build list of round-robin keys.
    if ( x.mode === 'round-robin' ) {
      self.lkeys.push(x.key);
      return;
    }
  });

  // last element is polling timeout
  if (this.lkeys.length > 0) {
    this.lkeys.push(60);
    this.brpop();
  }

  remaining ++;
  complete();

}

Run.prototype.output = function(clients, pipe, msg) {

  // default, fan-out. clients will subscribe
  if ( typeof pipe.mode === 'undefined' ) {
    clients.out.publish(
      pipe.key,
      JSON.stringify(msg)
    );
    return;
  }

  // round-robin. clients will brpop
  if ( pipe.mode === 'round-robin' ) {
    clients.out.lpush(
      pipe.key,
      JSON.stringify(msg)
    );
    return;
  }

  // no valid pipe type specified

};

module.exports = new Run();
<commit_msg>Added some error-catching to round-robin pipe brpop<commit_after>"use strict";

var redis = require('redis'), clients;
var Statsd = require('node-statsd').StatsD;

function Run() {

  var self = this;

  var node_f, Node, node, def, opts;

  // worker node to run
  node_f = process.argv[2];
  this.def = def = JSON.parse(process.argv[3]);
  this.opts = opts = JSON.parse(process.argv[4]);

  if (typeof opts.redis === 'undefined') {
    opts.redis = {
      host: '127.0.0.1',
      port: 6379
    };
  }

  clients = {
    sub: redis.createClient(opts.redis),
    pop: redis.createClient(opts.redis),
    out: redis.createClient(opts.redis)
  };

  // dummy for when statsd not enabled
  var stat = function(key){};

  var statsdClient;
  var statsKey;

  // if enabled, configure statsd
  if ( typeof opts.statsd !== 'undefined' ) {

    statsKey = opts.statsd.prefix ? (opts.statsd.prefix + '.') : '';
    statsKey += def.key;

    statsdClient = new Statsd(
      opts.statsd.host,
      opts.statsd.port
    );

    stat = function(key){
      statsdClient.increment(statsKey + '.' + key);
    };

  }

  Node = require(node_f);
  node = new Node(def);

  if ( def.output === undefined ) {
    def.output = [];
  }

  if ( def.outputs === undefined ) {
    def.outputs = {};
  }

  // connected pipes will be subscribed to this
  node.on('message', function(data){

    var outlet = data.outlet || false;
    var msg = data.message;

    if ( ! outlet ) {
      // no outlet specified? send it out the default
      self.def.output.forEach( function(x){
        self.output(clients, x, msg);
      });
      stat('output');
    } else if (self.def.outputs[outlet] !== undefined) {
      // outlet specified and exists, send it out that one
      self.output(clients, self.def.outputs[outlet], msg);
      stat('outputs.' + outlet);
    }

  });

  // receive command from controlling process
  process.on('message', function(data){
    if ( data === 'STOP' ) {
      for(var i in clients){
        clients[i].quit();
      }
      node.stop( function() {
        process.exit();
      });
    }
  });

  node.on('count', function(counts){
    clients.out.publish('stats:' + def.key, JSON.stringify(counts));
  });

  // pipes will push to this redis channel. process must execute the
  // callback to signal it's ready to process another message

  this.brpop = function() {
    var self = this;
    clients.pop.brpop(this.lkeys, function(err, reply){
      if (!reply){
        // brpop timed out
        self.brpop();
        return;
      }
      var msg = false;
      console.log(reply);
      if(!err){
        try {
          msg = JSON.parse(reply[1]);
        } catch (e) {
          // bad json
          self.brpop();
          return;
        }
        node.process(msg, function(err){
          self.brpop();
          stat('input');
        });
      }
    });
  };

  // set up pipes them run the node

  var run = function() {
    node.run(function(err){
      process.send('STARTED');
    });
  };

  if ( def.input === undefined ) {
    return run();
  }

  clients.sub.on('message', function(channel, msg){
    msg = JSON.parse(msg);
    node.process(msg, function(err, res){
      stat('input');
      stat('inputs.' + channel);
    });
  });

  var remaining = 0;
  var complete = function(){
    remaining --;
    if (remaining === 0) {
      run();
    }
  };

  if ( ! Array.isArray(def.input)) {
    def.input = [def.input];
  }

  // subscribe to fan-out pipes (redis pubsub)

  this.lkeys = [];
  def.input.forEach(function(x){
    if ( typeof x.mode === 'undefined' ) {
      // redis takes time to subscribe
      remaining ++;
      clients.sub.subscribe(x.key, complete);
      return;
    }

    // build list of round-robin keys.
    if ( x.mode === 'round-robin' ) {
      self.lkeys.push(x.key);
      return;
    }
  });

  // last element is polling timeout
  if (this.lkeys.length > 0) {
    this.lkeys.push(60);
    this.brpop();
  }

  remaining ++;
  complete();

}

Run.prototype.output = function(clients, pipe, msg) {

  // default, fan-out. clients will subscribe
  if ( typeof pipe.mode === 'undefined' ) {
    clients.out.publish(
      pipe.key,
      JSON.stringify(msg)
    );
    return;
  }

  // round-robin. clients will brpop
  if ( pipe.mode === 'round-robin' ) {
    clients.out.lpush(
      pipe.key,
      JSON.stringify(msg)
    );
    return;
  }

  // no valid pipe type specified

};

module.exports = new Run();
<|endoftext|>


<commit_before>var _ = require('lodash'),
    EventEmitter = require('eventemitter3'),
    runtime = require('postman-runtime'),
    Collection = require('postman-collection').Collection,

    runtimeEvents = ['start', 'beforeIteration', 'beforeItem', 'beforePrerequest', 'prerequest',
        'beforeRequest', 'request', 'beforeTest', 'test', 'item', 'iteration', 'beforeScript', 'script', 'console',
        'exception', 'done'];

/**
 * @param {Object} options
 * @param {Collection|Object} options.collection
 * @param {Object} options.environment
 * @param {Array|String} options.reporters
 * @param {Function} callback
 *
 * @returns {EventEmitter}
 */
module.exports = function (options, callback) {
    // validate all options. it is to be notet that `options` parameter is option and is polymorphic
    (!callback && _.isFunction(options)) && (
        (callback = options),
        (options = {})
    );
    options = _.isObject(options) ? _.clone(options) : {}; // we initialise this as object to avoid needless validations
    !_.isFunction(callback) && (callback = _.noop);

    var emitter = new EventEmitter(), // @todo: create a new inherited constructor
        runner = new runtime.Runner(),
        collection = options.collection,
        environment,
        globals;

    // ensure that the collection option is present before starting a run
    if (!_.isObject(options.collection)) {
        callback(new Error('newman: expecting a collection to run'));
        return emitter;
    }

    // create a collection in case it is not one. user can send v2 JSON as a source and that will be converted
    // to a collection
    if (_.isPlainObject(options.collection) && !Collection.isCollection(options.collection)) {
        options.collection = new Collection(options.collection);
    }

    // Create a Plain object from environments
    options.environment && (environment = (function (rawEnv) {
        // Cloud API
        rawEnv.environment && (rawEnv = rawEnv.environment);

        // Exported files
        if (rawEnv.values) {
            return _.object(_.map(rawEnv.values, 'key'), _.map(rawEnv.values, 'value'));
        }

        // Plain object
        return rawEnv;
    }(options.environment)));

    options.globals && (globals = function (rawGlobals) {
        // Cloud API
        rawGlobals.globals && (rawGlobals = rawGlobals.globals);

        // Exported global files
        _.isArray(globals) && (globals = _.object(_.map(rawGlobals, 'key'), _.map(rawGlobals, 'value')));

        return globals;
    });

    options.collection && runner.run(options.collection, {
        abortOnError: options.abortOnError,
        environment: environment,
        globals: globals,
    }, function (err, run) {
        var callbacks = {},
            // ensure that the reporter option type polymorphism is handled
            reporters = _.isString(options.reporters) ? [options.reporters] : options.reporters;

        // emit events for all the callbacks triggered by the runtime
        runtimeEvents.forEach(function (eventName) {
            callbacks[eventName] = emitter.emit.bind(emitter, eventName);
        });

        // override the `done` event to fire the end callback
        callbacks.done = function (err) { // @todo - do some meory cleanup here?
            callback(err);
            emitter.emit('done', err); // we now trigger the actual done event which we had overridden
        };

        // initialise all the reporters
        _.isArray(reporters) && _.each(reporters, function (reporterName) {
            // @todo validate reporter name to exist
            // @todo look up for node module "newman-reporter-" as reporters if one is not found locally
            // @todo sanitise the reporter name for security reasons
            (emitter.reporters || (emitter.reporters = {}))[reporterName] =
                new (require('./reporters/' + reporterName))(emitter, options);
        });

        // we ensure that everything is async to comply with event paradigm and start the run
        setImmediate(function () {
            run.start(callbacks);
        });
    });

    return emitter;
};
<commit_msg>fixed linting issues<commit_after>var _ = require('lodash'),
    EventEmitter = require('eventemitter3'),
    runtime = require('postman-runtime'),
    Collection = require('postman-collection').Collection,

    runtimeEvents = ['start', 'beforeIteration', 'beforeItem', 'beforePrerequest', 'prerequest',
        'beforeRequest', 'request', 'beforeTest', 'test', 'item', 'iteration', 'beforeScript', 'script', 'console',
        'exception', 'done'];

/**
 * @param {Object} options
 * @param {Collection|Object} options.collection
 * @param {Object} options.environment
 * @param {Array|String} options.reporters
 * @param {Function} callback
 *
 * @returns {EventEmitter}
 */
module.exports = function (options, callback) {
    // validate all options. it is to be notet that `options` parameter is option and is polymorphic
    (!callback && _.isFunction(options)) && (
        (callback = options),
        (options = {})
    );
    options = _.isObject(options) ? _.clone(options) : {}; // we initialise this as object to avoid needless validations
    !_.isFunction(callback) && (callback = _.noop);

    var emitter = new EventEmitter(), // @todo: create a new inherited constructor
        runner = new runtime.Runner(),
        environment,
        globals;

    // ensure that the collection option is present before starting a run
    if (!_.isObject(options.collection)) {
        callback(new Error('newman: expecting a collection to run'));
        return emitter;
    }

    // create a collection in case it is not one. user can send v2 JSON as a source and that will be converted
    // to a collection
    if (_.isPlainObject(options.collection) && !Collection.isCollection(options.collection)) {
        options.collection = new Collection(options.collection);
    }

    // Create a Plain object from environments
    options.environment && (environment = (function (rawEnv) {
        // Cloud API
        rawEnv.environment && (rawEnv = rawEnv.environment);

        // Exported files
        if (rawEnv.values) {
            return _.object(_.map(rawEnv.values, 'key'), _.map(rawEnv.values, 'value'));
        }

        // Plain object
        return rawEnv;
    }(options.environment)));

    options.globals && (globals = function (rawGlobals) {
        // Cloud API
        rawGlobals.globals && (rawGlobals = rawGlobals.globals);

        // Exported global files
        _.isArray(globals) && (globals = _.object(_.map(rawGlobals, 'key'), _.map(rawGlobals, 'value')));

        return globals;
    });

    options.collection && runner.run(options.collection, {
        abortOnError: options.abortOnError,
        environment: environment,
        globals: globals
    }, function (err, run) {
        var callbacks = {},
            // ensure that the reporter option type polymorphism is handled
            reporters = _.isString(options.reporters) ? [options.reporters] : options.reporters;

        // emit events for all the callbacks triggered by the runtime
        runtimeEvents.forEach(function (eventName) {
            callbacks[eventName] = emitter.emit.bind(emitter, eventName);
        });

        // override the `done` event to fire the end callback
        callbacks.done = function (err) { // @todo - do some meory cleanup here?
            callback(err);
            emitter.emit('done', err); // we now trigger the actual done event which we had overridden
        };

        // initialise all the reporters
        _.isArray(reporters) && _.each(reporters, function (reporterName) {
            // @todo validate reporter name to exist
            // @todo look up for node module "newman-reporter-" as reporters if one is not found locally
            // @todo sanitise the reporter name for security reasons
            (emitter.reporters || (emitter.reporters = {}))[reporterName] =
                new (require('./reporters/' + reporterName))(emitter, options);
        });

        // we ensure that everything is async to comply with event paradigm and start the run
        setImmediate(function () {
            run.start(callbacks);
        });
    });

    return emitter;
};
<|endoftext|>


<commit_before>var PRNG = require('burtleprng');

// TODO: add a maxSize parameter somehow.
// Returns true if all tests passed.
function run(func, numTests, seed) {
  // Mess with arguments. Varargs are generator constructors,
  // and numTests and seed are optional so may also be
  // generator constructors.
  // (However, numTests must be provided if seed is.)
  var generators = [].slice.call(arguments, 3);

  var defaultSeed = Date.now();
  if (typeof seed === 'function') {
    generators.unshift(seed);
    seed = defaultSeed;
  } else if (typeof seed === 'undefined') {
    seed = defaultSeed;
  } else if (typeof seed !== 'number') {
    throw new TypeError('Seed must be a number');
  }
  seed &= 0xffffffff;

  var DEFAULT_NUM_TESTS = 100;
  if (typeof numTests === 'function') {
    generators.unshift(numTests);
    numTests = DEFAULT_NUM_TESTS;
  } else if (typeof numTests === 'undefined') {
    // This suggests your tests use no generators at all,
    // which seems an unlikely case, but whatever.
    numTests = DEFAULT_NUM_TESTS;
  } else if (typeof numTests !== 'number' || numTests < 1) {
    throw new TypeError('numTests must be a positive number');
  }
  numTests >>>= 0;

  return _run(func, numTests, seed, generators);
}

function _run(func, numTests, seed, gens) {
  var prng = new PRNG(seed);
  var somethingFailed = false;

  for (var x = 0; x < numTests; x++) {
    var size = Math.floor(x/2) + 1;
    var values = gens.map(function(gen) {
      return gen(prng, size);
    });
    if (!func.apply(null, values)) {
      console.warn('Test failed! Test case:', values);
      somethingFailed = true;
    }
  }

  return !somethingFailed;
}

module.exports = run;
<commit_msg>Fix out of date comment<commit_after>var PRNG = require('burtleprng');

// TODO: add a maxSize parameter somehow.
// Returns true if all tests passed.
function run(func, numTests, seed) {
  // Mess with arguments. Varargs are generators,
  // and numTests and seed are optional so may also be
  // generators.
  // (However, numTests must be provided if seed is.)
  var generators = [].slice.call(arguments, 3);

  var defaultSeed = Date.now();
  if (typeof seed === 'function') {
    generators.unshift(seed);
    seed = defaultSeed;
  } else if (typeof seed === 'undefined') {
    seed = defaultSeed;
  } else if (typeof seed !== 'number') {
    throw new TypeError('seed must be a number');
  }
  seed &= 0xffffffff;

  var DEFAULT_NUM_TESTS = 100;
  if (typeof numTests === 'function') {
    generators.unshift(numTests);
    numTests = DEFAULT_NUM_TESTS;
  } else if (typeof numTests === 'undefined') {
    // This suggests your tests use no generators at all,
    // which seems an unlikely case, but whatever.
    numTests = DEFAULT_NUM_TESTS;
  } else if (typeof numTests !== 'number' || numTests < 1) {
    throw new TypeError('numTests must be a positive number');
  }
  numTests >>>= 0;

  return _run(func, numTests, seed, generators);
}

function _run(func, numTests, seed, gens) {
  var prng = new PRNG(seed);
  var somethingFailed = false;

  for (var x = 0; x < numTests; x++) {
    var size = Math.floor(x/2) + 1;
    var values = gens.map(function(gen) {
      return gen(prng, size);
    });
    if (!func.apply(null, values)) {
      console.warn('Test failed! Test case:', values);
      somethingFailed = true;
    }
  }

  return !somethingFailed;
}

module.exports = run;
<|endoftext|>


<commit_before>module.exports = {
  voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],

  configDefaults: {
      'closeTagOn </': true
  },

  activate: function(state) {
    var _this = this;

    atom.workspaceView.eachEditorView(function(editorView) {
        editorView.editor.buffer.on('changed', function(e) {
          _this.bufferChanged(e);
        });
    });

    return atom.workspaceView.command("tag:close-tag", (function(_this) {
      return function() {
        return _this.closeTag();
      };
    })(this));
  },

  closeTag: function() {
    var editor = atom.workspace.getActiveEditor();
    if (editor === undefined) {
      return;
    }

    var cursorPos = editor.getCursor().getBufferPosition();
    var buffer = editor.getTextInBufferRange([[0, 0], cursorPos]);
    editor.insertText(this.getClosingTag(buffer));
  },

  bufferChanged: function(e) {
    var editor = atom.workspace.getActiveEditor();

    if (editor === undefined || !atom.config.get('tag.closeTagOn </') || e.newText !== '/') {
      return;
    }

    var grammar = editor.getGrammar().name;
    if (
      grammar.length == 0 ||
      (grammar.indexOf('HTML') < 0 && grammar.indexOf('XML') < 0)
    ) {
      return;
    }

    var cursorPos = editor.getCursor().getBufferPosition();
    var translatedPos = cursorPos.translate([0, -2]);
    var lastTwo = editor.getTextInBufferRange([translatedPos, cursorPos]);

    if (lastTwo !== '</') {
      return;
    }

    var buffer = editor.getTextInBufferRange([[0, 0], translatedPos]);
    var closingTag = this.getClosingTag(buffer);
    if (closingTag.length > 0) {
      // Temporary workaround for a funny cursor bug
      setTimeout(function() {
        editor.insertText(closingTag.substring(2));
      }, 1);
    }
    return;
  },

  getClosingTag: function (str) {
    var _this = this;
    var tokens = str.split('<');

    if (tokens.length == 0) {
      return '';
    }
    tokens.reverse();

    var getTag = function (token) {
      var tag = token.substring(0, token.search('>')).trim();

      if (tag.substr(tag.length - 1) == '/') {
        return tag;
      } else if (tag.indexOf(' ') < 0) {
        return tag;
      } else {
        return tag.substring(0, token.search(' '));
      }
    }

    var isVoidElement = function(tag) {
      tag = tag.trim();
      var firstChar = tag.substr(0, 1);
      var lastChar = tag.substr(tag.length - 1);
      if (['?', '%'].indexOf(firstChar) >= 0) {
        return true;
      }
      if (['/'].indexOf(firstChar) >= 0) {
        return true;
      }

      tag = tag.toLowerCase();
      if (_this.voidElements.indexOf(tag) >= 0) {
        return true;
      }

      return false;
    }

    var skip = [];
    for (var i in tokens) {
      var token = tokens[i];
      var tag = getTag(token);
      if (tag !== undefined && tag.length > 0 && !isVoidElement(tag)) {
        if (tag.substring(0, 1) != '/') {
          if (skip.length === 0) {
            return '</' + tag + '>';
          } else if (skip[skip.length - 1] == tag) {
            skip.pop();
          }
        } else {
          skip.push(tag.substring(1));
        }
      }
    }
    return '';
  }
};
<commit_msg>Bug fix for mismatching of tag.<commit_after>module.exports = {
  voidElements: ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'],

  configDefaults: {
      'closeTagOn </': true
  },

  activate: function(state) {
    var _this = this;

    atom.workspaceView.eachEditorView(function(editorView) {
        editorView.editor.buffer.on('changed', function(e) {
          _this.bufferChanged(e);
        });
    });

    return atom.workspaceView.command("tag:close-tag", (function(_this) {
      return function() {
        return _this.closeTag();
      };
    })(this));
  },

  closeTag: function() {
    var editor = atom.workspace.getActiveEditor();
    if (editor === undefined) {
      return;
    }

    var cursorPos = editor.getCursor().getBufferPosition();
    var buffer = editor.getTextInBufferRange([[0, 0], cursorPos]);
    editor.insertText(this.getClosingTag(buffer));
  },

  bufferChanged: function(e) {
    var editor = atom.workspace.getActiveEditor();

    if (editor === undefined || !atom.config.get('tag.closeTagOn </') || e.newText !== '/') {
      return;
    }

    var grammar = editor.getGrammar().name;
    if (
      grammar.length == 0 ||
      (grammar.indexOf('HTML') < 0 && grammar.indexOf('XML') < 0)
    ) {
      return;
    }

    var cursorPos = editor.getCursor().getBufferPosition();
    var translatedPos = cursorPos.translate([0, -2]);
    var lastTwo = editor.getTextInBufferRange([translatedPos, cursorPos]);

    if (lastTwo !== '</') {
      return;
    }

    var buffer = editor.getTextInBufferRange([[0, 0], translatedPos]);
    var closingTag = this.getClosingTag(buffer);
    if (closingTag.length > 0) {
      // Temporary workaround for a funny cursor bug
      setTimeout(function() {
        editor.insertText(closingTag.substring(2));
      }, 1);
    }
    return;
  },

  getClosingTag: function (str) {
    var _this = this;
    var tokens = str.split('<');

    if (tokens.length == 0) {
      return '';
    }
    tokens.reverse();

    var getTag = function (token) {
      var tag = token.substring(0, token.search('>')).trim();

      if (tag.substr(tag.length - 1) == '/') {
        return tag;
      } else if (tag.indexOf(' ') < 0) {
        return tag;
      } else {
        return tag.substring(0, token.search(' '));
      }
    }

    var isVoidElement = function(tag) {
      tag = tag.trim();
      var firstChar = tag.substr(0, 1);
      var lastChar = tag.substr(tag.length - 1);
      if (['?', '%'].indexOf(firstChar) >= 0) {
        return true;
      }
      if (['/'].indexOf(lastChar) >= 0) {
        return true;
      }

      tag = tag.toLowerCase();
      if (_this.voidElements.indexOf(tag) >= 0) {
        return true;
      }

      return false;
    }

    var skip = [];
    for (var i in tokens) {
      var token = tokens[i];
      var tag = getTag(token);
      if (tag !== undefined && tag.length > 0 && !isVoidElement(tag)) {
        if (tag.substring(0, 1) != '/') {
          if (skip.length === 0) {
            return '</' + tag + '>';
          } else if (skip[skip.length - 1] == tag) {
            skip.pop();
          }
        } else {
          skip.push(tag.substring(1));
        }
      }
    }
    return '';
  }
};
<|endoftext|>


<commit_before>/*!
 * Tmp
 *
 * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
 *
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var
  fs     = require('fs'),
  path   = require('path'),
  os     = require('os'),
  crypto = require('crypto'),
  exists = fs.exists || path.exists,
  existsSync = fs.existsSync || path.existsSync,
  tmpDir = require('os-tmpdir'),
  _c     = require('constants');

/**
 * The working inner variables.
 */
var
  // store the actual TMP directory
  _TMP = tmpDir(),

  // the random characters to choose from
  RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',

  TEMPLATE_PATTERN = /XXXXXX/,

  DEFAULT_TRIES = 3,

  CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,

  DIR_MODE = 448 /* 0700 */,
  FILE_MODE = 384 /* 0600 */,

  // this will hold the objects need to be removed on exit
  _removeObjects = [],

  _gracefulCleanup = false,
  _uncaughtException = false;

/**
 * Random name generator based on crypto.
 * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
 *
 * @param {Number} howMany
 * @return {String}
 * @api private
 */
function _randomChars(howMany) {
  var
    value = [],
    rnd = null;

  // make sure that we do not fail because we ran out of entropy
  try {
    rnd = crypto.randomBytes(howMany);
  } catch (e) {
    rnd = crypto.pseudoRandomBytes(howMany);
  }

  for (var i = 0; i < howMany; i++) {
    value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
  }

  return value.join('');
}

/**
 * Checks whether the `obj` parameter is defined or not.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */
function _isUndefined(obj) {
  return typeof obj === 'undefined';
}

/**
 * Parses the function arguments.
 *
 * This function helps to have optional arguments.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _parseArguments(options, callback) {
  if (typeof options == 'function') {
    var
      tmp = options;
      options = callback || {};
      callback = tmp;
  } else if (typeof options == 'undefined') {
    options = {};
  }

  return [options, callback];
}

/**
 * Generates a new temporary name.
 *
 * @param {Object} opts
 * @returns {String}
 * @api private
 */
function _generateTmpName(opts) {
  if (opts.name) {
    return path.join(opts.dir || _TMP, opts.name);
  }

  // mkstemps like template
  if (opts.template) {
    return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
  }

  // prefix and postfix
  var name = [
    opts.prefix || 'tmp-',
    process.pid,
    _randomChars(12),
    opts.postfix || ''
  ].join('');

  return path.join(opts.dir || _TMP, name);
}

/**
 * Gets a temporary file name.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _getTmpName(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    return cb(new Error('Invalid tries'));

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    return cb(new Error('Invalid template provided'));

  (function _getUniqueName() {
    var name = _generateTmpName(opts);

    // check whether the path exists then retry if needed
    exists(name, function _pathExists(pathExists) {
      if (pathExists) {
        if (tries-- > 0) return _getUniqueName();

        return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
      }

      cb(null, name);
    });
  }());
}

/**
 * Synchronous version of _getTmpName.
 *
 * @param {Object} options
 * @returns {String}
 * @api private
 */
function _getTmpNameSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    throw new Error('Invalid tries');

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    throw new Error('Invalid template provided');

  do {
    var name = _generateTmpName(opts);
    if (!existsSync(name)) {
      return name;
    }
  } while (tries-- > 0);

  throw new Error('Could not get a unique tmp filename, max tries reached');
}

/**
 * Creates and opens a temporary file.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpFile(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

    opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create and open the file
    fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
      if (err) return cb(err);

      cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpFile.
 *
 * @param {Object} options
 * @returns {Object} object consists of name, fd and removeCallback
 * @api private
 */
function _createTmpFileSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

    opts.postfix = opts.postfix || '.tmp';

  var name = _getTmpNameSync(opts);
  var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);

  return {
    name : name,
    fd : fd,
    removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
  };
}

/**
 * Removes files and folders in a directory recursively.
 *
 * @param {String} root
 * @api private
 */
function _rmdirRecursiveSync(root) {
  var dirs = [root];

  do {
    var
      dir = dirs.pop(),
      canRemove = true,
      files = fs.readdirSync(dir);

    for (var i = 0, length = files.length; i < length; i++) {
      var
        file = path.join(dir, files[i]),
        stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories

      if (stat.isDirectory()) {
        canRemove = false;
        dirs.push(dir);
        dirs.push(file);
      } else {
        fs.unlinkSync(file);
      }
    }

    if (canRemove) {
      fs.rmdirSync(dir);
    }
  } while (dirs.length !== 0);
}

/**
 * Creates a temporary directory.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpDir(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create the directory
    fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
      if (err) return cb(err);

      cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpDir.
 *
 * @param {Object} options
 * @returns {Object} object consists of name and removeCallback
 * @api private
 */
function _createTmpDirSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

  var name = _getTmpNameSync(opts);
  fs.mkdirSync(name, opts.mode || DIR_MODE);

  return {
    name : name,
    removeCallback : _prepareTmpDirRemoveCallback(name, opts)
  };
}

/**
 * Prepares the callback for removal of the temporary file.
 *
 * @param {String} name
 * @param {int} fd
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpFileRemoveCallback(name, fd, opts) {
  var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
    fs.closeSync(fdPath[0]);
    fs.unlinkSync(fdPath[1]);
  }, [fd, name]);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Prepares the callback for removal of the temporary directory.
 *
 * @param {String} name
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpDirRemoveCallback(name, opts) {

  var removeCallback = _prepareRemoveCallback(
    opts.unsafeCleanup
        ? _rmdirRecursiveSync
        : fs.rmdirSync.bind(fs),
    name);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Creates a guarded function wrapping the removeFunction call.
 *
 * @param {Function} removeFunction
 * @param {Object} arg
 * @returns {Function}
 * @api private
 */
function _prepareRemoveCallback(removeFunction, arg) {
  var called = false;

  return function _cleanupCallback() {
    if (called) return;

    var index = _removeObjects.indexOf(removeFunction);
    if (index >= 0) {
      _removeObjects.splice(index, 1);
    }

    called = true;
    removeFunction(arg);
  };
}

/**
 * The garbage collector.
 *
 * @api private
 */
function _garbageCollector() {
  if (_uncaughtException && !_gracefulCleanup) {
    return;
  }

  for (var i = 0, length = _removeObjects.length; i < length; i++) {
    try {
      _removeObjects[i].call(null);
    } catch (e) {
      // already removed?
    }
  }
}

function _setGracefulCleanup() {
  _gracefulCleanup = true;
}

var version = process.versions.node.split('.').map(function (value) {
  return parseInt(value, 10);
});

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
  process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
    _uncaughtException = true;
    _garbageCollector();

    throw err;
  });
}

process.addListener('exit', function _exit(code) {
  if (code) _uncaughtException = true;
  _garbageCollector();
});

// exporting all the needed methods
module.exports.tmpdir = _TMP;
module.exports.dir = _createTmpDir;
module.exports.dirSync = _createTmpDirSync;
module.exports.file = _createTmpFile;
module.exports.fileSync = _createTmpFileSync;
module.exports.tmpName = _getTmpName;
module.exports.tmpNameSync = _getTmpNameSync;
module.exports.setGracefulCleanup = _setGracefulCleanup;
<commit_msg>fix JSHint error<commit_after>/*!
 * Tmp
 *
 * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu>
 *
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var
  fs     = require('fs'),
  path   = require('path'),
  os     = require('os'),
  crypto = require('crypto'),
  exists = fs.exists || path.exists,
  existsSync = fs.existsSync || path.existsSync,
  tmpDir = require('os-tmpdir'),
  _c     = require('constants');

/**
 * The working inner variables.
 */
var
  // store the actual TMP directory
  _TMP = tmpDir(),

  // the random characters to choose from
  RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',

  TEMPLATE_PATTERN = /XXXXXX/,

  DEFAULT_TRIES = 3,

  CREATE_FLAGS = _c.O_CREAT | _c.O_EXCL | _c.O_RDWR,

  DIR_MODE = 448 /* 0700 */,
  FILE_MODE = 384 /* 0600 */,

  // this will hold the objects need to be removed on exit
  _removeObjects = [],

  _gracefulCleanup = false,
  _uncaughtException = false;

/**
 * Random name generator based on crypto.
 * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript
 *
 * @param {Number} howMany
 * @return {String}
 * @api private
 */
function _randomChars(howMany) {
  var
    value = [],
    rnd = null;

  // make sure that we do not fail because we ran out of entropy
  try {
    rnd = crypto.randomBytes(howMany);
  } catch (e) {
    rnd = crypto.pseudoRandomBytes(howMany);
  }

  for (var i = 0; i < howMany; i++) {
    value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]);
  }

  return value.join('');
}

/**
 * Checks whether the `obj` parameter is defined or not.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */
function _isUndefined(obj) {
  return typeof obj === 'undefined';
}

/**
 * Parses the function arguments.
 *
 * This function helps to have optional arguments.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _parseArguments(options, callback) {
  if (typeof options == 'function') {
    var
      tmp = options;
      options = callback || {};
      callback = tmp;
  } else if (typeof options == 'undefined') {
    options = {};
  }

  return [options, callback];
}

/**
 * Generates a new temporary name.
 *
 * @param {Object} opts
 * @returns {String}
 * @api private
 */
function _generateTmpName(opts) {
  if (opts.name) {
    return path.join(opts.dir || _TMP, opts.name);
  }

  // mkstemps like template
  if (opts.template) {
    return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
  }

  // prefix and postfix
  var name = [
    opts.prefix || 'tmp-',
    process.pid,
    _randomChars(12),
    opts.postfix || ''
  ].join('');

  return path.join(opts.dir || _TMP, name);
}

/**
 * Gets a temporary file name.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api private
 */
function _getTmpName(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    return cb(new Error('Invalid tries'));

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    return cb(new Error('Invalid template provided'));

  (function _getUniqueName() {
    var name = _generateTmpName(opts);

    // check whether the path exists then retry if needed
    exists(name, function _pathExists(pathExists) {
      if (pathExists) {
        if (tries-- > 0) return _getUniqueName();

        return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name));
      }

      cb(null, name);
    });
  }());
}

/**
 * Synchronous version of _getTmpName.
 *
 * @param {Object} options
 * @returns {String}
 * @api private
 */
function _getTmpNameSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0],
    tries = opts.tries || DEFAULT_TRIES;

  if (isNaN(tries) || tries < 0)
    throw new Error('Invalid tries');

  if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
    throw new Error('Invalid template provided');

  do {
    var name = _generateTmpName(opts);
    if (!existsSync(name)) {
      return name;
    }
  } while (tries-- > 0);

  throw new Error('Could not get a unique tmp filename, max tries reached');
}

/**
 * Creates and opens a temporary file.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpFile(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

    opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix;

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create and open the file
    fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) {
      if (err) return cb(err);

      cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpFile.
 *
 * @param {Object} options
 * @returns {Object} object consists of name, fd and removeCallback
 * @api private
 */
function _createTmpFileSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

    opts.postfix = opts.postfix || '.tmp';

  var name = _getTmpNameSync(opts);
  var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE);

  return {
    name : name,
    fd : fd,
    removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts)
  };
}

/**
 * Removes files and folders in a directory recursively.
 *
 * @param {String} root
 * @api private
 */
function _rmdirRecursiveSync(root) {
  var dirs = [root];

  do {
    var
      dir = dirs.pop(),
      canRemove = true,
      files = fs.readdirSync(dir);

    for (var i = 0, length = files.length; i < length; i++) {
      var
        file = path.join(dir, files[i]),
        stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories

      if (stat.isDirectory()) {
        canRemove = false;
        dirs.push(dir);
        dirs.push(file);
      } else {
        fs.unlinkSync(file);
      }
    }

    if (canRemove) {
      fs.rmdirSync(dir);
    }
  } while (dirs.length !== 0);
}

/**
 * Creates a temporary directory.
 *
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */
function _createTmpDir(options, callback) {
  var
    args = _parseArguments(options, callback),
    opts = args[0],
    cb = args[1];

  // gets a temporary filename
  _getTmpName(opts, function _tmpNameCreated(err, name) {
    if (err) return cb(err);

    // create the directory
    fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) {
      if (err) return cb(err);

      cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
    });
  });
}

/**
 * Synchronous version of _createTmpDir.
 *
 * @param {Object} options
 * @returns {Object} object consists of name and removeCallback
 * @api private
 */
function _createTmpDirSync(options) {
  var
    args = _parseArguments(options),
    opts = args[0];

  var name = _getTmpNameSync(opts);
  fs.mkdirSync(name, opts.mode || DIR_MODE);

  return {
    name : name,
    removeCallback : _prepareTmpDirRemoveCallback(name, opts)
  };
}

/**
 * Prepares the callback for removal of the temporary file.
 *
 * @param {String} name
 * @param {int} fd
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpFileRemoveCallback(name, fd, opts) {
  var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) {
    fs.closeSync(fdPath[0]);
    fs.unlinkSync(fdPath[1]);
  }, [fd, name]);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Prepares the callback for removal of the temporary directory.
 *
 * @param {String} name
 * @param {Object} opts
 * @api private
 * @returns {Function} the callback
 */
function _prepareTmpDirRemoveCallback(name, opts) {
  var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs);
  var removeCallback = _prepareRemoveCallback(removeFunction, name);

  if (!opts.keep) {
    _removeObjects.unshift(removeCallback);
  }

  return removeCallback;
}

/**
 * Creates a guarded function wrapping the removeFunction call.
 *
 * @param {Function} removeFunction
 * @param {Object} arg
 * @returns {Function}
 * @api private
 */
function _prepareRemoveCallback(removeFunction, arg) {
  var called = false;

  return function _cleanupCallback() {
    if (called) return;

    var index = _removeObjects.indexOf(removeFunction);
    if (index >= 0) {
      _removeObjects.splice(index, 1);
    }

    called = true;
    removeFunction(arg);
  };
}

/**
 * The garbage collector.
 *
 * @api private
 */
function _garbageCollector() {
  if (_uncaughtException && !_gracefulCleanup) {
    return;
  }

  for (var i = 0, length = _removeObjects.length; i < length; i++) {
    try {
      _removeObjects[i].call(null);
    } catch (e) {
      // already removed?
    }
  }
}

function _setGracefulCleanup() {
  _gracefulCleanup = true;
}

var version = process.versions.node.split('.').map(function (value) {
  return parseInt(value, 10);
});

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
  process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
    _uncaughtException = true;
    _garbageCollector();

    throw err;
  });
}

process.addListener('exit', function _exit(code) {
  if (code) _uncaughtException = true;
  _garbageCollector();
});

// exporting all the needed methods
module.exports.tmpdir = _TMP;
module.exports.dir = _createTmpDir;
module.exports.dirSync = _createTmpDirSync;
module.exports.file = _createTmpFile;
module.exports.fileSync = _createTmpFileSync;
module.exports.tmpName = _getTmpName;
module.exports.tmpNameSync = _getTmpNameSync;
module.exports.setGracefulCleanup = _setGracefulCleanup;
<|endoftext|>