<commit_before>var _ = require('lodash')
  , async = require('async')
  , chalk = require('chalk')
  , cssParser = require('css')
  , CssSelectorParser = require('css-selector-parser').CssSelectorParser
  , fs = require('fs')
  , glob = require('glob')
  , jadeLexer = require('jade-lexer')
  , jadeParser = require('jade-parser')
  , jadeWalk = require('jade-walk')
  , minimatch = require('minimatch')
  , os = require('os')
  , path = require('path')
  , SourcemapConsumer = require('source-map').SourceMapConsumer
  , sourcemapParser = require('source-map-resolve')

JSON.minify = JSON.minify || require('jsonminify')

function process(cssFiles, jadeFiles, options, cb) {

  if (_.isFunction(options)) {
    cb = options
    options = {}
  } else if (!_.isFunction(cb)) {
    throw new TypeError('Stylperjade: expected a callback')
  }

  options = loadOptions(cssFiles, jadeFiles, options)

  if (!options.cssFiles.length) {
    throw new Error('Stylperjade: no CSS files found')
  }

  if (!options.jadeFiles.length) {
    throw new Error('Stylperjade: no Jade files found')
  }

  var cssClasses = []
    , jadeClasses = []

  async.parallelLimit(
    { css: function (cb) {
        async.eachLimit(options.cssFiles, os.cpus().length, processCssFile, function (err) {
          if (err) return cb(err)

          return cb(null, cssClasses)
        })
      }
    , jade: function (cb) {
        async.eachLimit(options.jadeFiles, os.cpus().length, processJadeFile, function (err) {
          if (err) return cb(err)

          return cb(null, jadeClasses)
        })
      }
    }
    , os.cpus().length
    , function (err, classes) {
        if (err)  return cb('Stylperjade: ' + err.message)

        var cssBlacklisted = filterBlacklistedClasses(classes.css, options.cssBlacklist)
          , jadeBlacklisted = filterBlacklistedClasses(classes.jade, options.jadeBlacklist)
          , blacklistedCount = cssBlacklisted.length + jadeBlacklisted.length
          , cssUnused = filterUnusedClasses(classes.css, classes.jade, options.cssWhitelist)
          , jadeUnused = filterUnusedClasses(classes.jade, classes.css, options.jadeWhitelist)
          , unusedCount = cssUnused.length + jadeUnused.length
          , results =
            { blacklistedTotal: blacklistedCount
            , blacklistedCssClasses: cssBlacklisted
            , blacklistedCssCount: cssBlacklisted.length
            , blacklistedJadeClasses: jadeBlacklisted
            , blacklistedJadeCount: jadeBlacklisted.length
            , unusedTotal: unusedCount
            , unusedCssClasses: cssUnused
            , unusedCssCount: cssUnused.length
            , unusedJadeClasses: jadeUnused
            , unusedJadeCount: jadeUnused.length
            }
          , report = generateReport
            ( 'unused'
            , unusedCount
            , cssUnused
            , jadeUnused
            , generateReport('blacklisted', blacklistedCount, cssBlacklisted, jadeBlacklisted)
            )

          results.report = report.join('\n')

        return cb(null, results)
      }
  )

  function filterBlacklistedClasses(a, blacklist) {

    return _(a)
      .compact()
      .filter(function (foundClass) {
        return _.any(blacklist, function (blacklistClass) {
          return minimatch(foundClass.name, blacklistClass)
        })
      })
      .sortBy('name')
      .value()

  }

  function filterUnusedClasses(a, b, whitelist) {

    return _(a)
      .compact()
      .reject(function (aClass) {
        // Remove any classes that exist in ignored files
        return _.any(options.ignoreFiles, function (ignoreFile) {
          var ignoreLocation = _.find(aClass.locations, function (location) {
            if (location.file) {
              return minimatch(location.file, ignoreFile)
            } else {
              return false
            }
          })

          if (ignoreLocation) {
            if (aClass.locations.length > 1) {
              // Remove location entries for ignored files
              _.remove(aClass.locations, function (location) {
                return minimatch(location.file, ignoreFile)
              })

              // Check any location entries still exist
              return aClass.locations.length === 0
            }

            return true
          }

          return false
        })
      })
      .reject(function (aClass) {
        return _.any(whitelist, function (whitelistClass) {
          if (path.extname(whitelistClass)) {
            // Remove any classes that exist in whitelisted files
            var whitelistedLocation = _.find(aClass.locations, function (location) {
              return path.basename(location.file) === whitelistClass
            })

            if (whitelistedLocation) {
              if (aClass.locations.length > 1) {
                // Remove location entries for whitelisted files
                _.remove(aClass.locations, function (location) {
                  return path.basename(location.file) === whitelistClass
                })

                // Check any location entries still exist
                return aClass.locations.length === 0
              }

              return true
            }

            return false
          } else {
            // Remove any classes that are white listed
            return minimatch(aClass.name, whitelistClass)
          }
        })
      })
      .reject(function (aClass) {
        // Remove classes that exist in both CSS and Jade files
        return _.any(b, function (bClass) {
          return aClass.name === bClass.name
        })
      })
      .sortBy('name')
      .value()

  }

  function generateReport(type, count, cssClasses, jadeClasses, report) {

    report = report || []
    if (report.length > 0) report.push('')

    if (count > 0) {
      report.push(chalk.red('✘ Stylperjade: ' + count + ' ' + type + ' classes found'))

      generateFileReport(type, 'CSS', cssClasses)
      generateFileReport(type, 'Jade', jadeClasses)
    } else {
      report.push(chalk.green('✔ Stylperjade: No ' + type + ' classes found'))
    }

    function generateFileReport(type, fileType, collection) {

      if (collection.length > 0) {
        report.push('')
        report.push(chalk.cyan(collection.length + ' ' + type + ' classes found in ' + fileType + ' files:'))

        collection.forEach(function (className) {
          report.push('- ' + className.name)

          _.sortBy(className.locations, 'file').forEach(function (location) {
            if (location.file) {
              report.push('  ' + chalk.grey(location.file + ':' + location.line
                + (location.column ? ':' + location.column : '')))
            }
          })
        })
      }

    }

    return report

  }

  function loadOptions(cssPatterns, jadePatterns, options) {

    var stylperjadercFile

    if (_.isEmpty(options)) stylperjadercFile = path.resolve('.stylperjaderc')

    if (options.stylperjaderc) stylperjadercFile = options.stylperjaderc

    if (stylperjadercFile) options = parseConfigFile(stylperjadercFile)

    options = _.defaults(options
      , { cwd: ''
        , ignoreFiles: []
        , cssFiles: []
        , jadeFiles: []
        , cssWhitelist: []
        , cssBlacklist: []
        , jadeWhitelist: []
        , jadeBlacklist: []
        }
    )

    cssPatterns.forEach(function (pattern) {
      options.cssFiles = options.cssFiles.concat(glob.sync(pattern
        , { ignore: options.ignoreFiles, cwd: options.cwd }))
    })

    jadePatterns.forEach(function (pattern) {
      options.jadeFiles = options.jadeFiles.concat(glob.sync(pattern
        , { ignore: options.ignoreFiles, cwd: options.cwd }))
    })

    return options

  }

  function parseConfigFile(file) {

    try {
      var options = JSON.parse(JSON.minify(fs.readFileSync(file, 'utf-8')))

      options.cwd = path.dirname(file)

      return options
    } catch (err) {
      if (err.code === 'ENOENT') err.message = 'Stylperjade: .stylperjaderc not found'
      if (err instanceof SyntaxError) throw new SyntaxError('Stylperjade: .stylperjaderc is invalid JSON')

      throw err
    }

  }

  function parseSourcemapFile(data, file) {

    var sourcemap = sourcemapParser.resolveSourceMapSync(data, file, fs.readFileSync)

    if (sourcemap) return new SourcemapConsumer(sourcemap.map)

    return null

  }

  function processCssClasses(obj, position, sourcemap) {

    switch (obj.type) {
      case 'ruleSet':
        processCssClasses(obj.rule, position, sourcemap)

        break
      case 'rule':
        if (obj.classNames) {
          pushCssClasses(obj.classNames, position, sourcemap)
        }

        if (obj.rule) {
          processCssClasses(obj.rule, position, sourcemap)
        }

        break
    }

  }

  function processCssFile(file, cb) {

    fs.readFile(file, 'utf8', function (err, data) {
      var sourcemap = parseSourcemapFile(data, file)

      try {
        cssParser.parse(data, { silent: false, source: file }).stylesheet.rules.forEach(function (rule) {
          processCssRule(rule, sourcemap)
        })
      } catch (err) {
        err.message = 'CSS file \'' + file + '\' error - ' + err.message

        return cb(err)
      }

      cb()
    })

  }

  function processCssRule(rule, sourcemap) {

    if (rule.rules) {
      rule.rules.forEach(function (rule) {
        processCssRule(rule, sourcemap)
      })
    }

    if (rule.selectors) {
      rule.selectors.forEach(function (selector) {
        processCssSelector(selector, rule.position, sourcemap)
      })
    }

  }

  function processCssSelector(selector, position, sourcemap) {

    var parseSelector = new CssSelectorParser()
        .registerNestingOperators('>', '+', '~')
        .registerAttrEqualityMods('^', '$', '*', '~')

    return processCssClasses(parseSelector.parse(selector), position, sourcemap)

  }

  function processJadeFile(file, cb) {

    fs.readFile(file, 'utf8', function (err, data) {
      try {
        jadeWalk(jadeParser(jadeLexer(data, file), file), function after(node) {
          if (node.type === 'Tag') {
            node.attrs.forEach(function (attr) {
              if (attr.name === 'class') {
                var sourceFile = node.filename ? path.resolve(options.cwd, node.filename) : ''

                if (!attr.escaped) {
                  // Remove single-quotes
                  pushUnique(jadeClasses, attr.val, sourceFile, node.line)
                } else {
                  // Find any single-quoted valid class names in Jade class attribute logic
                  // e.g. currentPage === '/hall-of-fame' ? 'nav-list-item--active' : ''
                  // finds; 'nav-list-item--active'
                  var regex = new RegExp(/\'\s*([_a-zA-Z]+[_a-zA-Z0-9-\s]*)\'(?!\s*\?)/g)
                    , match

                  while ((match = regex.exec(attr.val)) !== null) {
                    pushUnique(jadeClasses, match[1], sourceFile, node.line)
                  }
                }
              }
            })
          }
        })
      } catch (err) {
        err.message = 'Jade file \'' + file + '\' error - ' + err.message

        return cb(err)
      }

      cb()
    })

  }

  function pushCssClasses(classNames, position, sourcemap) {

    var file = path.resolve(options.cwd, position.source)
      , line = position.start.line
      , column = position.start.column

    if (sourcemap) {
      var sourcemapPosition = sourcemap.originalPositionFor({ 'line': line, 'column': column })

      file = path.resolve(options.cwd, sourcemapPosition.source)
      line = sourcemapPosition.line
      column = sourcemapPosition.column
    }

    classNames.forEach(function (className) {
      pushUnique(cssClasses, className, file, line, column)
    })

  }

  function pushUnique(collection, value, file, line, column) {

    var classNames = value.replace(/\'/g, '').split(' ')
      , location = { 'file': file, 'line': line, 'column': column > 1 ? column : null }

    classNames.forEach(function (className) {
      var existingClassName = _.find(collection, 'name', className)

      if (existingClassName) {
        if (!_.find(existingClassName.locations, location)) {
          existingClassName.locations.push(location)
        }
      } else {
        collection.push({ 'name': className, 'locations': [ location ] })
      }
    })

  }

}

module.exports = process
<commit_msg>> Remove `cwd` from globbing to fix paths<commit_after>var _ = require('lodash')
  , async = require('async')
  , chalk = require('chalk')
  , cssParser = require('css')
  , CssSelectorParser = require('css-selector-parser').CssSelectorParser
  , fs = require('fs')
  , glob = require('glob')
  , jadeLexer = require('jade-lexer')
  , jadeParser = require('jade-parser')
  , jadeWalk = require('jade-walk')
  , minimatch = require('minimatch')
  , os = require('os')
  , path = require('path')
  , SourcemapConsumer = require('source-map').SourceMapConsumer
  , sourcemapParser = require('source-map-resolve')

JSON.minify = JSON.minify || require('jsonminify')

function process(cssFiles, jadeFiles, options, cb) {

  if (_.isFunction(options)) {
    cb = options
    options = {}
  } else if (!_.isFunction(cb)) {
    throw new TypeError('Stylperjade: expected a callback')
  }

  options = loadOptions(cssFiles, jadeFiles, options)

  if (!options.cssFiles.length) {
    throw new Error('Stylperjade: no CSS files found')
  }

  if (!options.jadeFiles.length) {
    throw new Error('Stylperjade: no Jade files found')
  }

  var cssClasses = []
    , jadeClasses = []

  async.parallelLimit(
    { css: function (cb) {
        async.eachLimit(options.cssFiles, os.cpus().length, processCssFile, function (err) {
          if (err) return cb(err)

          return cb(null, cssClasses)
        })
      }
    , jade: function (cb) {
        async.eachLimit(options.jadeFiles, os.cpus().length, processJadeFile, function (err) {
          if (err) return cb(err)

          return cb(null, jadeClasses)
        })
      }
    }
    , os.cpus().length
    , function (err, classes) {
        if (err)  return cb('Stylperjade: ' + err.message)

        var cssBlacklisted = filterBlacklistedClasses(classes.css, options.cssBlacklist)
          , jadeBlacklisted = filterBlacklistedClasses(classes.jade, options.jadeBlacklist)
          , blacklistedCount = cssBlacklisted.length + jadeBlacklisted.length
          , cssUnused = filterUnusedClasses(classes.css, classes.jade, options.cssWhitelist)
          , jadeUnused = filterUnusedClasses(classes.jade, classes.css, options.jadeWhitelist)
          , unusedCount = cssUnused.length + jadeUnused.length
          , results =
            { blacklistedTotal: blacklistedCount
            , blacklistedCssClasses: cssBlacklisted
            , blacklistedCssCount: cssBlacklisted.length
            , blacklistedJadeClasses: jadeBlacklisted
            , blacklistedJadeCount: jadeBlacklisted.length
            , unusedTotal: unusedCount
            , unusedCssClasses: cssUnused
            , unusedCssCount: cssUnused.length
            , unusedJadeClasses: jadeUnused
            , unusedJadeCount: jadeUnused.length
            }
          , report = generateReport
            ( 'unused'
            , unusedCount
            , cssUnused
            , jadeUnused
            , generateReport('blacklisted', blacklistedCount, cssBlacklisted, jadeBlacklisted)
            )

          results.report = report.join('\n')

        return cb(null, results)
      }
  )

  function filterBlacklistedClasses(a, blacklist) {

    return _(a)
      .compact()
      .filter(function (foundClass) {
        return _.any(blacklist, function (blacklistClass) {
          return minimatch(foundClass.name, blacklistClass)
        })
      })
      .sortBy('name')
      .value()

  }

  function filterUnusedClasses(a, b, whitelist) {

    return _(a)
      .compact()
      .reject(function (aClass) {
        // Remove any classes that exist in ignored files
        return _.any(options.ignoreFiles, function (ignoreFile) {
          var ignoreLocation = _.find(aClass.locations, function (location) {
            if (location.file) {
              return minimatch(location.file, ignoreFile)
            } else {
              return false
            }
          })

          if (ignoreLocation) {
            if (aClass.locations.length > 1) {
              // Remove location entries for ignored files
              _.remove(aClass.locations, function (location) {
                return minimatch(location.file, ignoreFile)
              })

              // Check any location entries still exist
              return aClass.locations.length === 0
            }

            return true
          }

          return false
        })
      })
      .reject(function (aClass) {
        return _.any(whitelist, function (whitelistClass) {
          if (path.extname(whitelistClass)) {
            // Remove any classes that exist in whitelisted files
            var whitelistedLocation = _.find(aClass.locations, function (location) {
              return path.basename(location.file) === whitelistClass
            })

            if (whitelistedLocation) {
              if (aClass.locations.length > 1) {
                // Remove location entries for whitelisted files
                _.remove(aClass.locations, function (location) {
                  return path.basename(location.file) === whitelistClass
                })

                // Check any location entries still exist
                return aClass.locations.length === 0
              }

              return true
            }

            return false
          } else {
            // Remove any classes that are white listed
            return minimatch(aClass.name, whitelistClass)
          }
        })
      })
      .reject(function (aClass) {
        // Remove classes that exist in both CSS and Jade files
        return _.any(b, function (bClass) {
          return aClass.name === bClass.name
        })
      })
      .sortBy('name')
      .value()

  }

  function generateReport(type, count, cssClasses, jadeClasses, report) {

    report = report || []
    if (report.length > 0) report.push('')

    if (count > 0) {
      report.push(chalk.red('✘ Stylperjade: ' + count + ' ' + type + ' classes found'))

      generateFileReport(type, 'CSS', cssClasses)
      generateFileReport(type, 'Jade', jadeClasses)
    } else {
      report.push(chalk.green('✔ Stylperjade: No ' + type + ' classes found'))
    }

    function generateFileReport(type, fileType, collection) {

      if (collection.length > 0) {
        report.push('')
        report.push(chalk.cyan(collection.length + ' ' + type + ' classes found in ' + fileType + ' files:'))

        collection.forEach(function (className) {
          report.push('- ' + className.name)

          _.sortBy(className.locations, 'file').forEach(function (location) {
            if (location.file) {
              report.push('  ' + chalk.grey(location.file + ':' + location.line
                + (location.column ? ':' + location.column : '')))
            }
          })
        })
      }

    }

    return report

  }

  function loadOptions(cssPatterns, jadePatterns, options) {

    var stylperjadercFile

    if (_.isEmpty(options)) stylperjadercFile = path.resolve('.stylperjaderc')

    if (options.stylperjaderc) stylperjadercFile = options.stylperjaderc

    if (stylperjadercFile) options = parseConfigFile(stylperjadercFile)

    options = _.defaults(options
      , { cwd: ''
        , ignoreFiles: []
        , cssFiles: []
        , jadeFiles: []
        , cssWhitelist: []
        , cssBlacklist: []
        , jadeWhitelist: []
        , jadeBlacklist: []
        }
    )

    cssPatterns.forEach(function (pattern) {
      options.cssFiles = options.cssFiles.concat(glob.sync(pattern
        , { ignore: options.ignoreFiles }))
    })

    jadePatterns.forEach(function (pattern) {
      options.jadeFiles = options.jadeFiles.concat(glob.sync(pattern
        , { ignore: options.ignoreFiles }))
    })

    return options

  }

  function parseConfigFile(file) {

    try {
      var options = JSON.parse(JSON.minify(fs.readFileSync(file, 'utf-8')))

      options.cwd = path.dirname(file)

      return options
    } catch (err) {
      if (err.code === 'ENOENT') err.message = 'Stylperjade: .stylperjaderc not found'
      if (err instanceof SyntaxError) throw new SyntaxError('Stylperjade: .stylperjaderc is invalid JSON')

      throw err
    }

  }

  function parseSourcemapFile(data, file) {

    var sourcemap = sourcemapParser.resolveSourceMapSync(data, file, fs.readFileSync)

    if (sourcemap) return new SourcemapConsumer(sourcemap.map)

    return null

  }

  function processCssClasses(obj, position, sourcemap) {

    switch (obj.type) {
      case 'ruleSet':
        processCssClasses(obj.rule, position, sourcemap)

        break
      case 'rule':
        if (obj.classNames) {
          pushCssClasses(obj.classNames, position, sourcemap)
        }

        if (obj.rule) {
          processCssClasses(obj.rule, position, sourcemap)
        }

        break
    }

  }

  function processCssFile(file, cb) {

    fs.readFile(file, 'utf8', function (err, data) {
      var sourcemap = parseSourcemapFile(data, file)

      try {
        cssParser.parse(data, { silent: false, source: file }).stylesheet.rules.forEach(function (rule) {
          processCssRule(rule, sourcemap)
        })
      } catch (err) {
        err.message = 'CSS file \'' + file + '\' error - ' + err.message

        return cb(err)
      }

      cb()
    })

  }

  function processCssRule(rule, sourcemap) {

    if (rule.rules) {
      rule.rules.forEach(function (rule) {
        processCssRule(rule, sourcemap)
      })
    }

    if (rule.selectors) {
      rule.selectors.forEach(function (selector) {
        processCssSelector(selector, rule.position, sourcemap)
      })
    }

  }

  function processCssSelector(selector, position, sourcemap) {

    var parseSelector = new CssSelectorParser()
        .registerNestingOperators('>', '+', '~')
        .registerAttrEqualityMods('^', '$', '*', '~')

    return processCssClasses(parseSelector.parse(selector), position, sourcemap)

  }

  function processJadeFile(file, cb) {

    fs.readFile(file, 'utf8', function (err, data) {
      try {
        jadeWalk(jadeParser(jadeLexer(data, file), file), function after(node) {
          if (node.type === 'Tag') {
            node.attrs.forEach(function (attr) {
              if (attr.name === 'class') {
                var sourceFile = node.filename ? path.resolve(options.cwd, node.filename) : ''

                if (!attr.escaped) {
                  // Remove single-quotes
                  pushUnique(jadeClasses, attr.val, sourceFile, node.line)
                } else {
                  // Find any single-quoted valid class names in Jade class attribute logic
                  // e.g. currentPage === '/hall-of-fame' ? 'nav-list-item--active' : ''
                  // finds; 'nav-list-item--active'
                  var regex = new RegExp(/\'\s*([_a-zA-Z]+[_a-zA-Z0-9-\s]*)\'(?!\s*\?)/g)
                    , match

                  while ((match = regex.exec(attr.val)) !== null) {
                    pushUnique(jadeClasses, match[1], sourceFile, node.line)
                  }
                }
              }
            })
          }
        })
      } catch (err) {
        err.message = 'Jade file \'' + file + '\' error - ' + err.message

        return cb(err)
      }

      cb()
    })

  }

  function pushCssClasses(classNames, position, sourcemap) {

    var file = path.resolve(options.cwd, position.source)
      , line = position.start.line
      , column = position.start.column

    if (sourcemap) {
      var sourcemapPosition = sourcemap.originalPositionFor({ 'line': line, 'column': column })

      file = path.resolve(options.cwd, sourcemapPosition.source)
      line = sourcemapPosition.line
      column = sourcemapPosition.column
    }

    classNames.forEach(function (className) {
      pushUnique(cssClasses, className, file, line, column)
    })

  }

  function pushUnique(collection, value, file, line, column) {

    var classNames = value.replace(/\'/g, '').split(' ')
      , location = { 'file': file, 'line': line, 'column': column > 1 ? column : null }

    classNames.forEach(function (className) {
      var existingClassName = _.find(collection, 'name', className)

      if (existingClassName) {
        if (!_.find(existingClassName.locations, location)) {
          existingClassName.locations.push(location)
        }
      } else {
        collection.push({ 'name': className, 'locations': [ location ] })
      }
    })

  }

}

module.exports = process
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2012 Eirikur Nilsson,
 * code changed by Nick Caruso in 2017 to support Swagger 2.0
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the Software), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

'use strict';

var _ = require('underscore');
var spec = require('swagger-tools').specs.v2;
var util = require('util');

var SWAGGER_METHODS = ['get', 'patch', 'post', 'put', 'delete'];
var SWAGGER_VERSION = '2.0';

/**
 * Creates a resource opbject
 * @param {string} path           The path
 * @param {object} options        The options
 * @param {object} options.models The models
 * @param {string} options.description The description
 * @return {undefined}
 */
function Resource(path, options) {
  options = options || {};

  this.path = path;
  this.models = options.models || {};
  this.apis = {};
  this.description = options.description;
}

Resource.prototype.getApi = function(path) {
  if (!(path in this.apis)) {
    this.apis[path] = {
      path: path,
      description: '',
      operations: []
    };
  }
  return this.apis[path];
};

var operationType = function(method) {
  method = method.toUpperCase();

  return function(path, summary, operation) {
    if (!operation) {
      operation = summary;
      summary = '';
    } else {
      operation.summary = summary;
    }
    operation.httpMethod = method;
    operation.method = method;

    var api = this.getApi(path);
    api.operations.push(operation);
  };
};

for (var i = 0; i < SWAGGER_METHODS.length; i += 1) {
  var m = SWAGGER_METHODS[i];
  Resource.prototype[m] = operationType(m);
}

var swagger = module.exports = {};

swagger.Resource = Resource;

swagger.resources = [];

/**
 * Configures swagger-doc for a express or restify server.
 * @param  {Server} server  A server object from express or restify.
 * @param  {Object} options Options object
 * @param  {string} options.discoveryUrl  The discovery url
 * @param  {string} options.version       The version
 * @param  {string} options.basePath      The base path
 * @returns {undefined} All changes are mapped to the server attribute
 */
swagger.configure = function(server, options) {
  options = options || {};

  var discoveryUrl = options.discoveryUrl || '/resources.json';
  var self = this;

  this.server = server;
  this.apiVersion = options.version || this.server.versions || '1.0.0';
  this.basePath = options.basePath;
  this.info = options.info;
  this.responseMessages = options.responseMessages;
  var $this = this;

  this.server.get(discoveryUrl, function(req, res, next) {
    var result = self._createResponse(req);
    result.apis = self.resources.map(function(r) {
      return {
        path: r.path,
        description: r.description || '',
      };
    });
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PATCH, POST, DELETE, PUT');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    var swaggerFilter = req.params.swaggerFilter;

    convertSwagger2(options, result, $this.resources, swaggerFilter, function(errConvert, swagger2output) {
      res.send(swagger2output);
      next();
    });
  });
};

function convertSwagger2(options, result, resources, swaggerFilter, cbConvertSwagger) { // eslint-disable-line
  var paths = {};
  var definitions = options.definitions;

  // console.log('resources = ', JSON.stringify(resources, null, 4));

  resources.forEach(function(resource) {
    _.each(resource.models, function(model, key) {
      var defintion = {
        type: 'object',
        properties: model.properties,
      };

      definitions[key] = defintion;
    });

    _.each(resource.apis, function(api) {
      paths[api.path] = {};

      api.operations.forEach(function(operation) {
        var operationName = operation.method.toLowerCase();

        var parameters = [];
        if (operation.originalParameters) {
          parameters = operation.originalParameters;
        } else {
          operation.parameters.forEach(function(parameter) {
            parameters.push({
              // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject
              name: parameter.name,
              in: parameter.paramType,
              description: parameter.description,
              required: parameter.required,
              schema: parameter.schema,
              type: parameter.type, // if !body
              format: parameter.format, // TODO not mapped
              allowEmptyValue: parameter.allowEmptyValue, // TODO
              items: parameter.items, // TODO required if type is Array
              default: parameter.default, // TODO
            });
          });
        }

        var convertedOperation = {
          tags: operation.tags,
          summary: operation.summary,
          description: operation.description,
          operationId: operation.operationId,
          consumes: operation.consumes,
          produces: operation.produces,
          parameters: parameters,
          responses: operation.responses,
          security: operation.security,
        };

        paths[api.path][operationName] = convertedOperation;

        console.log('swaggerFilter = ', swaggerFilter);
        console.log('operation.swaggerFilter = ', operation.swaggerFilter);

        if (swaggerFilter) {
          if (swaggerFilter !== operation.swaggerFilter) {
            delete paths[api.path][operationName];
          }
        } else {
          if (operation.swaggerFilter) {
            delete paths[api.path][operationName];
          }
        }
      });
    });
  });

  var swagger2 = {
    swagger: '2.0',
    info: {
      description: options.description,
      version: options.version,
      title: options.title,
    },
    host: options.host,
    basePath: options.basePath,
    schemes: options.schemes,
    paths: paths,
    definitions: definitions,
  };

  if (options.securityDefinitions) {
    swagger2.securityDefinitions = options.securityDefinitions;
  }

  var swaggerSpec = JSON.stringify(swagger2, null, 4);
  swaggerSpec = JSON.parse(swaggerSpec);

  spec.validate(swaggerSpec, function(errSwaggerTools, specResolve) {
    if (errSwaggerTools) {
      console.log('errSwaggerTools = ', errSwaggerTools); // eslint-disable-line
      return cbConvertSwagger(errSwaggerTools);
    }

    if (specResolve && specResolve.errors && specResolve.errors.length > 0) {
      console.log('Error, invalid swagger: \n' + util.inspect(specResolve.errors, false, 10, true));  // eslint-disable-line
    }

    if (specResolve && specResolve.warnings && specResolve.warnings.length > 0) {
      console.log('Warning, invalid swagger: \n' + util.inspect(specResolve.warnings, false, 10, true));  // eslint-disable-line
    }

    // swagger2 = require('./sample.json');

    return cbConvertSwagger(null, swagger2);
  });
}

/**
 * Registers a Resource with the specified path and options.
 * @param  {!String} path     The path of the resource.
 * @param  {{models}} options Optional options that can contain models.
 * @return {Resource}         The new resource.
 */
swagger.createResource = function(path, options) {
  var resource = new Resource(path, options);
  var self = this;
  this.resources.push(resource);

  this.server.get(path, function(req, res) {
    var result = self._createResponse(req);
    result.resourcePath = path;
    result.apis = Object.keys(resource.apis).map(function(k) {
      return resource.apis[k];
    });
    result.models = resource.models;
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PATCH, POST, DELETE, PUT');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    res.send(result);
  });

  return resource;
};

swagger._createResponse = function(req) {
  var basePath = this.basePath || 'http://' + req.headers.host;
  return {
    swaggerVersion: SWAGGER_VERSION,
    apiVersion: this.apiVersion,
    basePath: basePath,
    info: this.info
  };
};
<commit_msg>removed console logs<commit_after>/**
 * Copyright (c) 2012 Eirikur Nilsson,
 * code changed by Nick Caruso in 2017 to support Swagger 2.0
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the Software), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

'use strict';

var _ = require('underscore');
var spec = require('swagger-tools').specs.v2;
var util = require('util');

var SWAGGER_METHODS = ['get', 'patch', 'post', 'put', 'delete'];
var SWAGGER_VERSION = '2.0';

/**
 * Creates a resource opbject
 * @param {string} path           The path
 * @param {object} options        The options
 * @param {object} options.models The models
 * @param {string} options.description The description
 * @return {undefined}
 */
function Resource(path, options) {
  options = options || {};

  this.path = path;
  this.models = options.models || {};
  this.apis = {};
  this.description = options.description;
}

Resource.prototype.getApi = function(path) {
  if (!(path in this.apis)) {
    this.apis[path] = {
      path: path,
      description: '',
      operations: []
    };
  }
  return this.apis[path];
};

var operationType = function(method) {
  method = method.toUpperCase();

  return function(path, summary, operation) {
    if (!operation) {
      operation = summary;
      summary = '';
    } else {
      operation.summary = summary;
    }
    operation.httpMethod = method;
    operation.method = method;

    var api = this.getApi(path);
    api.operations.push(operation);
  };
};

for (var i = 0; i < SWAGGER_METHODS.length; i += 1) {
  var m = SWAGGER_METHODS[i];
  Resource.prototype[m] = operationType(m);
}

var swagger = module.exports = {};

swagger.Resource = Resource;

swagger.resources = [];

/**
 * Configures swagger-doc for a express or restify server.
 * @param  {Server} server  A server object from express or restify.
 * @param  {Object} options Options object
 * @param  {string} options.discoveryUrl  The discovery url
 * @param  {string} options.version       The version
 * @param  {string} options.basePath      The base path
 * @returns {undefined} All changes are mapped to the server attribute
 */
swagger.configure = function(server, options) {
  options = options || {};

  var discoveryUrl = options.discoveryUrl || '/resources.json';
  var self = this;

  this.server = server;
  this.apiVersion = options.version || this.server.versions || '1.0.0';
  this.basePath = options.basePath;
  this.info = options.info;
  this.responseMessages = options.responseMessages;
  var $this = this;

  this.server.get(discoveryUrl, function(req, res, next) {
    var result = self._createResponse(req);
    result.apis = self.resources.map(function(r) {
      return {
        path: r.path,
        description: r.description || '',
      };
    });
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PATCH, POST, DELETE, PUT');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    var swaggerFilter = req.params.swaggerFilter;

    convertSwagger2(options, result, $this.resources, swaggerFilter, function(errConvert, swagger2output) {
      res.send(swagger2output);
      next();
    });
  });
};

function convertSwagger2(options, result, resources, swaggerFilter, cbConvertSwagger) { // eslint-disable-line
  var paths = {};
  var definitions = options.definitions;

  // console.log('resources = ', JSON.stringify(resources, null, 4));

  resources.forEach(function(resource) {
    _.each(resource.models, function(model, key) {
      var defintion = {
        type: 'object',
        properties: model.properties,
      };

      definitions[key] = defintion;
    });

    _.each(resource.apis, function(api) {
      paths[api.path] = {};

      api.operations.forEach(function(operation) {
        var operationName = operation.method.toLowerCase();

        var parameters = [];
        if (operation.originalParameters) {
          parameters = operation.originalParameters;
        } else {
          operation.parameters.forEach(function(parameter) {
            parameters.push({
              // https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject
              name: parameter.name,
              in: parameter.paramType,
              description: parameter.description,
              required: parameter.required,
              schema: parameter.schema,
              type: parameter.type, // if !body
              format: parameter.format, // TODO not mapped
              allowEmptyValue: parameter.allowEmptyValue, // TODO
              items: parameter.items, // TODO required if type is Array
              default: parameter.default, // TODO
            });
          });
        }

        var convertedOperation = {
          tags: operation.tags,
          summary: operation.summary,
          description: operation.description,
          operationId: operation.operationId,
          consumes: operation.consumes,
          produces: operation.produces,
          parameters: parameters,
          responses: operation.responses,
          security: operation.security,
        };

        paths[api.path][operationName] = convertedOperation;

        if (swaggerFilter) {
          if (swaggerFilter !== operation.swaggerFilter) {
            delete paths[api.path][operationName];
          }
        } else {
          if (operation.swaggerFilter) {
            delete paths[api.path][operationName];
          }
        }
      });
    });
  });

  var swagger2 = {
    swagger: '2.0',
    info: {
      description: options.description,
      version: options.version,
      title: options.title,
    },
    host: options.host,
    basePath: options.basePath,
    schemes: options.schemes,
    paths: paths,
    definitions: definitions,
  };

  if (options.securityDefinitions) {
    swagger2.securityDefinitions = options.securityDefinitions;
  }

  var swaggerSpec = JSON.stringify(swagger2, null, 4);
  swaggerSpec = JSON.parse(swaggerSpec);

  spec.validate(swaggerSpec, function(errSwaggerTools, specResolve) {
    if (errSwaggerTools) {
      console.log('errSwaggerTools = ', errSwaggerTools); // eslint-disable-line
      return cbConvertSwagger(errSwaggerTools);
    }

    if (specResolve && specResolve.errors && specResolve.errors.length > 0) {
      console.log('Error, invalid swagger: \n' + util.inspect(specResolve.errors, false, 10, true));  // eslint-disable-line
    }

    if (specResolve && specResolve.warnings && specResolve.warnings.length > 0) {
      console.log('Warning, invalid swagger: \n' + util.inspect(specResolve.warnings, false, 10, true));  // eslint-disable-line
    }

    // swagger2 = require('./sample.json');

    return cbConvertSwagger(null, swagger2);
  });
}

/**
 * Registers a Resource with the specified path and options.
 * @param  {!String} path     The path of the resource.
 * @param  {{models}} options Optional options that can contain models.
 * @return {Resource}         The new resource.
 */
swagger.createResource = function(path, options) {
  var resource = new Resource(path, options);
  var self = this;
  this.resources.push(resource);

  this.server.get(path, function(req, res) {
    var result = self._createResponse(req);
    result.resourcePath = path;
    result.apis = Object.keys(resource.apis).map(function(k) {
      return resource.apis[k];
    });
    result.models = resource.models;
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, PATCH, POST, DELETE, PUT');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    res.send(result);
  });

  return resource;
};

swagger._createResponse = function(req) {
  var basePath = this.basePath || 'http://' + req.headers.host;
  return {
    swaggerVersion: SWAGGER_VERSION,
    apiVersion: this.apiVersion,
    basePath: basePath,
    info: this.info
  };
};
<|endoftext|>


<commit_before>var addFollowers = require('./follow');
var checkAuth = require('./check_auth');
var checkPrivilege = require('./privilege_check');
var getUser = require('./user');
var githubOAuth = require('./github_oauth');
var users = require('./db').get('users');

module.exports = function(app) {

  app.get('/me', checkAuth, function(req, res) {
    getUser(req.session.user.login, function(err, data) {
      res.json(data);
    });
  });

  app.get('/user', checkAuth, function(req, res) {
    return res.json(req.session.user);
  });

  app.post('/follow/:login', checkAuth, function(req, res) {
    getUser(req.session.user.login, function(err, data) {
      addFollowers(req.session.user.login, data.amount, function(err, result) {
        res.json(result);
      });
    });
  });

  githubOAuth.addRoutes(app, function(err, token, res, ignore, req) {
    if (token.error) {
      return res.send('There was an error logging in: ' + token.error_description);
    }
    req.session.token = token.access_token;
    res.redirect('/');
  });

};
<commit_msg>Use req.params.login instead<commit_after>var addFollowers = require('./follow');
var checkAuth = require('./check_auth');
var checkPrivilege = require('./privilege_check');
var getUser = require('./user');
var githubOAuth = require('./github_oauth');
var users = require('./db').get('users');

module.exports = function(app) {

  app.get('/me', checkAuth, function(req, res) {
    getUser(req.session.user.login, function(err, data) {
      res.json(data);
    });
  });

  app.get('/user', checkAuth, function(req, res) {
    return res.json(req.session.user);
  });

  app.post('/follow/:login', checkAuth, function(req, res) {
    getUser(req.params.login, function(err, data) {
      addFollowers(req.params.login, data.amount, function(err, result) {
        res.json(result);
      });
    });
  });

  githubOAuth.addRoutes(app, function(err, token, res, ignore, req) {
    if (token.error) {
      return res.send('There was an error logging in: ' + token.error_description);
    }
    req.session.token = token.access_token;
    res.redirect('/');
  });

};
<|endoftext|>


<commit_before>'use babel';
import $ from 'jquery';
import ToggleTabsView from './tabs-toggle-view';
import { CompositeDisposable } from 'atom';

export default {
  visible: true,
  toggleTabsView: null,
  modalPanel: null,
  subscriptions: null,

  activate(state) {
    this.toggleTabsView = new ToggleTabsView(state.toggleTabsViewState);
    this.modalPanel = atom.workspace.addModalPanel({
      item: this.toggleTabsView.getElement(),
      visible: false
    });

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'tabs-toggle:toggle': () => this.toggle()
    }));
  },

  deactivate() {
    this.modalPanel.destroy();
    this.subscriptions.dispose();
    this.toggleTabsView.destroy();
  },

  serialize() {
    return {
      toggleTabsViewState: this.toggleTabsView.serialize()
    };
  },

  toggle() {
    if(this.visible == true){
      $('.tab-bar').css({"display":"none"});
    }
    else {
      $('.tab-bar').css({"display":"block"});
    }
    this.visible = !this.visible
  }

};
<commit_msg>cambio de visibility<commit_after>'use babel';
import $ from 'jquery';
import ToggleTabsView from './tabs-toggle-view';
import { CompositeDisposable } from 'atom';

