<commit_before>'use strict';

var Selectivity = require('./selectivity-base');

var KEY_BACKSPACE = 8;
var KEY_DOWN_ARROW = 40;
var KEY_ENTER = 13;
var KEY_ESCAPE = 27;
var KEY_TAB = 9;
var KEY_UP_ARROW = 38;

/**
 * Search input listener providing keyboard support for navigating the dropdown.
 */
function listener(selectivity, $input) {

    /**
     * Moves a dropdown's highlight to the next or previous result item.
     *
     * @param delta Either 1 to move to the next item, or -1 to move to the previous item.
     */
    function moveHighlight(dropdown, delta) {

        function findElementIndex($elements, selector) {
            for (var i = 0, length = $elements.length; i < length; i++) {
                if ($elements.eq(i).is(selector)) {
                    return i;
                }
            }
            return -1;
        }

        function scrollToHighlight() {
            var el;
            if (dropdown.highlightedResult) {
                var quotedId = Selectivity.quoteCssAttr(dropdown.highlightedResult.id);
                el = dropdown.$('.selectivity-result-item[data-item-id=' + quotedId + ']')[0];
            } else if (dropdown.loadMoreHighlighted) {
                el = dropdown.$('.selectivity-load-more')[0];
            } else {
                return; // no highlight to scroll to
            }

            var rect = el.getBoundingClientRect(),
                containerRect = dropdown.$results[0].getBoundingClientRect();

            if (rect.top < containerRect.top || rect.bottom > containerRect.bottom) {
                el.scrollIntoView(delta < 0);
            }
        }

        if (dropdown.submenu) {
            moveHighlight(dropdown.submenu, delta);
            return;
        }

        var results = dropdown.results;
        if (results.length) {
            var $results = dropdown.$('.selectivity-result-item');
            var defaultIndex = (delta > 0 ? 0 : $results.length - 1);
            var index = defaultIndex;
            var highlightedResult = dropdown.highlightedResult;
            if (highlightedResult) {
                var quotedId = Selectivity.quoteCssAttr(highlightedResult.id);
                index = findElementIndex($results, '[data-item-id=' + quotedId + ']') + delta;
                if (delta > 0 ? index >= $results.length : index < 0) {
                    if (dropdown.hasMore) {
                        dropdown.highlightLoadMore();
                        scrollToHighlight();
                        return;
                    } else {
                        index = defaultIndex;
                    }
                }
            }

            var result = Selectivity.findNestedById(results,
                                                    selectivity._getItemId($results[index]));
            if (result) {
                dropdown.highlight(result);
                scrollToHighlight();
            }
        }
    }

    function keyHeld(event) {

        var dropdown = selectivity.dropdown;
        if (dropdown) {
            if (event.keyCode === KEY_DOWN_ARROW) {
                moveHighlight(dropdown, 1);
            } else if (event.keyCode === KEY_UP_ARROW) {
                moveHighlight(dropdown, -1);
            } else if (event.keyCode === KEY_TAB) {
                setTimeout(function() {
                    selectivity.close({ keepFocus: false });
                }, 1);
            }
        }
    }

    function keyReleased(event) {

        function open() {
            if (selectivity.options.showDropdown !== false) {
                selectivity.open();
            }
        }

        var dropdown = selectivity.dropdown;
        if (event.keyCode === KEY_BACKSPACE) {
            if (!$input.val()) {
                if (dropdown && dropdown.submenu) {
                    var submenu = dropdown.submenu;
                    while (submenu.submenu) {
                        submenu = submenu.submenu;
                    }
                    submenu.close();
                    selectivity.focus();
                }

                event.preventDefault();
            }
        } else if (event.keyCode === KEY_ENTER && !event.ctrlKey) {
            if (dropdown) {
                dropdown.selectHighlight();
            } else if (selectivity.options.showDropdown !== false) {
                open();
            }

            event.preventDefault();
        } else if (event.keyCode === KEY_ESCAPE) {
            selectivity.close();

            event.preventDefault();
        } else if (event.keyCode === KEY_DOWN_ARROW || event.keyCode === KEY_UP_ARROW) {
            // handled in keyHeld() because the response feels faster and it works with repeated
            // events if the user holds the key for a longer period
            // still, we issue an open() call here in case the dropdown was not yet open...
            open();

            event.preventDefault();
        } else {
            open();
        }
    }

    $input.on('keydown', keyHeld).on('keyup', keyReleased);
}

Selectivity.SearchInputListeners.push(listener);
<commit_msg>Fixes #34: Incorrect behavior of backspace when deleting last character from search input.<commit_after>'use strict';

var Selectivity = require('./selectivity-base');

var KEY_BACKSPACE = 8;
var KEY_DOWN_ARROW = 40;
var KEY_ENTER = 13;
var KEY_ESCAPE = 27;
var KEY_TAB = 9;
var KEY_UP_ARROW = 38;

/**
 * Search input listener providing keyboard support for navigating the dropdown.
 */
function listener(selectivity, $input) {

    var keydownCanceled = false;
    var closeSubmenu = null;

    /**
     * Moves a dropdown's highlight to the next or previous result item.
     *
     * @param delta Either 1 to move to the next item, or -1 to move to the previous item.
     */
    function moveHighlight(dropdown, delta) {

        function findElementIndex($elements, selector) {
            for (var i = 0, length = $elements.length; i < length; i++) {
                if ($elements.eq(i).is(selector)) {
                    return i;
                }
            }
            return -1;
        }

        function scrollToHighlight() {
            var el;
            if (dropdown.highlightedResult) {
                var quotedId = Selectivity.quoteCssAttr(dropdown.highlightedResult.id);
                el = dropdown.$('.selectivity-result-item[data-item-id=' + quotedId + ']')[0];
            } else if (dropdown.loadMoreHighlighted) {
                el = dropdown.$('.selectivity-load-more')[0];
            } else {
                return; // no highlight to scroll to
            }

            var rect = el.getBoundingClientRect(),
                containerRect = dropdown.$results[0].getBoundingClientRect();

            if (rect.top < containerRect.top || rect.bottom > containerRect.bottom) {
                el.scrollIntoView(delta < 0);
            }
        }

        if (dropdown.submenu) {
            moveHighlight(dropdown.submenu, delta);
            return;
        }

        var results = dropdown.results;
        if (results.length) {
            var $results = dropdown.$('.selectivity-result-item');
            var defaultIndex = (delta > 0 ? 0 : $results.length - 1);
            var index = defaultIndex;
            var highlightedResult = dropdown.highlightedResult;
            if (highlightedResult) {
                var quotedId = Selectivity.quoteCssAttr(highlightedResult.id);
                index = findElementIndex($results, '[data-item-id=' + quotedId + ']') + delta;
                if (delta > 0 ? index >= $results.length : index < 0) {
                    if (dropdown.hasMore) {
                        dropdown.highlightLoadMore();
                        scrollToHighlight();
                        return;
                    } else {
                        index = defaultIndex;
                    }
                }
            }

            var result = Selectivity.findNestedById(results,
                                                    selectivity._getItemId($results[index]));
            if (result) {
                dropdown.highlight(result);
                scrollToHighlight();
            }
        }
    }

    function keyHeld(event) {

        var dropdown = selectivity.dropdown;
        if (dropdown) {
            if (event.keyCode === KEY_BACKSPACE) {
                if (!$input.val()) {
                    if (dropdown.submenu) {
                        var submenu = dropdown.submenu;
                        while (submenu.submenu) {
                            submenu = submenu.submenu;
                        }
                        closeSubmenu = submenu;
                    }

                    event.preventDefault();
                    keydownCanceled = true;
                }
            } else if (event.keyCode === KEY_DOWN_ARROW) {
                moveHighlight(dropdown, 1);
            } else if (event.keyCode === KEY_UP_ARROW) {
                moveHighlight(dropdown, -1);
            } else if (event.keyCode === KEY_TAB) {
                setTimeout(function() {
                    selectivity.close({ keepFocus: false });
                }, 1);
            }
        }
    }

    function keyReleased(event) {

        function open() {
            if (selectivity.options.showDropdown !== false) {
                selectivity.open();
            }
        }

        var dropdown = selectivity.dropdown;
        if (keydownCanceled) {
            event.preventDefault();
            keydownCanceled = false;

            if (closeSubmenu) {
                closeSubmenu.close();
                closeSubmenu = null;
            }
        } else if (event.keyCode === KEY_ENTER && !event.ctrlKey) {
            if (dropdown) {
                dropdown.selectHighlight();
            } else if (selectivity.options.showDropdown !== false) {
                open();
            }

            event.preventDefault();
        } else if (event.keyCode === KEY_ESCAPE) {
            selectivity.close();

            event.preventDefault();
        } else if (event.keyCode === KEY_DOWN_ARROW || event.keyCode === KEY_UP_ARROW) {
            // handled in keyHeld() because the response feels faster and it works with repeated
            // events if the user holds the key for a longer period
            // still, we issue an open() call here in case the dropdown was not yet open...
            open();

            event.preventDefault();
        } else {
            open();
        }
    }

    $input.on('keydown', keyHeld).on('keyup', keyReleased);
}

Selectivity.SearchInputListeners.push(listener);
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

var colors = require('./colors');
var fs = require('graceful-fs');
var path = require('path');
var Promise = require('bluebird');