export default {
  visible: true,
  toggleTabsView: null,
  modalPanel: null,
  subscriptions: null,

  activate(state) {
    this.toggleTabsView = new ToggleTabsView(state.toggleTabsViewState);
    this.modalPanel = atom.workspace.addModalPanel({
      item: this.toggleTabsView.getElement(),
      visible: false
    });

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'tabs-toggle:toggle': () => this.toggle()
    }));
  },

  deactivate() {
    this.modalPanel.destroy();
    this.subscriptions.dispose();
    this.toggleTabsView.destroy();
  },

  serialize() {
    return {
      toggleTabsViewState: this.toggleTabsView.serialize()
    };
  },

  toggle() {
    if(this.visible == true){
      $('.tab-bar').css({"visibility":"hidden"});
    }
    else {
      $('.tab-bar').css({"visibility":"visible"});
    }
    this.visible = !this.visible
  }

};
<|endoftext|>


<commit_before>var mdw = require('./mdw.js');

const routes = [
  {
    method: 'get',
    path: '/status',
    version: undefined,
    before: [mdw.m2xClientMdw],
    handler: require('./routes/get_status.js').handler
  }
];

function pushToChain(chain, routes) {
  if (Array.isArray(routes))
    chain.push.apply(chain, routes);
  else if (routes instanceof Function)
    chain.push(routes);
}