var DEFAULT_CONFIG_VALUES = {
  cacheDirectory: path.resolve(__dirname, '..', '..', '.haste_cache'),
  coverageCollector: require.resolve('../IstanbulCollector'),
  coverageReporters: [ 'json', 'text', 'lcov', 'clover' ],
  globals: {},
  moduleFileExtensions: ['js', 'json'],
  moduleLoader: require.resolve('../HasteModuleLoader/HasteModuleLoader'),
  preprocessorIgnorePatterns: [],
  modulePathIgnorePatterns: [],
  testDirectoryName: '__tests__',
  testEnvironment: require.resolve('../JSDomEnvironment'),
  testEnvData: {},
  testFileExtensions: ['js'],
  testPathDirs: ['<rootDir>'],
  testPathIgnorePatterns: ['/node_modules/'],
  testReporter: require.resolve('../IstanbulTestReporter'),
  testRunner: require.resolve('../jasmineTestRunner/jasmineTestRunner'),
  noHighlight: false,
};

function _replaceRootDirTags(rootDir, config) {
  switch (typeof config) {
    case 'object':
      if (config instanceof RegExp) {
        return config;
      }

      if (Array.isArray(config)) {
        return config.map(function(item) {
          return _replaceRootDirTags(rootDir, item);
        });
      }

      if (config !== null) {
        var newConfig = {};
        for (var configKey in config) {
          newConfig[configKey] =
            configKey === 'rootDir'
            ? config[configKey]
            : _replaceRootDirTags(rootDir, config[configKey]);
        }
        return newConfig;
      }
      break;
    case 'string':
      if (!/^<rootDir>/.test(config)) {
        return config;
      }

      return pathNormalize(path.resolve(
        rootDir,
        './' + path.normalize(config.substr('<rootDir>'.length))
      ));
  }
  return config;
}

function escapeStrForRegex(str) {
  return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

/**
 * Given the coverage info for a single file (as output by
 * CoverageCollector.js), return an array whose entries are bools indicating
 * whether anything on the line could have been covered and was, or null if the
 * line wasn't measurable (like empty lines, declaration keywords, etc).
 *
 * For example, for the following coverage info:
 *
 * COVERED:     var a = [];
 * NO CODE:
 * COVERED:     for (var i = 0; i < a.length; i++)
 * NOT COVERED:   console.log('hai!');
 *
 * You'd get an array that looks like this:
 *
 * [true, null, true, false]
 */
function getLineCoverageFromCoverageInfo(coverageInfo) {
  var coveredLines = {};
  coverageInfo.coveredSpans.forEach(function(coveredSpan) {
    var startLine = coveredSpan.start.line;
    var endLine = coveredSpan.end.line;
    for (var i = startLine - 1; i < endLine; i++) {
      coveredLines[i] = true;
    }
  });

  var uncoveredLines = {};
  coverageInfo.uncoveredSpans.forEach(function(uncoveredSpan) {
    var startLine = uncoveredSpan.start.line;
    var endLine = uncoveredSpan.end.line;
    for (var i = startLine - 1; i < endLine; i++) {
      uncoveredLines[i] = true;
    }
  });

  var sourceLines = coverageInfo.sourceText.trim().split('\n');

  return sourceLines.map(function(line, lineIndex) {
    if (uncoveredLines[lineIndex] === true) {
      return false;
    } else if (coveredLines[lineIndex] === true) {
      return true;
    } else {
      return null;
    }
  });
}

/**
 * Given the coverage info for a single file (as output by
 * CoverageCollector.js), return the decimal percentage of lines in the file
 * that had any coverage info.
 *
 * For example, for the following coverage info:
 *
 * COVERED:     var a = [];
 * NO CODE:
 * COVERED:     for (var i = 0; i < a.length; i++)
 * NOT COVERED:   console.log('hai');
 *
 * You'd get: 2/3 = 0.666666
 */
function getLinePercentCoverageFromCoverageInfo(coverageInfo) {
  var lineCoverage = getLineCoverageFromCoverageInfo(coverageInfo);
  var numMeasuredLines = 0;
  var numCoveredLines = lineCoverage.reduce(function(counter, lineIsCovered) {
    if (lineIsCovered !== null) {
      numMeasuredLines++;
      if (lineIsCovered === true) {
        counter++;
      }
    }
    return counter;
  }, 0);

  return numCoveredLines / numMeasuredLines;
}

function normalizeConfig(config) {
  var newConfig = {};

  // Assert that there *is* a rootDir
  if (!config.hasOwnProperty('rootDir')) {
    throw new Error('No rootDir config value found!');
  }

  config.rootDir = pathNormalize(config.rootDir);

  // Normalize user-supplied config options
  Object.keys(config).reduce(function(newConfig, key) {
    var value;
    switch (key) {
      case 'collectCoverageOnlyFrom':
        value = Object.keys(config[key]).reduce(function(normObj, filePath) {
          filePath = pathNormalize(path.resolve(
            config.rootDir,
            _replaceRootDirTags(config.rootDir, filePath)
          ));
          normObj[filePath] = true;
          return normObj;
        }, {});
        break;

      case 'testPathDirs':
        value = config[key].map(function(scanDir) {
          return pathNormalize(path.resolve(
            config.rootDir,
            _replaceRootDirTags(config.rootDir, scanDir)
          ));
        });
        break;

      case 'cacheDirectory':
      case 'scriptPreprocessor':
      case 'setupEnvScriptFile':
      case 'setupTestFrameworkScriptFile':
        value = pathNormalize(path.resolve(
          config.rootDir,
          _replaceRootDirTags(config.rootDir, config[key])
        ));
        break;

      case 'preprocessorIgnorePatterns':
      case 'testPathIgnorePatterns':
      case 'modulePathIgnorePatterns':
      case 'unmockedModulePathPatterns':
        // _replaceRootDirTags is specifically well-suited for substituting
        // <rootDir> in paths (it deals with properly interpreting relative path
        // separators, etc).
        //
        // For patterns, direct global substitution is far more ideal, so we
        // special case substitutions for patterns here.
        value = config[key].map(function(pattern) {
          return pattern.replace(/<rootDir>/g, config.rootDir);
        });
        break;
      case 'coverageReporters':
      case 'collectCoverage':
      case 'coverageCollector':
      case 'globals':
      case 'moduleLoader':
      case 'name':
      case 'persistModuleRegistryBetweenSpecs':
      case 'rootDir':
      case 'setupJSLoaderOptions':
      case 'setupJSTestLoaderOptions':
      case 'setupJSMockLoaderOptions':
      case 'testDirectoryName':
      case 'testEnvData':
      case 'testFileExtensions':
      case 'testReporter':
      case 'moduleFileExtensions':
      case 'noHighlight':
        value = config[key];
        break;

      default:
        throw new Error('Unknown config option: ' + key);
    }
    newConfig[key] = value;
    return newConfig;
  }, newConfig);

  // If any config entries weren't specified but have default values, apply the
  // default values
  Object.keys(DEFAULT_CONFIG_VALUES).reduce(function(newConfig, key) {
    if (!newConfig[key]) {
      newConfig[key] = DEFAULT_CONFIG_VALUES[key];
    }
    return newConfig;
  }, newConfig);

  // Fill in some default values for node-haste config
  newConfig.setupJSLoaderOptions = newConfig.setupJSLoaderOptions || {};
  newConfig.setupJSTestLoaderOptions = newConfig.setupJSTestLoaderOptions || {};
  newConfig.setupJSMockLoaderOptions = newConfig.setupJSMockLoaderOptions || {};

  if (!newConfig.setupJSTestLoaderOptions.extensions) {
    newConfig.setupJSTestLoaderOptions.extensions =
      newConfig.testFileExtensions.map(_addDot);
  }

  if (!newConfig.setupJSLoaderOptions.extensions) {
    newConfig.setupJSLoaderOptions.extensions = uniqueStrings(
      newConfig.moduleFileExtensions.map(_addDot).concat(
        newConfig.setupJSTestLoaderOptions.extensions
      )
    );
  }

  if (!newConfig.setupJSMockLoaderOptions.extensions) {
    newConfig.setupJSMockLoaderOptions.extensions =
      newConfig.setupJSLoaderOptions.extensions;
  }

  return _replaceRootDirTags(newConfig.rootDir, newConfig);
}

function _addDot(ext) {
  return '.' + ext;
}

function uniqueStrings(set) {
  var newSet = [];
  var has = {};
  set.forEach(function (item) {
    if (!has[item]) {
      has[item] = true;
      newSet.push(item);
    }
  });
  return newSet;
}

function pathNormalize(dir) {
  return path.normalize(dir.replace(/\\/g, '/')).replace(/\\/g, '/');
}

var readFile = Promise.promisify(fs.readFile);
function loadConfigFromFile(filePath) {
  var fileDir = path.dirname(filePath);
  return readFile(filePath, 'utf8').then(function(fileData) {
    var config = JSON.parse(fileData);
    if (!config.hasOwnProperty('rootDir')) {
      config.rootDir = fileDir;
    } else {
      config.rootDir = path.resolve(fileDir, config.rootDir);
    }
    return normalizeConfig(config);
  });
}

function loadConfigFromPackageJson(filePath) {
  var pkgJsonDir = path.dirname(filePath);
  return readFile(filePath, 'utf8').then(function(fileData) {
    var packageJsonData = JSON.parse(fileData);
    var config = packageJsonData.jest;
    config.name = packageJsonData.name;
    if (!config.hasOwnProperty('rootDir')) {
      config.rootDir = pkgJsonDir;
    } else {
      config.rootDir = path.resolve(pkgJsonDir, config.rootDir);
    }
    return normalizeConfig(config);
  });
}

var _contentCache = {};
function readAndPreprocessFileContent(filePath, config) {
  var cacheRec;
  var mtime = fs.statSync(filePath).mtime;
  if (_contentCache.hasOwnProperty(filePath)) {
    cacheRec = _contentCache[filePath];
    if (cacheRec.mtime.getTime() === mtime.getTime()) {
      return cacheRec.content;
    }
  }

  var fileData = fs.readFileSync(filePath, 'utf8');

  // If the file data starts with a shebang remove it (but leave the line empty
  // to keep stack trace line numbers correct)
  if (fileData.substr(0, 2) === '#!') {
    fileData = fileData.replace(/^#!.*/, '');
  }

  if (config.scriptPreprocessor &&
      !config.preprocessorIgnorePatterns.some(function(pattern) {
        return pattern.test(filePath);
      })) {
    try {
      fileData = require(config.scriptPreprocessor).process(
        fileData,
        filePath,
        {}, // options
        [], // excludes
        config
      );
    } catch (e) {
      e.message = config.scriptPreprocessor + ': ' + e.message;
      throw e;
    }
  }
  _contentCache[filePath] = cacheRec = {mtime: mtime, content: fileData};
  return cacheRec.content;
}

function runContentWithLocalBindings(contextRunner, scriptContent, scriptPath,
                                     bindings) {
  var boundIdents = Object.keys(bindings);
  try {
    var wrapperFunc = contextRunner(
      '(function(' + boundIdents.join(',') + '){' +
      scriptContent +
      '\n})',
      scriptPath
    );
  } catch (e) {
    e.message = scriptPath + ': ' + e.message;
    throw e;
  }

  var bindingValues = boundIdents.map(function(ident) {
    return bindings[ident];
  });

  try {
    wrapperFunc.apply(null, bindingValues);
  } catch (e) {
    e.message = scriptPath + ': ' + e.message;
    throw e;
  }
}

/**
 * Given a test result, return a human readable string representing the
 * failures.
 *
 * @param {Object} testResult
 * @param {boolean} color true if message should include color flags
 * @return {String}
 */
function formatFailureMessage(testResult, color) {
  var colorize = color ? colors.colorize : function (str) { return str; };
  var ancestrySeparator = ' \u203A ';
  var descBullet = colorize('\u25cf ', colors.BOLD);
  var msgBullet = '  - ';
  var msgIndent = msgBullet.replace(/./g, ' ');

  return testResult.testResults.filter(function (result) {
    return result.failureMessages.length !== 0;
  }).map(function(result) {
    var failureMessages = result.failureMessages.map(function (errorMsg) {
      // Filter out q and jasmine entries from the stack trace.
      // They're super noisy and unhelpful
      errorMsg = errorMsg.split('\n').filter(function(line) {
        if (/^\s+at .*?/.test(line)) {
          // Extract the file path from the trace line
          var filePath = line.match(/(?:\(|at (?=\/))(.*):[0-9]+:[0-9]+\)?$/);
          if (filePath
              && STACK_TRACE_LINE_IGNORE_RE.test(filePath[1])) {
            return false;
          }
        }
        return true;
      }).join('\n');

      return msgBullet + errorMsg.replace(/\n/g, '\n' + msgIndent);
    }).join('\n');

    var testTitleAncestry = result.ancestorTitles.map(function(title) {
      return colorize(title, colors.BOLD);
    }).join(ancestrySeparator) + ancestrySeparator;

    return descBullet + testTitleAncestry + result.title + '\n' +
      failureMessages;
  }).join('\n');
}

function formatMsg(msg, color, _config) {
  _config = _config || {};
  if (_config.noHighlight) {
    return msg;
  }
  return colors.colorize(msg, color);
}

// A RegExp that matches paths that should not be included in error stack traces
// (mostly because these paths represent noisy/unhelpful libs)
var STACK_TRACE_LINE_IGNORE_RE = new RegExp('^(?:' + [
    path.resolve(__dirname, '..', 'node_modules', 'q'),
    path.resolve(__dirname, '..', 'node_modules', 'bluebird'),
    path.resolve(__dirname, '..', 'vendor', 'jasmine')
].join('|') + ')');

exports.escapeStrForRegex = escapeStrForRegex;
exports.formatMsg = formatMsg;
exports.getLineCoverageFromCoverageInfo = getLineCoverageFromCoverageInfo;
exports.getLinePercentCoverageFromCoverageInfo =
  getLinePercentCoverageFromCoverageInfo;
exports.loadConfigFromFile = loadConfigFromFile;
exports.loadConfigFromPackageJson = loadConfigFromPackageJson;
exports.normalizeConfig = normalizeConfig;
exports.pathNormalize = pathNormalize;
exports.readAndPreprocessFileContent = readAndPreprocessFileContent;
exports.runContentWithLocalBindings = runContentWithLocalBindings;
exports.formatFailureMessage = formatFailureMessage;
<commit_msg>Preprocessor disk cache<commit_after>/**
 * Copyright (c) 2014, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

var crypto = require('crypto');
var colors = require('./colors');
var fs = require('graceful-fs');
var os = require('os');
var path = require('path');
var Promise = require('bluebird');

var DEFAULT_CONFIG_VALUES = {
  cacheDirectory: path.resolve(__dirname, '..', '..', '.haste_cache'),
  coverageCollector: require.resolve('../IstanbulCollector'),
  coverageReporters: [ 'json', 'text', 'lcov', 'clover' ],
  globals: {},
  moduleFileExtensions: ['js', 'json'],
  moduleLoader: require.resolve('../HasteModuleLoader/HasteModuleLoader'),
  preprocessorIgnorePatterns: [],
  modulePathIgnorePatterns: [],
  testDirectoryName: '__tests__',
  testEnvironment: require.resolve('../JSDomEnvironment'),
  testEnvData: {},
  testFileExtensions: ['js'],
  testPathDirs: ['<rootDir>'],
  testPathIgnorePatterns: ['/node_modules/'],
  testReporter: require.resolve('../IstanbulTestReporter'),
  testRunner: require.resolve('../jasmineTestRunner/jasmineTestRunner'),
  noHighlight: false,
};

function _replaceRootDirTags(rootDir, config) {
  switch (typeof config) {
    case 'object':
      if (config instanceof RegExp) {
        return config;
      }

      if (Array.isArray(config)) {
        return config.map(function(item) {
          return _replaceRootDirTags(rootDir, item);
        });
      }

      if (config !== null) {
        var newConfig = {};
        for (var configKey in config) {
          newConfig[configKey] =
            configKey === 'rootDir'
            ? config[configKey]
            : _replaceRootDirTags(rootDir, config[configKey]);
        }
        return newConfig;
      }
      break;
    case 'string':
      if (!/^<rootDir>/.test(config)) {
        return config;
      }

      return pathNormalize(path.resolve(
        rootDir,
        './' + path.normalize(config.substr('<rootDir>'.length))
      ));
  }
  return config;
}

function escapeStrForRegex(str) {
  return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

/**
 * Given the coverage info for a single file (as output by
 * CoverageCollector.js), return an array whose entries are bools indicating
 * whether anything on the line could have been covered and was, or null if the
 * line wasn't measurable (like empty lines, declaration keywords, etc).
 *
 * For example, for the following coverage info:
 *
 * COVERED:     var a = [];
 * NO CODE:
 * COVERED:     for (var i = 0; i < a.length; i++)
 * NOT COVERED:   console.log('hai!');
 *
 * You'd get an array that looks like this:
 *
 * [true, null, true, false]
 */
function getLineCoverageFromCoverageInfo(coverageInfo) {
  var coveredLines = {};
  coverageInfo.coveredSpans.forEach(function(coveredSpan) {
    var startLine = coveredSpan.start.line;
    var endLine = coveredSpan.end.line;
    for (var i = startLine - 1; i < endLine; i++) {
      coveredLines[i] = true;
    }
  });

  var uncoveredLines = {};
  coverageInfo.uncoveredSpans.forEach(function(uncoveredSpan) {
    var startLine = uncoveredSpan.start.line;
    var endLine = uncoveredSpan.end.line;
    for (var i = startLine - 1; i < endLine; i++) {
      uncoveredLines[i] = true;
    }
  });

  var sourceLines = coverageInfo.sourceText.trim().split('\n');

  return sourceLines.map(function(line, lineIndex) {
    if (uncoveredLines[lineIndex] === true) {
      return false;
    } else if (coveredLines[lineIndex] === true) {
      return true;
    } else {
      return null;
    }
  });
}

/**
 * Given the coverage info for a single file (as output by
 * CoverageCollector.js), return the decimal percentage of lines in the file
 * that had any coverage info.
 *
 * For example, for the following coverage info:
 *
 * COVERED:     var a = [];
 * NO CODE:
 * COVERED:     for (var i = 0; i < a.length; i++)
 * NOT COVERED:   console.log('hai');
 *
 * You'd get: 2/3 = 0.666666
 */
function getLinePercentCoverageFromCoverageInfo(coverageInfo) {
  var lineCoverage = getLineCoverageFromCoverageInfo(coverageInfo);
  var numMeasuredLines = 0;
  var numCoveredLines = lineCoverage.reduce(function(counter, lineIsCovered) {
    if (lineIsCovered !== null) {
      numMeasuredLines++;
      if (lineIsCovered === true) {
        counter++;
      }
    }
    return counter;
  }, 0);

  return numCoveredLines / numMeasuredLines;
}

function normalizeConfig(config) {
  var newConfig = {};

  // Assert that there *is* a rootDir
  if (!config.hasOwnProperty('rootDir')) {
    throw new Error('No rootDir config value found!');
  }

  config.rootDir = pathNormalize(config.rootDir);

  // Normalize user-supplied config options
  Object.keys(config).reduce(function(newConfig, key) {
    var value;
    switch (key) {
      case 'collectCoverageOnlyFrom':
        value = Object.keys(config[key]).reduce(function(normObj, filePath) {
          filePath = pathNormalize(path.resolve(
            config.rootDir,
            _replaceRootDirTags(config.rootDir, filePath)
          ));
          normObj[filePath] = true;
          return normObj;
        }, {});
        break;

      case 'testPathDirs':
        value = config[key].map(function(scanDir) {
          return pathNormalize(path.resolve(
            config.rootDir,
            _replaceRootDirTags(config.rootDir, scanDir)
          ));
        });
        break;

      case 'cacheDirectory':
      case 'scriptPreprocessor':
      case 'setupEnvScriptFile':
      case 'setupTestFrameworkScriptFile':
        value = pathNormalize(path.resolve(
          config.rootDir,
          _replaceRootDirTags(config.rootDir, config[key])
        ));
        break;

      case 'preprocessorIgnorePatterns':
      case 'testPathIgnorePatterns':
      case 'modulePathIgnorePatterns':
      case 'unmockedModulePathPatterns':
        // _replaceRootDirTags is specifically well-suited for substituting
        // <rootDir> in paths (it deals with properly interpreting relative path
        // separators, etc).
        //
        // For patterns, direct global substitution is far more ideal, so we
        // special case substitutions for patterns here.
        value = config[key].map(function(pattern) {
          return pattern.replace(/<rootDir>/g, config.rootDir);
        });
        break;
      case 'coverageReporters':
      case 'collectCoverage':
      case 'coverageCollector':
      case 'globals':
      case 'moduleLoader':
      case 'name':
      case 'persistModuleRegistryBetweenSpecs':
      case 'rootDir':
      case 'setupJSLoaderOptions':
      case 'setupJSTestLoaderOptions':
      case 'setupJSMockLoaderOptions':
      case 'testDirectoryName':
      case 'testEnvData':
      case 'testFileExtensions':
      case 'testReporter':
      case 'moduleFileExtensions':
      case 'noHighlight':
        value = config[key];
        break;

      default:
        throw new Error('Unknown config option: ' + key);
    }
    newConfig[key] = value;
    return newConfig;
  }, newConfig);

  // If any config entries weren't specified but have default values, apply the
  // default values
  Object.keys(DEFAULT_CONFIG_VALUES).reduce(function(newConfig, key) {
    if (!newConfig[key]) {
      newConfig[key] = DEFAULT_CONFIG_VALUES[key];
    }
    return newConfig;
  }, newConfig);

  // Fill in some default values for node-haste config
  newConfig.setupJSLoaderOptions = newConfig.setupJSLoaderOptions || {};
  newConfig.setupJSTestLoaderOptions = newConfig.setupJSTestLoaderOptions || {};
  newConfig.setupJSMockLoaderOptions = newConfig.setupJSMockLoaderOptions || {};

  if (!newConfig.setupJSTestLoaderOptions.extensions) {
    newConfig.setupJSTestLoaderOptions.extensions =
      newConfig.testFileExtensions.map(_addDot);
  }

  if (!newConfig.setupJSLoaderOptions.extensions) {
    newConfig.setupJSLoaderOptions.extensions = uniqueStrings(
      newConfig.moduleFileExtensions.map(_addDot).concat(
        newConfig.setupJSTestLoaderOptions.extensions
      )
    );
  }

  if (!newConfig.setupJSMockLoaderOptions.extensions) {
    newConfig.setupJSMockLoaderOptions.extensions =
      newConfig.setupJSLoaderOptions.extensions;
  }

  return _replaceRootDirTags(newConfig.rootDir, newConfig);
}

function _addDot(ext) {
  return '.' + ext;
}

function uniqueStrings(set) {
  var newSet = [];
  var has = {};
  set.forEach(function (item) {
    if (!has[item]) {
      has[item] = true;
      newSet.push(item);
    }
  });
  return newSet;
}

function pathNormalize(dir) {
  return path.normalize(dir.replace(/\\/g, '/')).replace(/\\/g, '/');
}

var readFile = Promise.promisify(fs.readFile);
function loadConfigFromFile(filePath) {
  var fileDir = path.dirname(filePath);
  return readFile(filePath, 'utf8').then(function(fileData) {
    var config = JSON.parse(fileData);
    if (!config.hasOwnProperty('rootDir')) {
      config.rootDir = fileDir;
    } else {
      config.rootDir = path.resolve(fileDir, config.rootDir);
    }
    return normalizeConfig(config);
  });
}

function loadConfigFromPackageJson(filePath) {
  var pkgJsonDir = path.dirname(filePath);
  return readFile(filePath, 'utf8').then(function(fileData) {
    var packageJsonData = JSON.parse(fileData);
    var config = packageJsonData.jest;
    config.name = packageJsonData.name;
    if (!config.hasOwnProperty('rootDir')) {
      config.rootDir = pkgJsonDir;
    } else {
      config.rootDir = path.resolve(pkgJsonDir, config.rootDir);
    }
    return normalizeConfig(config);
  });
}

function cleanupCacheFile(cachePath) {
  try {
    fs.unlinkSync(cachePath);
  } catch (e) {
    /*ignore errors*/
  }
}

function storeCacheRecord(mtime, fileData, filePath) {
  _contentCache[filePath] = {mtime: mtime, content: fileData};
  return fileData;
}

// There are two layers of caching: in memory (always enabled),
// and on disk (enabled by default, and managed by the
// `preprocessCachingDisabled` option). The preprocessor script can also
// provide hashing function for the cache key.
var _contentCache = {};
function readAndPreprocessFileContent(filePath, config) {
  var cacheRec;
  var mtime = fs.statSync(filePath).mtime;
  if (_contentCache.hasOwnProperty(filePath)) {
    cacheRec = _contentCache[filePath];
    if (cacheRec.mtime.getTime() === mtime.getTime()) {
      return cacheRec.content;
    }
  }

  var fileData = fs.readFileSync(filePath, 'utf8');

  // If the file data starts with a shebang remove it (but leave the line empty
  // to keep stack trace line numbers correct)
  if (fileData.substr(0, 2) === '#!') {
    fileData = fileData.replace(/^#!.*/, '');
  }

  if (config.scriptPreprocessor &&
      !config.preprocessorIgnorePatterns.some(function(pattern) {
        return pattern.test(filePath);
      })) {
    try {
      var preprocessor = require(config.scriptPreprocessor);
      if (typeof preprocessor.process !== 'function') {
        throw new TypeError('Preprocessor should export `process` function.');
      }
      // On disk cache is enabled by default, unless explicitly disabled.
      if (config.preprocessCachingDisabled !== true) {
        var cacheDir = path.join(
          os.tmpDir(),
          'jest_preprocess_cache'
        );

        try {
          fs.mkdirSync(cacheDir);
        } catch(e) {
          if (e.code !== 'EEXIST') {
            throw e;
          }
        }

        fs.chmodSync(cacheDir, '777');

        var cacheKey;
        // If preprocessor defines custom cache hashing and
        // invalidating logic.
        if (typeof preprocessor.getCacheKey === 'function') {
          cacheKey = preprocessor.getCacheKey(
            fileData,
            filePath,
            {}, // options
            [], //excludes
            config
          );
        } else {
          // Default cache hashing.
          cacheKey = crypto.createHash('md5')
            .update(fileData)
            .update(JSON.stringify(config))
            .digest('hex');
        }

        var cachePath = path.join(
          cacheDir,
          cacheKey + '_' + path.basename(filePath)
        );

        if (fs.existsSync(cachePath)) {
          try {
            var cachedData = fs.readFileSync(cachePath, 'utf8');
            if (cachedData) {
              return storeCacheRecord(mtime, cachedData, filePath);
            } else {
              // In this case we must have somehow created the file but failed
              // to write to it, lets just delete it and move on
              cleanupCacheFile(cachePath);
            }
          } catch (e) {
            e.message = 'Failed to read preprocess cache file: ' + cachePath;
            cleanupCacheFile(cachePath);
            throw e;
          }
        }

        fileData = preprocessor.process(
          fileData,
          filePath,
          {}, // options
          [], // excludes
          config
        );

        try {
          fs.writeFileSync(cachePath, fileData);
        } catch (e) {
          e.message = 'Failed to cache preprocess results in: ' + cachePath;
          cleanupCacheFile(cachePath);
          throw e;
        }

      } else {
        fileData = preprocessor.process(
          fileData,
          filePath,
          {}, // options
          [], // excludes
          config
        );
      }
    } catch (e) {
      e.message = config.scriptPreprocessor + ': ' + e.message;
      throw e;
    }
  }
  return storeCacheRecord(mtime, fileData, filePath);
}

function runContentWithLocalBindings(contextRunner, scriptContent, scriptPath,
                                     bindings) {
  var boundIdents = Object.keys(bindings);
  try {
    var wrapperFunc = contextRunner(
      '(function(' + boundIdents.join(',') + '){' +
      scriptContent +
      '\n})',
      scriptPath
    );
  } catch (e) {
    e.message = scriptPath + ': ' + e.message;
    throw e;
  }

  var bindingValues = boundIdents.map(function(ident) {
    return bindings[ident];
  });

  try {
    wrapperFunc.apply(null, bindingValues);
  } catch (e) {
    e.message = scriptPath + ': ' + e.message;
    throw e;
  }
}

/**
 * Given a test result, return a human readable string representing the
 * failures.
 *
 * @param {Object} testResult
 * @param {boolean} color true if message should include color flags
 * @return {String}
 */
function formatFailureMessage(testResult, color) {
  var colorize = color ? colors.colorize : function (str) { return str; };
  var ancestrySeparator = ' \u203A ';
  var descBullet = colorize('\u25cf ', colors.BOLD);
  var msgBullet = '  - ';
  var msgIndent = msgBullet.replace(/./g, ' ');

  return testResult.testResults.filter(function (result) {
    return result.failureMessages.length !== 0;
  }).map(function(result) {
    var failureMessages = result.failureMessages.map(function (errorMsg) {
      // Filter out q and jasmine entries from the stack trace.
      // They're super noisy and unhelpful
      errorMsg = errorMsg.split('\n').filter(function(line) {
        if (/^\s+at .*?/.test(line)) {
          // Extract the file path from the trace line
          var filePath = line.match(/(?:\(|at (?=\/))(.*):[0-9]+:[0-9]+\)?$/);
          if (filePath
              && STACK_TRACE_LINE_IGNORE_RE.test(filePath[1])) {
            return false;
          }
        }
        return true;
      }).join('\n');

      return msgBullet + errorMsg.replace(/\n/g, '\n' + msgIndent);
    }).join('\n');

    var testTitleAncestry = result.ancestorTitles.map(function(title) {
      return colorize(title, colors.BOLD);
    }).join(ancestrySeparator) + ancestrySeparator;

    return descBullet + testTitleAncestry + result.title + '\n' +
      failureMessages;
  }).join('\n');
}

function formatMsg(msg, color, _config) {
  _config = _config || {};
  if (_config.noHighlight) {
    return msg;
  }
  return colors.colorize(msg, color);
}

// A RegExp that matches paths that should not be included in error stack traces
// (mostly because these paths represent noisy/unhelpful libs)
var STACK_TRACE_LINE_IGNORE_RE = new RegExp('^(?:' + [
    path.resolve(__dirname, '..', 'node_modules', 'q'),
    path.resolve(__dirname, '..', 'node_modules', 'bluebird'),
    path.resolve(__dirname, '..', 'vendor', 'jasmine')
].join('|') + ')');

exports.escapeStrForRegex = escapeStrForRegex;
exports.formatMsg = formatMsg;
exports.getLineCoverageFromCoverageInfo = getLineCoverageFromCoverageInfo;
exports.getLinePercentCoverageFromCoverageInfo =
  getLinePercentCoverageFromCoverageInfo;
exports.loadConfigFromFile = loadConfigFromFile;
exports.loadConfigFromPackageJson = loadConfigFromPackageJson;
exports.normalizeConfig = normalizeConfig;
exports.pathNormalize = pathNormalize;
exports.readAndPreprocessFileContent = readAndPreprocessFileContent;
exports.runContentWithLocalBindings = runContentWithLocalBindings;
exports.formatFailureMessage = formatFailureMessage;
<|endoftext|>


<commit_before>// @see https://devcenter.heroku.com/articles/nodejs#write-your-app

var express = require('express')
  , fs      = require('fs') // node
  , gm      = require('gm')
  , http    = require('http') // node
  , https   = require('https') // node
  , mime    = require('mime')
  , url     = require('url') // node
  // @see http://aaronheckmann.posterous.com/graphicsmagick-on-heroku-with-nodejs
  , app = express()
  , imageMagick = gm.subClass({imageMagick: true})
  , whitelist = process.env.WHITELIST || [] // [/\.gov$/, /google\.com$/]
  , delay = parseInt(process.env.DELAY) || 5000
  , mimeTypes = [
    'image/gif',
    'image/jpeg',
    'image/png',
    // Common typos
    'image/jpg'
  ];

function retrieveRemote(res, next, remote, width, height, extension) {
  console.log('DEBUG - remote=' + remote);
  console.log('DEBUG - width=' + width);
  console.log('DEBUG - height=' + height);
  // @see http://nodejs.org/api/url.html#url_url
  var options = url.parse(remote);
  // @see https://github.com/substack/hyperquest
  options.agent = false;
  if (options.protocol !== 'http:' && options.protocol !== 'https:') {
    return res.status(404).send('Expected URI scheme to be HTTP or HTTPS');
  }
  if (!options.hostname) {
    return res.status(404).send('Expected URI host to be non-empty');
  }
  options.headers = {'User-Agent': 'image-proxy/0.0.6'}

  var agent = options.protocol === 'http:' ? http : https
    , timeout = false
      // @see http://nodejs.org/api/http.html#http_http_get_options_callback
    , request = agent.get(options, function (response) {
        if (timeout) {
          // Status code 504 already sent.
          return;
        }

        // @see http://nodejs.org/api/http.html#http_response_statuscode
        if ((response.statusCode === 301 || response.statusCode === 302) && response.headers['location']) {
          var redirect = url.parse(response.headers['location']);
          // @see https://tools.ietf.org/html/rfc7231#section-7.1.2
          if (!redirect.protocol) {
            redirect.protocol = options.protocol;
          }
          if (!redirect.hostname) {
            redirect.hostname = options.hostname;
          }
          if (!redirect.port) {
            redirect.port = options.port;
          }
          if (!redirect.hash) {
            redirect.hash = options.hash;
          }
          return retrieveRemote(res, next, url.format(redirect), weight, height, extension);
        }

        // The image must return status code 200.
        if (response.statusCode !== 200) {
          return res.status(404).send('Expected response code 200, got ' + response.statusCode);
        }

        // The image must be a valid content type.
        // @see http://nodejs.org/api/http.html#http_request_headers
        var mimeType;
        if (extension) {
          mimeType = mime.lookup(extension);
        }
        else {
          mimeType = (response.headers['content-type'] || '').replace(/;.*/, '');
          extension = mime.extension(mimeType);
        }
        if (mimeTypes.indexOf(mimeType) === -1) {
          return res.status(404).send('Expected content type ' + mimeTypes.join(', ') + ', got ' + mimeType);
        }

        // @see https://github.com/aheckmann/gm#constructor
        var im = imageMagick(response, 'image.' + extension);
          // @see http://www.imagemagick.org/Usage/thumbnails/#cut
        if (width && width !== 'undefined') {
          if (height && height !== 'undefined') {
            im.resize(width, height, '^>');
          } else {
            im.resize(width, null, '^>');
          }
        } else if (height && height !== 'undefined') {
          im.resize(null, height, '^>');
        }

        im.gravity('Center'); // faces are most often near the center
        if (width && height && width !== 'undefined' && height !== 'undefined') {
          im.extent(width, height);
        }

        im.stream(function (err, stdout, stderr) {
            if (err) return next(err);
            // Log errors in production.
            stderr.pipe(process.stderr);
            // @see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html
            res.writeHead(200, {
              'Content-Type': mimeType,
              'Cache-Control': 'max-age=31536000, public', // 1 year
            });
            stdout.pipe(res);
          });
      }).on('error', next);

  // Timeout after five seconds. Better luck next time.
  request.setTimeout(delay, function () {
    timeout = true; // if we abort, we'll get a "socket hang up" error
    return res.status(504).send();
  });
}

function handleRequest(req, res, next, width, height, extension) {
  // Validate parameters.
  if (whitelist.length) {
    var parts = url.parse(req.params.url);
    if (parts.hostname) {
      var any = false, _i, _len;
      if (typeof whitelist === 'string') {
        whitelist = whitelist.split(',');
      }
      for (_i = 0, _len = whitelist.length; _i < _len; _i++) {
        if (typeof whitelist[_i] === 'string') {
          // Escape periods and add anchor.
          whitelist[_i] = new RegExp(whitelist[_i].replace('.', '\\.') + '$')
        }
        if (whitelist[_i].test(parts.hostname)) {
          any = true;
          break;
        }
      }
      if (!any) { // if none
        return res.status(404).send('Expected URI host to be whitelisted');
      }
    }
  }
  retrieveRemote(res, next, 'http://s3.amazonaws.com/qmerce-static-media/' + req.params.url, width, height, extension);
}

module.exports = function () {
  app.get('/:url', function (req, res, next) {
    var defaultWidth = 1200;
    handleRequest(req, res, next, defaultWidth, null, null);
  });

  app.get('/:url/:width/:height.:extension?', function (req, res, next) {
    var width = req.params.width
      , height = req.params.height
      , extension = req.params.extension;

    if (isNaN(parseInt(width)) && width !== 'undefined') {
      return res.status(404).send('Expected width to be an integer');
    }
    if (isNaN(parseInt(height)) && height !== 'undefined') {
      return res.status(404).send('Expected height to be an integer');
    }
    handleRequest(req, res, next, width, height, extension);
  });

  return app;
};
<commit_msg>Fix. Removing the fill feature.<commit_after>// @see https://devcenter.heroku.com/articles/nodejs#write-your-app

var express = require('express')
  , fs      = require('fs') // node
  , gm      = require('gm')
  , http    = require('http') // node
  , https   = require('https') // node
  , mime    = require('mime')
  , url     = require('url') // node
  // @see http://aaronheckmann.posterous.com/graphicsmagick-on-heroku-with-nodejs
  , app = express()
  , imageMagick = gm.subClass({imageMagick: true})
  , whitelist = process.env.WHITELIST || [] // [/\.gov$/, /google\.com$/]
  , delay = parseInt(process.env.DELAY) || 5000
  , mimeTypes = [
    'image/gif',
    'image/jpeg',
    'image/png',
    // Common typos
    'image/jpg'
  ];

function retrieveRemote(res, next, remote, width, height, extension) {
  console.log('DEBUG - remote=' + remote);
  console.log('DEBUG - width=' + width);
  console.log('DEBUG - height=' + height);
  // @see http://nodejs.org/api/url.html#url_url
  var options = url.parse(remote);
  // @see https://github.com/substack/hyperquest
  options.agent = false;
  if (options.protocol !== 'http:' && options.protocol !== 'https:') {
    return res.status(404).send('Expected URI scheme to be HTTP or HTTPS');
  }
  if (!options.hostname) {
    return res.status(404).send('Expected URI host to be non-empty');
  }
  options.headers = {'User-Agent': 'image-proxy/0.0.6'}

  var agent = options.protocol === 'http:' ? http : https
    , timeout = false
      // @see http://nodejs.org/api/http.html#http_http_get_options_callback
    , request = agent.get(options, function (response) {
        if (timeout) {
          // Status code 504 already sent.
          return;
        }

        // @see http://nodejs.org/api/http.html#http_response_statuscode
        if ((response.statusCode === 301 || response.statusCode === 302) && response.headers['location']) {
          var redirect = url.parse(response.headers['location']);
          // @see https://tools.ietf.org/html/rfc7231#section-7.1.2
          if (!redirect.protocol) {
            redirect.protocol = options.protocol;
          }
          if (!redirect.hostname) {
            redirect.hostname = options.hostname;
          }
          if (!redirect.port) {
            redirect.port = options.port;
          }
          if (!redirect.hash) {
            redirect.hash = options.hash;
          }
          return retrieveRemote(res, next, url.format(redirect), weight, height, extension);
        }

        // The image must return status code 200.
        if (response.statusCode !== 200) {
          return res.status(404).send('Expected response code 200, got ' + response.statusCode);
        }

        // The image must be a valid content type.
        // @see http://nodejs.org/api/http.html#http_request_headers
        var mimeType;
        if (extension) {
          mimeType = mime.lookup(extension);
        }
        else {
          mimeType = (response.headers['content-type'] || '').replace(/;.*/, '');
          extension = mime.extension(mimeType);
        }
        if (mimeTypes.indexOf(mimeType) === -1) {
          return res.status(404).send('Expected content type ' + mimeTypes.join(', ') + ', got ' + mimeType);
        }

        // @see https://github.com/aheckmann/gm#constructor
        var im = imageMagick(response, 'image.' + extension);
          // @see http://www.imagemagick.org/Usage/thumbnails/#cut
        if (width && width !== 'undefined') {
          if (height && height !== 'undefined') {
            im.resize(width, height, '>');
          } else {
            im.resize(width, null, '>');
          }
        } else if (height && height !== 'undefined') {
          im.resize(null, height, '>');
        }

        im.gravity('Center'); // faces are most often near the center
        if (width && height && width !== 'undefined' && height !== 'undefined') {
          im.extent(width, height);
        }

        im.stream(function (err, stdout, stderr) {
            if (err) return next(err);
            // Log errors in production.
            stderr.pipe(process.stderr);
            // @see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html
            res.writeHead(200, {
              'Content-Type': mimeType,
              'Cache-Control': 'max-age=31536000, public', // 1 year
            });
            stdout.pipe(res);
          });
      }).on('error', next);

  // Timeout after five seconds. Better luck next time.
  request.setTimeout(delay, function () {
    timeout = true; // if we abort, we'll get a "socket hang up" error
    return res.status(504).send();
  });
}

function handleRequest(req, res, next, width, height, extension) {
  // Validate parameters.
  if (whitelist.length) {
    var parts = url.parse(req.params.url);
    if (parts.hostname) {
      var any = false, _i, _len;
      if (typeof whitelist === 'string') {
        whitelist = whitelist.split(',');
      }
      for (_i = 0, _len = whitelist.length; _i < _len; _i++) {
        if (typeof whitelist[_i] === 'string') {
          // Escape periods and add anchor.
          whitelist[_i] = new RegExp(whitelist[_i].replace('.', '\\.') + '$')
        }
        if (whitelist[_i].test(parts.hostname)) {
          any = true;
          break;
        }
      }
      if (!any) { // if none
        return res.status(404).send('Expected URI host to be whitelisted');
      }
    }
  }
  retrieveRemote(res, next, 'http://s3.amazonaws.com/qmerce-static-media/' + req.params.url, width, height, extension);
}

module.exports = function () {
  app.get('/:url', function (req, res, next) {
    var defaultWidth = 1200;
    handleRequest(req, res, next, defaultWidth, null, null);
  });

  app.get('/:url/:width/:height.:extension?', function (req, res, next) {
    var width = req.params.width
      , height = req.params.height
      , extension = req.params.extension;

    if (isNaN(parseInt(width)) && width !== 'undefined') {
      return res.status(404).send('Expected width to be an integer');
    }
    if (isNaN(parseInt(height)) && height !== 'undefined') {
      return res.status(404).send('Expected height to be an integer');
    }
    handleRequest(req, res, next, width, height, extension);
  });

  return app;
};
<|endoftext|>


<commit_before>const url = require('url');
const path = require('path');
const http = require('http');
const imageSize = require('image-size');
const ratio = require('./ratio.js');

module.exports = {
    default: function(options) {
        options = options || {};
        options.root = options.root || '.';
        options.processEmptySize = options.processEmptySize || false;

        const projectRoot = path.resolve(options.root);

        return function(tree) {
            const htmlRelativePath = path.relative(process.cwd(), path.dirname(tree.options.to || ''));

            const promises = [];

            tree.match({ tag: 'img' }, img => {
                img.attrs = img.attrs || {};

                if (!img.attrs.src) {
                    return img;
                }

                const imagePath = translatePath(projectRoot, htmlRelativePath, img.attrs.src);

                if (!img.attrs.width && options.processEmptySize) {
                    img.attrs.width = 'auto';
                }

                if (!img.attrs.height && options.processEmptySize) {
                    img.attrs.height = 'auto';
                }

                promises.push(
                    getImageDimensions(imagePath).then(dimensions => {
                        dimensions = ratio(
                            { width: img.attrs.width, height: img.attrs.height },
                            dimensions
                        );

                        if (dimensions.width) {
                            img.attrs.width = dimensions.width;
                        }

                        if (dimensions.height) {
                            img.attrs.height = dimensions.height;
                        }
                    })
                );

                return img;
            });

            return Promise.all(promises).then(() => tree);
        };
    }
};

function translatePath(projectRoot, htmlRelativePath, imgPath) {
    const img = url.parse(imgPath);

    if (img.host) {
        return imgPath;
    }

    const imgProjectPath = path.resolve('/' + htmlRelativePath, imgPath);
    const imgAbsolutePath = path.join(projectRoot, imgProjectPath);

    return imgAbsolutePath;
}

function getImageDimensions(imgPath) {
    const img = url.parse(imgPath);

    if (!img.host) {
        return new Promise((resolve, reject) => {
            imageSize(imgPath, (err, dimensions) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(dimensions);
                }
            });
        });
    }

    return new Promise((resolve, reject) => {
        http.get(imgPath, response => {
            const chunks = [];

            response
                .on('data', chunk => chunks.push(chunk))
                .on('end', () => resolve(imageSize(Buffer.concat(chunks))));
        }).on('error', reject);
    });
}
<commit_msg>Skip images with defined widths and heights<commit_after>const url = require('url');
const path = require('path');
const http = require('http');
const imageSize = require('image-size');
const ratio = require('./ratio.js');

module.exports = {
    default: function(options) {
        options = options || {};
        options.root = options.root || '.';
        options.processEmptySize = options.processEmptySize || false;

        const projectRoot = path.resolve(options.root);

        return function(tree) {
            const htmlRelativePath = path.relative(process.cwd(), path.dirname(tree.options.to || ''));

            const promises = [];

            tree.match({ tag: 'img' }, img => {
                img.attrs = img.attrs || {};

                if (!img.attrs.src) {
                    return img;
                }

                const hasDimensions =
                    !isNaN(parseInt(img.attrs.width)) &&
                    !isNaN(parseInt(img.attrs.height));
                if (hasDimensions) {
                    return img;
                }

                const imagePath = translatePath(projectRoot, htmlRelativePath, img.attrs.src);

                if (!img.attrs.width && options.processEmptySize) {
                    img.attrs.width = 'auto';
                }

                if (!img.attrs.height && options.processEmptySize) {
                    img.attrs.height = 'auto';
                }

                promises.push(
                    getImageDimensions(imagePath).then(dimensions => {
                        dimensions = ratio(
                            { width: img.attrs.width, height: img.attrs.height },
                            dimensions
                        );

                        if (dimensions.width) {
                            img.attrs.width = dimensions.width;
                        }

                        if (dimensions.height) {
                            img.attrs.height = dimensions.height;
                        }
                    })
                );

                return img;
            });

            return Promise.all(promises).then(() => tree);
        };
    }
};

function translatePath(projectRoot, htmlRelativePath, imgPath) {
    const img = url.parse(imgPath);

    if (img.host) {
        return imgPath;
    }

    const imgProjectPath = path.resolve('/' + htmlRelativePath, imgPath);
    const imgAbsolutePath = path.join(projectRoot, imgProjectPath);

    return imgAbsolutePath;
}

function getImageDimensions(imgPath) {
    const img = url.parse(imgPath);

    if (!img.host) {
        return new Promise((resolve, reject) => {
            imageSize(imgPath, (err, dimensions) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(dimensions);
                }
            });
        });
    }

    return new Promise((resolve, reject) => {
        http.get(imgPath, response => {
            const chunks = [];

            response
                .on('data', chunk => chunks.push(chunk))
                .on('end', () => resolve(imageSize(Buffer.concat(chunks))));
        }).on('error', reject);
    });
}
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');