function loadRoutes(server, log) {
  var chain;
  routes.forEach(function (route) {
    chain = [];
    if (route.before) pushToChain(chain, route.before);
    pushToChain(chain, route.handler);

    server[route.method]({path: route.path, version: route.version}, chain);
    var msg = 'successfully loaded route: ' + route.method.toUpperCase() + ' ' + route.path;
    if (route.version) msg += ' [' + route.version + ']';
    log.info(msg);
  });
}
module.exports.loadRoutes = loadRoutes;<commit_msg>changes<commit_after>var mdw = require('./mdw.js');

const routes = [
  {
    method: 'get',
    path: '/api/status',
    version: undefined,
    before: [mdw.m2xClientMdw],
    handler: require('./routes/get_status.js').handler
  },
  {
    method: 'get',
    path: /^[^(api)].*$/,
    version: undefined,
    handler: require('./routes/get_assets.js').handler
  }
];

function pushToChain(chain, routes) {
  if (Array.isArray(routes))
    chain.push.apply(chain, routes);
  else if (routes instanceof Function)
    chain.push(routes);
}

function loadRoutes(server, log) {
  var chain;
  routes.forEach(function (route) {
    chain = [];
    if (route.before) pushToChain(chain, route.before);
    pushToChain(chain, route.handler);

    server[route.method]({path: route.path, version: route.version}, chain);
    var msg = 'successfully loaded route: ' + route.method.toUpperCase() + ' ' + route.path;
    if (route.version) msg += ' [' + route.version + ']';
    log.info(msg);
  });
}
module.exports.loadRoutes = loadRoutes;<|endoftext|>


<commit_before>const router = require('koa-router')()
const models = require('./models')

router.get('/', async (ctx) => {
  await models.dbHealthCheck()
  ctx.body = 'ok'
})

/* Team */

router
  .get('/team', async (ctx) => {
    ctx.body = await models.db.team.findAll({order: 'name ASC'})
  })
  .get('/team/:id', async (ctx) => {
    ctx.body = await models.db.team.findById(ctx.params.id)
  })
  .post('/team', async (ctx) => {
    let name = ctx.request.body.name
    await models.db.team
      .findOrCreate({where: {name: name}})
      .spread(function (team, created) {
        if (!created) {
          ctx.status = 409
          ctx.body = `Team named "${name}" already exists!`
        } else {
          ctx.body = team
        }
      })
  })
  .put('/team/:id', async (ctx) => {
    await models.db.team.update({
      name: ctx.request.body.newname
    }, {
      where: {id: ctx.params.id}
    })
      .then(result => {
        ctx.body = result
      })
      .catch(err => {
        ctx.status = 409
        ctx.body = err
      })
  })
  .del('/team/:id', async (ctx) => {
    ctx.body = await models.db.team.destroy({where: {id: ctx.params.id}})
  })

module.exports = router
<commit_msg>Rename team REST endpoints, update status codes<commit_after>const router = require('koa-router')()
const models = require('./models')