var data = [];

function load() {
  fs.readFile('./data/information.json', 'utf8', function (error, data) {
    if (error) {
      console.error('failed to load information.json');
      return;
    }

    try {
      data = JSON.parse(data);
    } catch (e) {
      console.error('failed to parse information.json');
    }
  });
}

load();

process.on('SIGHUP', load);

module.exports = {
  getData: function () {
    return data;
  }
};
<commit_msg>Fixed module<commit_after>'use strict';

var fs = require('fs');

var info = [];

function load() {
  fs.readFile(__dirname + '/data/information.json', 'utf8', function (error, data) {
    if (error) {
      console.error('failed to load information.json');
      return;
    }

    try {
      info = JSON.parse(data);
    } catch (e) {
      console.error('failed to parse information.json');
    }
  });
}

load();

process.on('SIGHUP', load);

module.exports = {
  getData: function () {
    return info;
  }
};
<|endoftext|>


<commit_before>var fs = require('fs'),
    Q = require('q'),
    path = require('path'),
    argv = require('optimist').argv,
    connect = require('connect'),
    finalhandler = require('finalhandler'),
    http = require('http'),
    serveStatic = require('serve-static'),
    tinylr = require('tiny-lr-fork'),
    lr = require('connect-livereload'),
    vfs = require('vinyl-fs'),
    request = require('request'),
    IonicProject = require('./project'),
    Task = require('./task').Task,
    proxyMiddleware = require('proxy-middleware'),
    url = require('url'),
    xml2js = require('xml2js'),
    IonicStats = require('./stats').IonicStats,
    ionicCliLib = require('ionic-app-lib'),
    Serve = ionicCliLib.serve,
    shelljs = require('shelljs'),
    events = ionicCliLib.events,
    ports = require('./ports');