router.get('/', async (ctx) => {
  await models.dbHealthCheck()
  ctx.body = 'ok'
})

/* Teams */

router
  .get('/teams', async (ctx) => {
    ctx.body = await models.db.team.findAll({order: 'name ASC'})
  })
  .get('/teams/:id', async (ctx) => {
    ctx.body = await models.db.team.findById(ctx.params.id)
  })
  .post('/teams', async (ctx) => {
    let name = ctx.request.body.name
    await models.db.team
      .findOrCreate({where: {name}})
      .spread(function (team, created) {
        if (created) {
          ctx.status = 201
          ctx.body = team
        } else {
          ctx.status = 409
          ctx.body = `Team named "${name}" already exists!`
        }
      })
  })
  .patch('/teams/:id', async (ctx) => {
    await models.db.team.update({
      name: ctx.request.body.name
    }, {
      where: {id: ctx.params.id}
    })
      .then(result => {
        let status = result.every((r) => r)
        ctx.status = status ? 200 : 400
        ctx.body = result
      })
      .catch(err => {
        ctx.status = 400
        ctx.body = err.message
      })
  })
  .del('/teams/:id', async (ctx) => {
    // TODO: #15: Add team size constraint
    await models.db.team.destroy({
      where: {id: ctx.params.id}
    })
      .then(result => {
        ctx.status = result ? 204 : 400
        ctx.body = result
      })
  })

module.exports = router
<|endoftext|>


<commit_before>'use strict';

var runCommand = require('../utils/run-command');
var path       = require('path');
var linkEnv    = require('../tasks/link-environment');
var Project    = require('../models/project');

module.exports = function(env, platform, config) {

  var command = 'cordova build ' + platform;

  var msg = 'Building cordova project for platform ' + platform
              + ' with environment ' + env;

  if(env === 'production') {
    linkEnv(Project.closest(), 'ember/dist');
  } else {
    linkEnv(Project.closest(), 'ember/tmp/output');
  }

  return runCommand(command, msg);
};
<commit_msg>remove unused config passed to build command<commit_after>'use strict';

var runCommand = require('../utils/run-command');
var path       = require('path');
var linkEnv    = require('../tasks/link-environment');
var Project    = require('../models/project');

module.exports = function(env, platform) {

  var command = 'cordova build ' + platform;

  var msg = 'Building cordova project for platform ' + platform
              + ' with environment ' + env;

  if(env === 'production') {
    linkEnv(Project.closest(), 'ember/dist');
  } else {
    linkEnv(Project.closest(), 'ember/tmp/output');
  }

  return runCommand(command, msg);
};
<|endoftext|>


<commit_before>
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// This file must be webpacked because it contains .css imports

// Load jquery and jquery-ui
var $ = require('jquery');
require('jquery-ui');
window.$ = window.jQuery = $;
require('jquery-ui');

// Load styling
require('jquery-ui/themes/smoothness/jquery-ui.min.css');
require('../css/widgets.min.css');

const widgets = require('./index');
console.info('jupyter-js-widgets loaded successfully');

const manager = new widgets.EmbedManager();

// Magic global widget rendering function:
function renderInlineWidgets(event) {
  var element = event.target || document;
  var tags = element.querySelectorAll('script[type="application/vnd.jupyter-embedded-widgets"]');
  for (var i=0; i!=tags.length; ++i) {
    var tag = tags[i];
    var widgetStateObject = JSON.parse(tag.innerHTML);
    var widgetContainer = document.createElement('div');
    widgetContainer.className = 'widget-area';
    manager.display_widget_state(widgetStateObject, widgetContainer).then(function() {
        if (tag.previousElementSibling &&
            tag.previousElementSibling.matches('img.jupyter-widget')) {
            tag.parentElement.removeChild(tag.previousElementSibling);
        }
        tag.parentElement.insertBefore(widgetContainer, tag);
    });
  }
}

// Module exports
exports.manager = manager;
exports.renderInlineWidgets = renderInlineWidgets;

// Set window globals
window.manager = manager;

window.addEventListener('load', renderInlineWidgets);
<commit_msg>Define jupyter-js-widgets module<commit_after>
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

// This file must be webpacked because it contains .css imports

// Load jquery and jquery-ui
var $ = require('jquery');
require('jquery-ui');
window.$ = window.jQuery = $;
require('jquery-ui');

// Load styling
require('jquery-ui/themes/smoothness/jquery-ui.min.css');
require('../css/widgets.min.css');

// Define jupyter-js-widget requirejs module
// This is needed for custom widget model to be able to require
// jupyter-js-widgets.
var widgets = require('./index');

if (window.define) {
    window.define('jupyter-js-widgets', function() {
        return widgets;
    });
}

// Magic global widget rendering function:
var manager = new widgets.EmbedManager();

function renderInlineWidgets(event) {
  var element = event.target || document;
  var tags = element.querySelectorAll('script[type="application/vnd.jupyter-embedded-widgets"]');
  for (var i=0; i!=tags.length; ++i) {
    var tag = tags[i];
    var widgetStateObject = JSON.parse(tag.innerHTML);
    var widgetContainer = document.createElement('div');
    widgetContainer.className = 'widget-area';
    manager.display_widget_state(widgetStateObject, widgetContainer).then(function() {
        if (tag.previousElementSibling &&
            tag.previousElementSibling.matches('img.jupyter-widget')) {
            tag.parentElement.removeChild(tag.previousElementSibling);
        }
        tag.parentElement.insertBefore(widgetContainer, tag);
    });
  }
}

window.addEventListener('load', renderInlineWidgets);

// Module exports
module.exports = {
    manager: manager,
    renderInlineWidgets: renderInlineWidgets
}
<|endoftext|>