var DEFAULT_HTTP_PORT = 8100;
var DEFAULT_LIVE_RELOAD_PORT = 35729;
var IONIC_LAB_URL = '/ionic-lab';

var IonicTask = function() {};

IonicTask.prototype = new Task();

IonicTask.prototype.run = function(ionic) {
  var self = this;
  this.ionic = ionic;
  this.port = argv._[1];
  this.liveReloadPort = argv._[2];

  var options = Serve.loadSettings(argv);
  options.appDirectory = process.cwd();//called from cli - ionic serve - cwd is this
  //TODO: Look up address stuffs

  var promise;

  try {
    if (argv.allIps) {
      options.address = '0.0.0.0';
      promise = Q();
    } else {
      promise = Serve.getAddress(options);
    }

    return promise
    .then(function() {
      return Serve.checkPorts(true, options.port, options.address, options);
    })
    .then(function() {
      if(options.runLivereload) {
        return Serve.checkPorts(false, options.liveReloadPort, options.address, options);
      }
    })
    .then(function() {
      return Serve.start(options);
    })
    .then(function() {
      return Serve.showFinishedServeMessage(options);
    })
    .then(function() {
      // return Serve.listenToServeLogs(options);
      events.on('serverlog', console.log);
      events.on('consolelog', console.log);
    })
    .catch(function(error) {
      console.log('There was an error serving your Ionic application:', error);
      throw error;
    })
  } catch (ex) {
    Utils.fail('Error with serve- ' + ex);
    console.log(ex.stack);
  }

  // return;
  // this.loadSettings();

  // this.getAddress()
  // .then(function() {
  //   return self.checkPorts(true, self.port, self.address);
  // })
  // .then(function() {
  //   if(self.runLivereload) {
  //     return self.checkPorts(false, self.liveReloadPort, self.address);
  //   }
  // })
  // .then(function() {
  //   if(self.isAddressCmd) {
  //     console.log( self.address );
  //     process.exit();
  //   }

  //   self.start(ionic)

  //   // self.printCommandTips();
  //   // console.log('listenForServerCommands')
  //   // self.listenForServerCommands();

  //   if(ionic.hasFailed) return;

  //   ionic.latestVersion.promise.then(function(){
  //     ionic.printVersionWarning();
  //   });
  // })
  // .catch(function(error) {

  // })

};

//isIonicServer = true when serving www directory, false when live reload
IonicTask.prototype.checkPorts = function(isIonicServer, testPort, testHost) {
  var q = Q.defer();

  var self = this,
      message = [];

  if(isIonicServer) {
    testHost = this.address == 'localhost' ? null : this.address;
  } else {
    testHost = null;
  }

  ports.getPort({port: testPort, host: testHost},
    function(err, port) {
    if(port != testPort) {
      message = ['The port ', testPort, ' was taken on the host ', self.address, ' - using port ', port, ' instead'].join('');
      console.log(message.yellow.bold);
      if(isIonicServer) {
        self.port = port;
      } else {
        self.liveReloadPort = port;
      }
    }
    q.resolve();
  });

  return q.promise;
}

IonicTask.prototype.loadSettings = function(cb) {
  var project = null;

  try {
    project = IonicProject.load();
  } catch (ex) {
    this.ionic.fail(ex.message);
    return
  }
  var self = this;

  this.port = this.port || argv.port || argv.p || DEFAULT_HTTP_PORT;
  this.liveReloadPort = this.liveReloadPort || argv.livereloadport || argv.r || argv['livereload-port'] || argv.i || DEFAULT_LIVE_RELOAD_PORT;
  this.launchBrowser = !argv.nobrowser && !argv.b;
  this.launchLab = this.launchBrowser && (argv.lab || argv.l);
  this.runLivereload = !(argv.nolivereload || argv.d);
  this.useProxy = !argv.noproxy && !argv.x;
  this.proxies = project.get('proxies') || [];
  this.watchSass = project.get('sass') === true && !argv.nosass && !argv.n;
  this.gulpStartupTasks = project.get('gulpStartupTasks');

  this.browser = argv.browser || argv.w || '';
  this.browserOption = argv.browserOption || argv.o || '';

  //Check for default browser being specified
  this.defaultBrowser = argv.defaultBrowser || argv.f || project.get('defaultBrowser');

  if(this.defaultBrowser) {
    project.set('defaultBrowser', this.defaultBrowser);
    project.save();
  }

  this.browser = this.browser || this.defaultBrowser;

  this.watchPatterns = project.get('watchPatterns') || ['www/**/*', '!www/lib/**/*'];
  this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c;
  this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s;
  this.isAddressCmd = argv._[0].toLowerCase() == 'address';
  this.documentRoot = project.get('documentRoot') || 'www';
  this.createDocumentRoot = project.get('createDocumentRoot') || null;
  this.contentSrc = path.join(this.documentRoot, this.ionic.getContentSrc());

};