<commit_before>'use strict';
var hapi        = require('hapi'),
	joi 		= require('joi'),
	handlers    = require('../lib/handlers.js'),
	routes;

// adds the routes and validation for api
routes = [{
		method: 'GET',
		path: '/',
		config: {
			handler: handlers.index
		}
	},  {
		method: 'GET',
		path: '/images/{file*}',
		handler:{
		    directory:{
				path:'./node_modules/hapi-swagger/public/swaggerui/images'
		    }
		}
	}, {
		method: 'POST',
		path: '/parse/url/',
		config: {
			handler: handlers.parseUrl,
			description: 'Parse microformats at a URL',
			notes: [
				'Parses microformats found in the HTML of URL',
				'Error status codes',
				'400, bad request',
				'404, not found',
				'500, internal server error'
			],
			tags: ['api'],
			validate: {
				query: {
					url: joi.string()
						.required()
						.description('the url to parse')
				}
			}
		}
	}, {
		method: 'POST',
		path: '/parse/html/',
		config: {
			handler: handlers.parseHtml,
			description: 'Parse microformats in HTML string',
			notes: [
				'Parses microformats found in the HTML string',
				'Error status codes',
				'400, bad request',
				'500, internal server error'
			],
			tags: ['api'],
			validate: {
				query: {
					html: joi.string()
						.required()
						.description('the HTML string to parse'),

					baseurl: joi.string()
						.description('a URL to resolve links in HTML')

				}
			},
			jsonp: 'callback'
		}
	},  {
		method: 'GET',
		path: '/{path*}',
		handler: {
			directory: {
				path: __dirname.replace('/lib','') + '/public',
				listing: false,
				index: true
			}
		}
	}];

exports.routes = routes;<commit_msg>Changes routes to GET<commit_after>'use strict';
var hapi        = require('hapi'),
	joi 		= require('joi'),
	handlers    = require('../lib/handlers.js'),
	routes;

// adds the routes and validation for api
routes = [{
		method: 'GET',
		path: '/',
		config: {
			handler: handlers.index
		}
	},  {
		method: 'GET',
		path: '/images/{file*}',
		handler:{
		    directory:{
				path:'./node_modules/hapi-swagger/public/swaggerui/images'
		    }
		}
	}, {
		method: 'GET',
		path: '/parse/url/',
		config: {
			handler: handlers.parseUrl,
			description: 'Parse microformats from a URL',
			notes: [
				'Parses microformats found in the HTML of URL',
				'Error status codes',
				'400, bad request',
				'404, not found',
				'500, internal server error'
			],
			tags: ['api'],
			validate: {
				query: {
					url: joi.string()
						.required()
						.description('the url to parse')
				}
			}
		}
	}, {
		method: 'GET',
		path: '/parse/html/',
		config: {
			handler: handlers.parseHtml,
			description: 'Parse microformats from HTML',
			notes: [
				'Parses microformats found in the HTML string',
				'Error status codes',
				'400, bad request',
				'500, internal server error'
			],
			tags: ['api'],
			validate: {
				query: {
					html: joi.string()
						.required()
						.description('the HTML string to parse'),

					baseurl: joi.string()
						.description('a URL to resolve links in HTML')

				}
			},
			jsonp: 'callback'
		}
	},  {
		method: 'GET',
		path: '/{path*}',
		handler: {
			directory: {
				path: __dirname.replace('/lib','') + '/public',
				listing: false,
				index: true
			}
		}
	}];

exports.routes = routes;<|endoftext|>


<commit_before>var when = require("when");
var http = require("http");

function req(opts, cb) {
    opts.host = opts.host || "localhost";

    return http.request(opts, function (res) {
        var body = "";
        res.setEncoding("utf8");
        res.on("data", function (chunk) { body += chunk; });
        res.on("end", function () { cb(res, body); });
    });
}

module.exports = {
    captureSlave: function (port, ua) {
        // Avoiding circular require
        var bCapServ = require("./buster-capture-server");

        ua = ua || "Whatever";
        var deferred = when.defer();
        var self = this;

        var c = bCapServ.createServerClient(port);
        c.connect().then(function () {

            c.on("slave:captured", function (slave) {
                c.disconnect();
                deferred.resolve(slave);
            });

            req({path: "/capture", port: port}, function (res, body) {
                var slave = JSON.parse(body);
                c.emit("slave:" + slave.id + ":imprisoned",
                       {userAgent: ua,
                        pubsubClientId: c._pubsubClient.connectionId});
            }).end();
        }, deferred.reject);

        return deferred.promise;
    }
};
<commit_msg>Using a separate pubsub client for the mock browser, to mimic browser behaviour.<commit_after>var when = require("when");
var http = require("http");

function req(opts, cb) {
    opts.host = opts.host || "localhost";

    return http.request(opts, function (res) {
        var body = "";
        res.setEncoding("utf8");
        res.on("data", function (chunk) { body += chunk; });
        res.on("end", function () { cb(res, body); });
    });
}

module.exports = {
    captureSlave: function (port, ua) {
        // Avoiding circular require
        var bCapServ = require("./buster-capture-server");
        var bCapServPubsubClient = require("./pubsub-client")

        ua = ua || "Whatever";
        var deferred = when.defer();
        var self = this;

        var mockBrowserPubsub = bCapServPubsubClient.create({
            host: "localhost",
            port: port
        });

        var c = bCapServ.createServerClient(port);
        c.connect().then(function () {

            c.on("slave:captured", function (slave) {
                c.disconnect();
                mockBrowserPubsub.disconnect();
                deferred.resolve(slave);
            });

            req({path: "/capture", port: port}, function (res, body) {
                var slave = JSON.parse(body);

                mockBrowserPubsub.connect().then(function () {
                    c.emit("slave:" + slave.id + ":imprisoned",
                           {userAgent: ua,
                            pubsubClientId: mockBrowserPubsub.id});
                });
            }).end();
        }, deferred.reject);

        return deferred.promise;
    }
};
<|endoftext|>