IonicTask.prototype.getAddress = function(cb) {
  var q = Q.defer();
  try {
    var self = this;
    var addresses = [];
    var os = require('os');
    var ifaces = os.networkInterfaces();
    var ionicConfig = require('./config').load();

    var addressConfigKey = (self.isPlatformServe ? 'platformServeAddress' : 'ionicServeAddress');
    var tryAddress;

    if(self.isAddressCmd) {
      // reset any address configs
      ionicConfig.set('ionicServeAddress', null);
      ionicConfig.set('platformServeAddress', null);
    } else {
      if(!argv.address)
        tryAddress = ionicConfig.get(addressConfigKey);
      else
        tryAddress = argv.address;
    }

    if(ifaces){
      for (var dev in ifaces) {
        if(!dev) continue;
        ifaces[dev].forEach(function(details){
          if (details && details.family == 'IPv4' && !details.internal && details.address) {
            addresses.push({
              address: details.address,
              dev: dev
            });
          }
        });
      }
    }

    if(tryAddress) {
      if(tryAddress == 'localhost') {
        self.address = tryAddress;
        // cb();
        q.resolve();
        return q.promise;
      }
      for(var x=0; x<addresses.length; x++) {
        // double check if this address is still available
        if(addresses[x].address == tryAddress)
        {
          self.address = addresses[x].address;
          // cb();
          q.resolve();
          return q.promise;
        }
      }
      if (argv.address) {
        self.ionic.fail('Address ' + argv.address + ' not available.');
        return q.promise;
      }
    }

    if(addresses.length > 0) {

      if(!self.isPlatformServe) {
        addresses.push({
          address: 'localhost'
        });
      }

      if(addresses.length === 1) {
        self.address = addresses[0].address;
        // cb();
        q.resolve();
        return q.promise;
      }

      console.log('\nMultiple addresses available.'.error.bold);
      console.log('Please select which address to use by entering its number from the list below:'.error.bold);
      if(self.isPlatformServe) {
        console.log('Note that the emulator/device must be able to access the given IP address'.small);
      }

      for(var x=0; x<addresses.length; x++) {
        console.log( (' ' + (x+1) + ') ' + addresses[x].address + ( addresses[x].dev ? ' (' + addresses[x].dev + ')' : '' )).yellow );
      }

      console.log('Std in before prompt')
      // console.log(process.stdin)

      var prompt = require('prompt');
      var promptProperties = {
        selection: {
          name: 'selection',
          description: 'Address Selection: '.yellow.bold,
          required: true
        }
      };

      prompt.override = argv;
      prompt.message = '';
      prompt.delimiter = '';
      prompt.start();

      prompt.get({properties: promptProperties}, function (err, promptResult) {
        if(err) {
          return console.log(err);
        }

        var selection = promptResult.selection;
        for(var x=0; x<addresses.length; x++) {
          if(selection == (x + 1) || selection == addresses[x].address || selection == addresses[x].dev) {
            self.address = addresses[x].address;
            if(!self.isAddressCmd) {
              console.log('Selected address: '.green.bold + self.address);
            }
            ionicConfig.set(addressConfigKey, self.address);
            // cb();
            prompt.resume();
            q.resolve();
            return q.promise;
          }
        }

        self.ionic.fail('Invalid address selection');
      });

    } else if(self.isPlatformServe) {
      // no addresses found
      self.ionic.fail('Unable to find an IPv4 address for run/emulate live reload.\nIs WiFi disabled or LAN disconnected?');

    } else {
      // no address found, but doesn't matter if it doesn't need an ip address and localhost will do
      self.address = 'localhost';
      // cb();
      q.resolve();
    }

  } catch(e) {
    self.ionic.fail('Error getting IPv4 address: ' + e);
  }

  return q.promise;
};

exports.IonicTask = IonicTask;
<commit_msg>Adding flag for all ips to be served<commit_after>var fs = require('fs'),
    Q = require('q'),
    path = require('path'),
    argv = require('optimist').argv,
    connect = require('connect'),
    finalhandler = require('finalhandler'),
    http = require('http'),
    serveStatic = require('serve-static'),
    tinylr = require('tiny-lr-fork'),
    lr = require('connect-livereload'),
    vfs = require('vinyl-fs'),
    request = require('request'),
    IonicProject = require('./project'),
    Task = require('./task').Task,
    proxyMiddleware = require('proxy-middleware'),
    url = require('url'),
    xml2js = require('xml2js'),
    IonicStats = require('./stats').IonicStats,
    ionicCliLib = require('ionic-app-lib'),
    Serve = ionicCliLib.serve,
    shelljs = require('shelljs'),
    events = ionicCliLib.events,
    ports = require('./ports');

var DEFAULT_HTTP_PORT = 8100;
var DEFAULT_LIVE_RELOAD_PORT = 35729;
var IONIC_LAB_URL = '/ionic-lab';

var IonicTask = function() {};

IonicTask.prototype = new Task();

IonicTask.prototype.run = function(ionic) {
  var self = this;
  this.ionic = ionic;
  this.port = argv._[1];
  this.liveReloadPort = argv._[2];

  var options = Serve.loadSettings(argv);
  options.appDirectory = process.cwd();//called from cli - ionic serve - cwd is this
  //TODO: Look up address stuffs

  var promise;

  try {
    if (argv.all) {
      options.address = '0.0.0.0';
      promise = Q();
    } else {
      promise = Serve.getAddress(options);
    }

    return promise
    .then(function() {
      return Serve.checkPorts(true, options.port, options.address, options);
    })
    .then(function() {
      if(options.runLivereload) {
        return Serve.checkPorts(false, options.liveReloadPort, options.address, options);
      }
    })
    .then(function() {
      return Serve.start(options);
    })
    .then(function() {
      return Serve.showFinishedServeMessage(options);
    })
    .then(function() {
      // return Serve.listenToServeLogs(options);
      events.on('serverlog', console.log);
      events.on('consolelog', console.log);
    })
    .catch(function(error) {
      console.log('There was an error serving your Ionic application:', error);
      throw error;
    })
  } catch (ex) {
    Utils.fail('Error with serve- ' + ex);
    console.log(ex.stack);
  }

  // return;
  // this.loadSettings();

  // this.getAddress()
  // .then(function() {
  //   return self.checkPorts(true, self.port, self.address);
  // })
  // .then(function() {
  //   if(self.runLivereload) {
  //     return self.checkPorts(false, self.liveReloadPort, self.address);
  //   }
  // })
  // .then(function() {
  //   if(self.isAddressCmd) {
  //     console.log( self.address );
  //     process.exit();
  //   }

  //   self.start(ionic)

  //   // self.printCommandTips();
  //   // console.log('listenForServerCommands')
  //   // self.listenForServerCommands();

  //   if(ionic.hasFailed) return;

  //   ionic.latestVersion.promise.then(function(){
  //     ionic.printVersionWarning();
  //   });
  // })
  // .catch(function(error) {

  // })

};

//isIonicServer = true when serving www directory, false when live reload
IonicTask.prototype.checkPorts = function(isIonicServer, testPort, testHost) {
  var q = Q.defer();

  var self = this,
      message = [];

  if(isIonicServer) {
    testHost = this.address == 'localhost' ? null : this.address;
  } else {
    testHost = null;
  }

  ports.getPort({port: testPort, host: testHost},
    function(err, port) {
    if(port != testPort) {
      message = ['The port ', testPort, ' was taken on the host ', self.address, ' - using port ', port, ' instead'].join('');
      console.log(message.yellow.bold);
      if(isIonicServer) {
        self.port = port;
      } else {
        self.liveReloadPort = port;
      }
    }
    q.resolve();
  });

  return q.promise;
}

IonicTask.prototype.loadSettings = function(cb) {
  var project = null;

  try {
    project = IonicProject.load();
  } catch (ex) {
    this.ionic.fail(ex.message);
    return
  }
  var self = this;

  this.port = this.port || argv.port || argv.p || DEFAULT_HTTP_PORT;
  this.liveReloadPort = this.liveReloadPort || argv.livereloadport || argv.r || argv['livereload-port'] || argv.i || DEFAULT_LIVE_RELOAD_PORT;
  this.launchBrowser = !argv.nobrowser && !argv.b;
  this.launchLab = this.launchBrowser && (argv.lab || argv.l);
  this.runLivereload = !(argv.nolivereload || argv.d);
  this.useProxy = !argv.noproxy && !argv.x;
  this.proxies = project.get('proxies') || [];
  this.watchSass = project.get('sass') === true && !argv.nosass && !argv.n;
  this.gulpStartupTasks = project.get('gulpStartupTasks');

  this.browser = argv.browser || argv.w || '';
  this.browserOption = argv.browserOption || argv.o || '';

  //Check for default browser being specified
  this.defaultBrowser = argv.defaultBrowser || argv.f || project.get('defaultBrowser');

  if(this.defaultBrowser) {
    project.set('defaultBrowser', this.defaultBrowser);
    project.save();
  }

  this.browser = this.browser || this.defaultBrowser;

  this.watchPatterns = project.get('watchPatterns') || ['www/**/*', '!www/lib/**/*'];
  this.printConsoleLogs = argv.consolelogs || argv['console-logs'] || argv.c;
  this.printServerLogs = argv.serverlogs || argv['server-logs'] || argv.s;
  this.isAddressCmd = argv._[0].toLowerCase() == 'address';
  this.documentRoot = project.get('documentRoot') || 'www';
  this.createDocumentRoot = project.get('createDocumentRoot') || null;
  this.contentSrc = path.join(this.documentRoot, this.ionic.getContentSrc());

};

IonicTask.prototype.getAddress = function(cb) {
  var q = Q.defer();
  try {
    var self = this;
    var addresses = [];
    var os = require('os');
    var ifaces = os.networkInterfaces();
    var ionicConfig = require('./config').load();

    var addressConfigKey = (self.isPlatformServe ? 'platformServeAddress' : 'ionicServeAddress');
    var tryAddress;

    if(self.isAddressCmd) {
      // reset any address configs
      ionicConfig.set('ionicServeAddress', null);
      ionicConfig.set('platformServeAddress', null);
    } else {
      if(!argv.address)
        tryAddress = ionicConfig.get(addressConfigKey);
      else
        tryAddress = argv.address;
    }

    if(ifaces){
      for (var dev in ifaces) {
        if(!dev) continue;
        ifaces[dev].forEach(function(details){
          if (details && details.family == 'IPv4' && !details.internal && details.address) {
            addresses.push({
              address: details.address,
              dev: dev
            });
          }
        });
      }
    }

    if(tryAddress) {
      if(tryAddress == 'localhost') {
        self.address = tryAddress;
        // cb();
        q.resolve();
        return q.promise;
      }
      for(var x=0; x<addresses.length; x++) {
        // double check if this address is still available
        if(addresses[x].address == tryAddress)
        {
          self.address = addresses[x].address;
          // cb();
          q.resolve();
          return q.promise;
        }
      }
      if (argv.address) {
        self.ionic.fail('Address ' + argv.address + ' not available.');
        return q.promise;
      }
    }

    if(addresses.length > 0) {

      if(!self.isPlatformServe) {
        addresses.push({
          address: 'localhost'
        });
      }

      if(addresses.length === 1) {
        self.address = addresses[0].address;
        // cb();
        q.resolve();
        return q.promise;
      }

      console.log('\nMultiple addresses available.'.error.bold);
      console.log('Please select which address to use by entering its number from the list below:'.error.bold);
      if(self.isPlatformServe) {
        console.log('Note that the emulator/device must be able to access the given IP address'.small);
      }

      for(var x=0; x<addresses.length; x++) {
        console.log( (' ' + (x+1) + ') ' + addresses[x].address + ( addresses[x].dev ? ' (' + addresses[x].dev + ')' : '' )).yellow );
      }

      console.log('Std in before prompt')
      // console.log(process.stdin)

      var prompt = require('prompt');
      var promptProperties = {
        selection: {
          name: 'selection',
          description: 'Address Selection: '.yellow.bold,
          required: true
        }
      };

      prompt.override = argv;
      prompt.message = '';
      prompt.delimiter = '';
      prompt.start();

      prompt.get({properties: promptProperties}, function (err, promptResult) {
        if(err) {
          return console.log(err);
        }

        var selection = promptResult.selection;
        for(var x=0; x<addresses.length; x++) {
          if(selection == (x + 1) || selection == addresses[x].address || selection == addresses[x].dev) {
            self.address = addresses[x].address;
            if(!self.isAddressCmd) {
              console.log('Selected address: '.green.bold + self.address);
            }
            ionicConfig.set(addressConfigKey, self.address);
            // cb();
            prompt.resume();
            q.resolve();
            return q.promise;
          }
        }

        self.ionic.fail('Invalid address selection');
      });

    } else if(self.isPlatformServe) {
      // no addresses found
      self.ionic.fail('Unable to find an IPv4 address for run/emulate live reload.\nIs WiFi disabled or LAN disconnected?');

    } else {
      // no address found, but doesn't matter if it doesn't need an ip address and localhost will do
      self.address = 'localhost';
      // cb();
      q.resolve();
    }

  } catch(e) {
    self.ionic.fail('Error getting IPv4 address: ' + e);
  }

  return q.promise;
};

exports.IonicTask = IonicTask;
<|endoftext|>