<commit_before>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define([
    'jquery',
    'bootstraptour',
    'handlebars',
    'text!kbase/templates/tour/tour_panel.html',
    'css!kbase/css/kbaseTour.css'
], function($, Tour, Handlebars, TourTmpl) {
    "use strict";

    var NarrativeTour = function (narrative, notebook, events) {
        var that = this;
        this.notebook = notebook;
        this.narrative = narrative;
        this.step_duration = 0;
        this.events = events;
        this.template = Handlebars.compile(TourTmpl);
        this.tour_steps = [
            {
                title: "Welcome to the Narrative Tour", // add a grayed out background.
                placement: 'bottom',
                orphan: true,
                content: '
This tour will demonstrate how to use KBase’s Narrative User Interface to begin creating Narratives for data analysis. Think of a Narrative as an advanced lab notebook that contains all of your data, analyses, notes, visualizations, custom scripts, and results.
Use the left and right arrow buttons to go backwards and forwards through this tour.',
                backdrop: true
            },
            {
                element: "#notebook_name",
                title: "Narrative Name",
                placement: 'bottom',
                content: 'By default, each new Narrative is named “Untitled.” Click on this name and enter a new title.'
            },
            {
                element: '#kb-view-mode',
                title: 'Toggle View Only',
                placement: 'bottom',
                content: 'If you have edit permission for this Narrative, you can choose View Only (uneditable) or Edit mode'
            },
            {
                element: '#kb-update-btn',
                title: 'Narrative Update',
                placement: 'bottom',
                content: 'If there\'s an update to the Narrative Interface, this button will appear. Click it before you start working on your Narrative.',
                onShow: function(tour) {
                    $('#kb-update-btn').show();
                },
                onHide: function(tour) {
                    $('#kb-update-btn').hide();
                }
            },
            {
                element: $('#kb-share-btn .kb-nav-btn-txt'),
                title: 'Share with others',
                placement: 'bottom',
                content: 'Click the Share button to open a dialog box allowing you to search for and select KBase users you want to share with. You can give them view-only access, editing privileges, or the ability to edit and share the Narrative with other users. By default, each Narrative you create is private. You can choose to make your Narrative public or share it with certain collaborators.'
            },
            {
                element: '#kb-save-btn',
                title: 'Save the Narrative',
                placement: 'bottom',
                content: 'You should periodically save your Narrative by clicking the save button. KBase does autosave your Narrative whenever an App is run, but other changes should be saved manually.'
            },
            {
                element: '#signin-button',
                title: 'Profile and Logout',
                placement: 'left',
                content: 'Click here for a menu that will let you view and edit your user profile or log out.'
            },
            {
                element: '.kb-side-header[kb-data-id="0"]',
                title: 'Analyze Tab',
                placement: 'bottom',
                content: 'The Analyze tab contains both the data you have added to this Narrative and the analysis tools in KBase--called Apps.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                }
            },
            {
                element: $('.kb-side-tab[kb-data-id="0"]').find('button > .fa-arrow-right').first(), //$('.kb-side-tab[kb-data-id="0"] > button > .fa-arrow-right'),
                title: 'Data Slideout',
                placement: 'left',
                content: 'This button opens the data slideout, which allows you to add data to your Narrative from a variety of sources.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(1)', // my data tab
                title: 'My Data',
                placement: 'bottom',
                content: 'The My Data tab shows data from your other Narratives. If this is your first Narrative, it will be empty.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(1)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(2)', // shared with me tab
                title: 'Shared With Me',
                placement: 'bottom',
                content: 'The Shared With Me tab contains data from any Narratives that have been shared with you by collaborators.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(2)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(3)', // public tab
                title: 'Public Data',
                placement: 'bottom',
                content: 'The Public tab provides direct access to public data that KBase periodically imports from several sources. These sources are listed on the KBase website kbase.us/data-policy-and-sources',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(3)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(4)', // example tab
                title: 'Example Data',
                placement: 'bottom',
                content: 'The Example tab contains sample data that can be used to try out KBase analysis tools.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(4)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(5)', // import tab
                title: 'Import Data',
                placement: 'bottom',
                content: 'Finally, the Import tab allows you to upload your own data for analysis. This and other data you add are private unless you choose to share your Narrative. Click the drop-down menu to see a list of currently uploadable data typestab allows you to upload your own data for analysis. This and other data you add are private unless you choose to share your Narrative. Click the drop-down menu to see a list of currently uploadable data types.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(5)').click();
                }
            },
            {
                element: '.kb-narr-side-panel-set > .kb-side-separator:nth-child(2)', // The 'Apps' area
                title: 'Apps in KBase',
                placement: 'right',
                content: 'In KBase, you can run complex analyses on the data in your Narrative by using Apps. All of the Apps available in KBase are listed just below the Data Panel. Click an App name to add it to your Narrative, or click the “...” that appears to the right of the name to see more information about the App.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                    that.openDataSlideout(false);
                }
            },
            {
                element: '.kb-side-separator:nth-child(2) .kb-function-body .kb-data-list-obj-row:first', //'.kb-narr-side-panel-set > .kb-side-separator:nth-child(2)', // [...] or more... btn on first app
                title: 'App Information',
                placement: 'right',
                content: 'To see a brief description of an App, click the short description under its name. The more... link at the end of this description will open another tab with detailed information about the App.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                    $('.kb-side-separator:nth-child(2) .kb-function-body .kb-data-list-obj-row:first').click();
                    // get first ellipsis and click on it.
                }
            },
            {
                element: '.kb-side-header[kb-data-id="1"]',
                title: 'Narratives Tab',
                placement: 'bottom',
                content: '
This tab lists all your Narratives and those that have been shared with you. Click the name of any Narrative to open it in a new window. Hovering over a Narrative name will reveal options for viewing its history, reverting to an earlier version, and copying or deleting it. This tab also lets you create a new, empty Narrative or copy a Narrative, complete with all its data and contents.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="1"]').click();
                }
            },
            {
                element: '#kb-add-md-cell',
                title: 'Markdown Cells',
                placement: 'left',
                content: 'You can include formatted text in your Narratives in markdown cells, which can be inserted by clicking the paragraph icon at the bottom of the Narrative. These cells can contain raw text, HTML, or LaTeX.'
            },
            {
                title: "End of Basic Features Tour",
                orphan: true,
                content: "
You have now been introduced to the basic features of the Narrative Interface. We hope you conduct interesting and creative research using these tools and are able to expand your collaborator network with other KBase users. If you have any questions or feature requests, please follow the Contact Us link in the hamburger menu at the top left.
To learn about advanced features of the Narrative Interface, click the right arrow button to proceed. Click the exit button to return to your Narrative.",
                backdrop: true
            },
            {
                element: '#kb-ipy-menu',
                title: 'Kernel Controls',
                placement: 'bottom',
                content: 'Access control options for the Jupyter kernel that powers the Narrative Interface. This is useful for restarting or reconnecting to KBase after your network connection has been disrupted.',
            },
            {
                element: '#kb-settings-btn',
                title: 'Adjust Global Settings',
                placement: 'bottom',
                content: 'Enable advanced or developer features for your Narrative in the Settings Menu. These option are enabled for individual Narratives. Refresh your browser to enable these settings for your Narrative.'
            },
            {
                element: '#kb-add-code-cell',
                title: 'Code Cells',
                placement: 'left',
                content: 'Code Cells allow you to use Python and other coding languages to create custom scripts in your Narrative.'
            },
            {
                title: 'End of Advanced Features Tour',
                orphan: true,
                content: 'You have now been introduced to the advanced features of the Narrative Interface. The Narrative User Guide has more information. If you have any questions or feature requests, please follow the Contact Us link in the hamburger menu.',
                backdrop: true
            }

        ];

        this.tour = new Tour({
            storage: false, // start tour from beginning every time
            debug: true,
            reflex: true, // click on element to continue tour
            animation: false,
            duration: this.step_duration,
            onStart: function() { console.log('tour started'); },
            // TODO: remove the onPause/onResume logic once pi's patch has been
            // merged upstream to make this work via data-resume-class and
            // data-resume-text attributes.
            onPause: this.toggle_pause_play,
            onResume: this.toggle_pause_play,
            steps: this.tour_steps,
            template: function(i, step) {
                return that.template({step: (i+1), totalSteps: that.tour_steps.length});
            },
            orphan: true
        });

    };

    /**
     * Opens the data slideout if it's not open already.
     */
    NarrativeTour.prototype.openDataSlideout = function(setOpen) {
        if (setOpen) {
            this.narrative.showDataOverlay();
            // $(document).trigger('showSidePanelOverlay.Narrative');
            // $('.kb-side-tab[kb-data-id="0"]').find('button > .fa-arrow-right')
            //                                  .first()
            //                                  .click();
        }
        else {
            this.narrative.hideOverlay();
            // $(document).trigger('hideSidePanelOverlay.Narrative');
        }
    };

    NarrativeTour.prototype.start = function () {
        console.log("let's start the tour");
        this.tour.init();
        this.tour.start();
        if (this.tour.ended()) {
            this.tour.restart();
        }
    };

    NarrativeTour.prototype.command_icon_hack = function() {
        $('#modal_indicator').css('min-height', '18px');
    };

    NarrativeTour.prototype.toggle_pause_play = function () {
        $('#tour-pause').toggleClass('fa-pause fa-play');
    };

    NarrativeTour.prototype.edit_mode = function() {
        this.notebook.focus_cell();
        this.notebook.edit_mode();
    };

    return {'Tour': NarrativeTour};

});
<commit_msg>Update tour.js<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define([
    'jquery',
    'bootstraptour',
    'handlebars',
    'text!kbase/templates/tour/tour_panel.html',
    'css!kbase/css/kbaseTour.css'
], function($, Tour, Handlebars, TourTmpl) {
    "use strict";

    var NarrativeTour = function (narrative, notebook, events) {
        var that = this;
        this.notebook = notebook;
        this.narrative = narrative;
        this.step_duration = 0;
        this.events = events;
        this.template = Handlebars.compile(TourTmpl);
        this.tour_steps = [
            {
                title: "Welcome to the Narrative Tour", // add a grayed out background.
                placement: 'bottom',
                orphan: true,
                content: '
This tour will demonstrate how to use KBase’s Narrative User Interface to begin creating Narratives for data analysis. Think of a Narrative as an advanced lab notebook that contains all of your data, analyses, notes, visualizations, custom scripts, and results.
Use the left and right arrow buttons to go backwards and forwards through this tour.',
                backdrop: true
            },
            {
                element: "#notebook_name",
                title: "Narrative Name",
                placement: 'bottom',
                content: 'By default, each new Narrative is named “Untitled.” Click on this name and enter a new title.'
            },
            {
                element: '#kb-view-mode',
                title: 'Toggle View Only',
                placement: 'bottom',
                content: 'If you have edit permission for this Narrative, you can choose View Only (uneditable) or Edit mode'
            },
            {
                element: '#kb-update-btn',
                title: 'Narrative Update',
                placement: 'bottom',
                content: 'If there\'s an update to the Narrative Interface, this button will appear. Click it before you start working on your Narrative.',
                onShow: function(tour) {
                    $('#kb-update-btn').show();
                },
                onHide: function(tour) {
                    $('#kb-update-btn').hide();
                }
            },
            {
                element: $('#kb-share-btn .kb-nav-btn-txt'),
                title: 'Share with others',
                placement: 'bottom',
                content: 'Click the Share button to open a dialog box allowing you to search for and select KBase users you want to share with. You can give them view-only access, editing privileges, or the ability to edit and share the Narrative with other users. By default, each Narrative you create is private. You can choose to make your Narrative public or share it with certain collaborators.'
            },
            {
                element: '#kb-save-btn',
                title: 'Save the Narrative',
                placement: 'bottom',
                content: 'You should periodically save your Narrative by clicking the save button. KBase does autosave your Narrative whenever an App is run, but other changes should be saved manually.'
            },
            {
                element: '#signin-button',
                title: 'Profile and Logout',
                placement: 'left',
                content: 'Click here for a menu that will let you view and edit your user profile or log out.'
            },
            {
                element: '.kb-side-header[kb-data-id="0"]',
                title: 'Analyze Tab',
                placement: 'bottom',
                content: 'The Analyze tab contains both the data you have added to this Narrative and the analysis tools in KBase--called Apps.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                }
            },
            {
                element: $('.kb-side-tab[kb-data-id="0"]').find('button > .fa-arrow-right').first(), //$('.kb-side-tab[kb-data-id="0"] > button > .fa-arrow-right'),
                title: 'Data Slideout',
                placement: 'left',
                content: 'This arrow opens the data slideout, which allows you to add data to your Narrative from a variety of sources.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(1)', // my data tab
                title: 'My Data',
                placement: 'bottom',
                content: 'The My Data tab shows data from your other Narratives. If this is your first Narrative, it will be empty.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(1)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(2)', // shared with me tab
                title: 'Shared With Me',
                placement: 'bottom',
                content: 'The Shared With Me tab contains data from any Narratives that have been shared with you by collaborators.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(2)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(3)', // public tab
                title: 'Public Data',
                placement: 'bottom',
                content: 'The Public tab provides direct access to public data that KBase periodically imports from several sources. These sources are listed on the KBase website kbase.us/data-policy-and-sources',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(3)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(4)', // example tab
                title: 'Example Data',
                placement: 'bottom',
                content: 'The Example tab contains sample data that can be used to try out KBase analysis tools.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(4)').click();
                }
            },
            {
                element: '.kb-side-overlay-container .kb-side-header:nth-child(5)', // import tab
                title: 'Import Data',
                placement: 'bottom',
                content: 'Finally, the Import tab allows you to upload your own data for analysis. Any data you add are kept private unless you choose to share your Narrative. This and other data you add are private unless you choose to share your Narrative. Click the drop-down menu to see a list of currently uploadable data types.',
                onShow: function(tour) {
                    that.openDataSlideout(true);
                    $('.kb-side-overlay-container .kb-side-header:nth-child(5)').click();
                }
            },
            {
                element: '.kb-narr-side-panel-set > .kb-side-separator:nth-child(2)', // The 'Apps' area
                title: 'Apps in KBase',
                placement: 'right',
                content: 'In KBase, you can run complex analyses on the data in your Narrative by using Apps. All of the Apps available in KBase are listed just below the Data Panel. Click an App name to add it to your Narrative, or click the “...” that appears to the right of the name when hovering your mouse to see more information about the App.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                    that.openDataSlideout(false);
                }
            },
            {
                element: '.kb-side-separator:nth-child(2) .kb-function-body .kb-data-list-obj-row:first', //'.kb-narr-side-panel-set > .kb-side-separator:nth-child(2)', // [...] or more... btn on first app
                title: 'App Information',
                placement: 'right',
                content: 'After clicking the "..." to see a short description of an App, click the "more..." link at the end of the description to open another tab with detailed information about the App.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="0"]').click();
                    $('.kb-side-separator:nth-child(2) .kb-function-body .kb-data-list-obj-row:first').click();
                    // get first ellipsis and click on it.
                }
            },
            {
                element: '.kb-side-header[kb-data-id="1"]',
                title: 'Narratives Tab',
                placement: 'bottom',
                content: '
This tab lists all your Narratives and those that have been shared with you. Click the name of any Narrative to open it in a new window. Hovering over a Narrative name will reveal options for viewing its history, reverting to an earlier version, and copying or deleting it. This tab also lets you create a new, empty Narrative or copy a Narrative, complete with all its data and contents.',
                onShow: function(tour) {
                    $('.kb-side-header[kb-data-id="1"]').click();
                }
            },
            {
                element: '#kb-add-md-cell',
                title: 'Markdown Cells',
                placement: 'left',
                content: 'You can include formatted text in your Narratives in markdown cells, which can be inserted by clicking the paragraph icon at the bottom of the Narrative. Markdown cells can also contain raw text, HTML, or LaTeX.'
            },
            {
                title: "End of Basic Features Tour",
                orphan: true,
                content: "
You have now been introduced to the basic features of the Narrative Interface. We hope you conduct interesting and creative research using these tools and are able to expand your collaborator network with other KBase users. If you have any questions or feature requests, please follow the Contact Us link in the hamburger menu at the top left.
To learn about advanced features of the Narrative Interface, click the right arrow button to proceed. To exit the tour, click the X at the top right of this box.",
                backdrop: true
            },
            {
                element: '#kb-ipy-menu',
                title: 'Kernel Controls',
                placement: 'bottom',
                content: 'Access control options for the Jupyter kernel that powers the Narrative Interface. This is useful for restarting or reconnecting to KBase after your network connection has been disrupted.',
            },
            {
                element: '#kb-settings-btn',
                title: 'Adjust Global Settings',
                placement: 'bottom',
                content: 'Enable advanced or developer features for your Narrative in the Settings Menu. These option are enabled for individual Narratives. Refresh your browser to enable these settings for your Narrative.'
            },
            {
                element: '#kb-add-code-cell',
                title: 'Code Cells',
                placement: 'left',
                content: 'Code Cells allow you to use Python and other coding languages to create custom scripts in your Narrative.'
            },
            {
                title: 'End of Advanced Features Tour',
                orphan: true,
                content: 'You have now been introduced to the advanced features of the Narrative Interface. The Narrative User Guide has more information. If you have any questions or feature requests, please follow the Contact Us link in the hamburger menu.',
                backdrop: true
            }

        ];

        this.tour = new Tour({
            storage: false, // start tour from beginning every time
            debug: true,
            reflex: true, // click on element to continue tour
            animation: false,
            duration: this.step_duration,
            onStart: function() { console.log('tour started'); },
            // TODO: remove the onPause/onResume logic once pi's patch has been
            // merged upstream to make this work via data-resume-class and
            // data-resume-text attributes.
            onPause: this.toggle_pause_play,
            onResume: this.toggle_pause_play,
            steps: this.tour_steps,
            template: function(i, step) {
                return that.template({step: (i+1), totalSteps: that.tour_steps.length});
            },
            orphan: true
        });

    };

    /**
     * Opens the data slideout if it's not open already.
     */
    NarrativeTour.prototype.openDataSlideout = function(setOpen) {
        if (setOpen) {
            this.narrative.showDataOverlay();
            // $(document).trigger('showSidePanelOverlay.Narrative');
            // $('.kb-side-tab[kb-data-id="0"]').find('button > .fa-arrow-right')
            //                                  .first()
            //                                  .click();
        }
        else {
            this.narrative.hideOverlay();
            // $(document).trigger('hideSidePanelOverlay.Narrative');
        }
    };

    NarrativeTour.prototype.start = function () {
        console.log("let's start the tour");
        this.tour.init();
        this.tour.start();
        if (this.tour.ended()) {
            this.tour.restart();
        }
    };

    NarrativeTour.prototype.command_icon_hack = function() {
        $('#modal_indicator').css('min-height', '18px');
    };

    NarrativeTour.prototype.toggle_pause_play = function () {
        $('#tour-pause').toggleClass('fa-pause fa-play');
    };

    NarrativeTour.prototype.edit_mode = function() {
        this.notebook.focus_cell();
        this.notebook.edit_mode();
    };

    return {'Tour': NarrativeTour};

});
<|endoftext|>


<commit_before>"use strict";

var _ = require('underscore');
var helpers = require('./helpers');
var logger = require('./logger');

function afterRun(keyword, keywordsToRun, keywordsDefined, localVars, then, returnValue) {
    logger('Returned from ' + keyword.name + ', return value:', returnValue);

    if(keyword.returnVar && returnValue !== undefined) {
        localVars.set(keyword.returnVar, returnValue);
    }

    runKeyword(keywordsToRun, keywordsDefined, localVars, then);
}

function getKeyword(name, keywordsDefined) {
    var def = keywordsDefined[name];

    if(!def) {
        console.error("Can not find keyword '" + name + "'");
        throw new Error("Can not find keyword '" + name + "'");
    } else {
        return def;
    }
}

function runKeyword(keywordsToRun, keywordsDefined, localVars, then) {
    var keyword = keywordsToRun.shift();

    if(!keyword) {
        var retVal = localVars.get('return');
        localVars.remove('return');
        return (then || _.identity)(retVal);
    }

    var replacedArgs = helpers.replaceArgPlaceholders(keyword.args, localVars);
    var callback = afterRun.bind(null, keyword, keywordsToRun, keywordsDefined, localVars, then);
    var defArgs = [callback].concat(replacedArgs);
    defArgs.push(keyword);
    defArgs.push(localVars);

    logger("Calling keyword '" + keyword.name + "'. Original args " +
        JSON.stringify(keyword.args) + " replaced with " + JSON.stringify(replacedArgs));

    var def = getKeyword(keyword.name, keywordsDefined);
    def.apply(null, defArgs);
}

/**
    Take the following ARRAY as an argument:
    [n]: keywords to run
    [last]["next"]: next()
    [last]["args"]: args
    [last]["keywordInfo"]: keywordInfo
    [last]["vars"]: vars
*/
function run(keywordsToRun, keywordsDefined, passedArguments) {
    var vars;

    vars = helpers.createLocalEnv((passedArguments || {}).args || []);
    keywordsToRun = helpers.createKeywords(keywordsToRun);

    return {
        then: function(callback) {
            runKeyword(keywordsToRun, keywordsDefined, vars, callback);
        }
    };
}

module.exports = Object.freeze({
    run: run
});<commit_msg>Splited runKeyword -> runKeywords & runKeyword<commit_after>"use strict";

var _ = require('underscore');
var helpers = require('./helpers');
var logger = require('./logger');

function afterRun(keyword, keywordsToRun, keywordsDefined, localVars, then, returnValue) {
    logger('Returned from ' + keyword.name + ', return value:', returnValue);

    if(keyword.returnVar && returnValue !== undefined) {
        localVars.set(keyword.returnVar, returnValue);
    }

    runKeywords(keywordsToRun, keywordsDefined, localVars, then);
}

function getKeyword(name, keywordsDefined) {
    var def = keywordsDefined[name];

    if(!def) {
        console.error("Can not find keyword '" + name + "'");
        throw new Error("Can not find keyword '" + name + "'");
    } else {
        return def;
    }
}

function runKeyword(keyword, keywordsToRun, keywordsDefined, localVars, then) {
    var replacedArgs = helpers.replaceArgPlaceholders(keyword.args, localVars);
    var callback = afterRun.bind(null, keyword, keywordsToRun, keywordsDefined, localVars, then);
    var defArgs = [callback].concat(replacedArgs);
    defArgs.push(keyword);
    defArgs.push(localVars);

    logger("Calling keyword '" + keyword.name + "'. Original args " +
        JSON.stringify(keyword.args) + " replaced with " + JSON.stringify(replacedArgs));

    var def = getKeyword(keyword.name, keywordsDefined);
    def.apply(null, defArgs);
}

function runKeywords(keywordsToRun, keywordsDefined, localVars, then) {
    var keyword = keywordsToRun.shift();

    if(!keyword) {
        var retVal = localVars.get('return');
        localVars.remove('return');
        return (then || _.identity)(retVal);
    } else {
        runKeyword(keyword, keywordsToRun, keywordsDefined, localVars, then);
    }
}

/**
    Take the following ARRAY as an argument:
    [n]: keywords to run
    [last]["next"]: next()
    [last]["args"]: args
    [last]["keywordInfo"]: keywordInfo
    [last]["vars"]: vars
*/
function run(keywordsToRun, keywordsDefined, passedArguments) {
    var vars;

    vars = helpers.createLocalEnv((passedArguments || {}).args || []);
    keywordsToRun = helpers.createKeywords(keywordsToRun);

    return {
        then: function(callback) {
            runKeywords(keywordsToRun, keywordsDefined, vars, callback);
        }
    };
}

module.exports = Object.freeze({
    run: run
});<|endoftext|>