<commit_before>var fs = require('fs'),
	sys = require('sys'),
	path = require('path')

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	bind = util.bind,
	map = util.map,
	each = util.each,
	name = util.name,
	shallowCopy = util.shallowCopy

// Resolve imports by injecting declarations into the reference table
// Resolve aliases to values
// Return an AST with no import statements and no aliases

// TODO Read types from types
// TODO read tags from tags
var Tags = require('./Tags'),
	Types = require('./Types')

exports.resolve = util.intercept('ResolveError', function (ast, context) {
	if (!context) {
		context = { modules:{}, declarations:[], fileDependencies:[], aliases: {} }
	}
	var ast = resolve(context, ast)
	return {
		ast: ast,
		modules: context.modules,
		declarations: context.declarations,
		dependencies: context.dependencies
	}
})

var resolve = function(context, ast) {
	if (!ast) {
		return null
	} else if (ast instanceof Array) {
		return map(ast, bind(this, resolveStatement, context))
	} else {
		return resolveStatement(context, ast)
	}
}

var resolveStatement = function(context, ast) {
	switch(ast.type) {
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)      ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)        ;break
		case 'DECLARATION':          handleDeclaration(context, ast)       ;break

		case 'XML':                  return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)
		case 'INVOCATION':           return resolveInvocation(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'MUTATION_DECLARATION': handleDeclaration(context, ast)

		case 'ALIAS':                return resolveAlias(context, ast)

		case 'RUNTIME_ITERATOR':     return resolveRuntimeIterator(context, ast)
		case 'ITEM_PROPERTY':        return resolveItemProperty(context, ast)
		case 'STATIC_VALUE':         return resolveStaticValue(context, ast)

		// Inline handler - will be compiled inline. Fall through to debugger to then return the AST
		case 'HANDLER':              resolve(_createScope(context), ast.block)
		case 'DEBUGGER':             return ast

		default:                     console.log(ast); UNKNOWN_AST_TYPE
	}
}

var lookup = function(context, aliasOrValue) {
	if (aliasOrValue.type == 'NESTED_ALIAS') {
		for (var i=0, prop; prop = aliasOrValue.content[i]; i++) {
			prop.value = lookup(context, prop.value)
		}
	}
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return lookup(context, _resolveAlias(context, aliasOrValue)) }
}

function resolveAlias(context, ast) {
	var res = lookup(context, ast)
	return resolveStatement(context, res)
}

/* Item Properties
 ******************/
var resolveItemProperty = function(context, ast) {
	// TODO Can we infer the type of item properties?
	return Types.infer(ast, [])
}

/* Static values
 ****************/

var resolveStaticValue = function(context, ast) {
	switch(ast.valueType) {
		case 'string': return Types.infer(ast, [Types.byName.Text])
		case 'number': return Types.infer(ast, [Types.byName.Number])
		default:       halt(ast, 'Unknown static value type "'+ast.valueType+'"')
	}
}

/* XML
 ******/
var resolveXML = function(context, ast) {
	ast.attributes = map(ast.attributes, bind(this, _resolveAttributes, context))
	ast.block = resolve(context, ast.block)
	return ast
}

var _resolveAttributes = function(context, ast) {
	assert(ast, ast.namespace.length == 1, 'TODO Handle dot notation XML attribute namespace (for e.g. style.width=100)')
	ast.value = resolve(context, ast.value)
	return ast
}

/* Imports (modules and files)
 ******************************/
var handleModuleImport = function(context, ast) {
	if (context.modules[ast.name]) { return }
	var module = context.modules[ast.name] = { name: ast.name, path: __dirname + '/Modules/' + ast.name + '/' }
	assert(ast, fs.statSync(module.path).isDirectory(), 'Could not find the module at ' + module.path)

	// TODO Read a package/manifest.json file in the module directory, describing name/version/which files to load, etc
	var funFile = module.path + module.name + '.fun'
	if (fs.statSync(funFile).isFile()) { _importFile(funFile, context) }

	var jsFile = module.path + module.name + '.js'
	if (path.existsSync(jsFile)) { module.jsCode = fs.readFileSync(jsFile) }
	else { module.jsCode = '// No JS code for ' + module.name }
}

var handleFileImport = function(context, ast) {
	var filePath = __dirname + '/' + ast.path + '.fun'
	assert(ast, path.existsSync(filePath), 'Could not find file for import: "'+filePath+'"')
	context.fileDependencies.push(filePath)
	_importFile(filePath, context, true)
}

var _importFile = function(path, context, a) {
	var tokens = tokenizer.tokenize(path)
	var newAST = parser.parse(tokens)
	resolve(context, newAST)
}

/* Invocations
 **************/
var resolveInvocation = function(context, ast) {
	if (ast.alias) { ast.invocable = lookup(context, ast.alias) }
	assert(ast, ast.invocable, 'Found an invocation without a reference to a invocable')
	return ast
}

/* Mutations
 ************/
var resolveMutation = function(context, ast) {
	ast.value = lookup(context, ast.alias)
	ast.method = ast.alias.namespace.pop()
	delete ast.alias
	ast.args = map(ast.args, bind(this, lookup, context))
	Types.inferByMethod(ast.value, ast.method)
	return ast
}

/* For loops
 ************/
var resolveForLoop = function(context, ast) {
	ast.iteratorRuntimeName = ast.iterator.value.runtimeName = name('RUNTIME_ITERATOR_NAME')
	ast.iterable = lookup(context, ast.iterable)
	ast.iterator.value.iterable = ast.iterable
	Types.infer(ast.iterable, [Types.byName.List])
	var loopContext = _createScope(context)
	handleDeclaration(loopContext, ast.iterator)
	ast.block = resolve(loopContext, ast.block)
	return ast
}

var resolveRuntimeIterator = function(context, ast) {
	// TODO give types to runtime iterators, so that you can have complex items in lists
	// TODO Infer type of iterator from the iterable
	return Types.infer(ast, [Types.Text])
}

/* If statements
 ****************/
var resolveIfStatement = function(context, ast) {
	ast.condition.left = lookup(context, ast.condition.left)
	if (ast.condition.right) {
		ast.condition.right = lookup(context, ast.condition.right)
	}
	ast.ifBlock = resolve(_createScope(context), ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseBlock = resolve(_createScope(context), ast.elseBlock)
	}
	return ast
}

/* Declarations
 ***************/
var handleDeclaration = function(context, ast) {
	_declareAlias(context, ast)
	handleDeclarationsWithCompilation(context, ast.value)
}

// some types need compiled code just by being declared
var handleDeclarationsWithCompilation = function(context, ast) {
	switch(ast.type) {
		case 'TEMPLATE':
		case 'HANDLER':
			context.declarations.push(ast)
			resolve(_createScope(context), ast.block)
			break
		case 'MUTATION_ITEM_CREATION':
			each(ast.properties.content, function(prop) {
				prop.value = lookup(context, prop.value)
			})
		default:
			// do nothing
	}
}

var _declareAlias = function(context, ast) {
	var aliases = context.aliases,
		namespace = ast.namespace,
		valueAST = ast.value

	if (valueAST.type == 'NESTED_ALIAS') {
		var baseNamespace = ast.namespace
		for (var i=0, kvp; kvp = valueAST.content[i]; i++) {
			var nestedDeclarationAST = util.create(ast)
			nestedDeclarationAST.namespace = namespace.concat(kvp.name)
			nestedDeclarationAST.value = kvp.value
			handleDeclaration(context, nestedDeclarationAST)
		}
	} else {
		var namespaceKey = ast.namespace.join('.')
		assert(ast, !aliases[namespaceKey], 'Repeat declaration of "'+namespaceKey+'"')
		aliases[namespaceKey] = valueAST
	}
}

var _resolveAlias = function(context, ast, skipLast) {
	var lookupNamespace = [],
		aliases = context.aliases,
		len = ast.namespace.length - (skipLast ? 1 : 0)

	for (var i=0; i < len; i++) {
		lookupNamespace.push(ast.namespace[i])
		var namespaceKey = lookupNamespace.join('.'),
			value = context.aliases[namespaceKey]

		if (!value) { continue }

		switch(value.type) {
			case 'RUNTIME_ITERATOR':
				return util.shallowCopy(value, { iteratorProperty: ast.namespace.slice(i).join('.') })
			case 'ITEM':
				return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:ast.namespace.slice(i+1) })
			default:
				return value
		}
	}

	halt(ast, 'Lookup of undeclared alias "'+ast.namespace.join('.')+'"')
}

/* Utility
 **********/
var ResolveError = function(file, ast, msg) {
	this.name = "ResolveError"
	this.message = ['on line', ast.line + ',', 'column', ast.column, 'of', '"'+file+'":', msg].join(' ')
}
ResolveError.prototype = Error.prototype

function _createScope(context) {
	// Creates a scope by prototypically inheriting from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// However, writes *will* shadow values up the prototype chain
	context = util.create(context)
	context.aliases = util.create(context.aliases)
	return context
}

var assert = function(ast, ok, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	if (ast.file) { sys.puts(util.grabLine(ast.file, ast.line, ast.column, ast.span)) }
	throw new ResolveError(ast.file, ast, msg)
}
<commit_msg>Fix a bad bug where handleDeclaration would fall through to return resolveAlias<commit_after>var fs = require('fs'),
	sys = require('sys'),
	path = require('path')

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	bind = util.bind,
	map = util.map,
	each = util.each,
	name = util.name,
	shallowCopy = util.shallowCopy

// Resolve imports by injecting declarations into the reference table
// Resolve aliases to values
// Return an AST with no import statements and no aliases

// TODO Read types from types
// TODO read tags from tags
var Tags = require('./Tags'),
	Types = require('./Types')

exports.resolve = util.intercept('ResolveError', function (ast, context) {
	if (!context) {
		context = { modules:{}, declarations:[], fileDependencies:[], aliases: {} }
	}
	var ast = resolve(context, ast)
	return {
		ast: ast,
		modules: context.modules,
		declarations: context.declarations,
		dependencies: context.dependencies
	}
})

var resolve = function(context, ast) {
	if (!ast) {
		return null
	} else if (ast instanceof Array) {
		return map(ast, bind(this, resolveStatement, context))
	} else {
		return resolveStatement(context, ast)
	}
}

var resolveStatement = function(context, ast) {
	switch(ast.type) {
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)      ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)        ;break
		case 'DECLARATION':          handleDeclaration(context, ast)       ;break

		case 'XML':                  return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)
		case 'INVOCATION':           return resolveInvocation(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'MUTATION_DECLARATION': handleDeclaration(context, ast)       ;break

		case 'ALIAS':                return resolveAlias(context, ast)

		case 'RUNTIME_ITERATOR':     return resolveRuntimeIterator(context, ast)
		case 'ITEM_PROPERTY':        return resolveItemProperty(context, ast)
		case 'STATIC_VALUE':         return resolveStaticValue(context, ast)

		// Inline handler - will be compiled inline. Fall through to debugger to then return the AST
		case 'HANDLER':              resolve(_createScope(context), ast.block)
		case 'DEBUGGER':             return ast

		default:                     console.log(ast); UNKNOWN_AST_TYPE
	}
}

var lookup = function(context, aliasOrValue) {
	if (aliasOrValue.type == 'NESTED_ALIAS') {
		for (var i=0, prop; prop = aliasOrValue.content[i]; i++) {
			prop.value = lookup(context, prop.value)
		}
	}
	if (aliasOrValue.type != 'ALIAS') { return aliasOrValue }
	else { return lookup(context, _resolveAlias(context, aliasOrValue)) }
}

function resolveAlias(context, ast) {
	var res = lookup(context, ast)
	return resolveStatement(context, res)
}

/* Item Properties
 ******************/
var resolveItemProperty = function(context, ast) {
	// TODO Can we infer the type of item properties?
	return Types.infer(ast, [])
}

/* Static values
 ****************/

var resolveStaticValue = function(context, ast) {
	switch(ast.valueType) {
		case 'string': return Types.infer(ast, [Types.byName.Text])
		case 'number': return Types.infer(ast, [Types.byName.Number])
		default:       halt(ast, 'Unknown static value type "'+ast.valueType+'"')
	}
}

/* XML
 ******/
var resolveXML = function(context, ast) {
	ast.attributes = map(ast.attributes, bind(this, _resolveAttributes, context))
	ast.block = resolve(context, ast.block)
	return ast
}

var _resolveAttributes = function(context, ast) {
	assert(ast, ast.namespace.length == 1, 'TODO Handle dot notation XML attribute namespace (for e.g. style.width=100)')
	ast.value = resolve(context, ast.value)
	return ast
}

/* Imports (modules and files)
 ******************************/
var handleModuleImport = function(context, ast) {
	if (context.modules[ast.name]) { return }
	var module = context.modules[ast.name] = { name: ast.name, path: __dirname + '/Modules/' + ast.name + '/' }
	assert(ast, fs.statSync(module.path).isDirectory(), 'Could not find the module at ' + module.path)

	// TODO Read a package/manifest.json file in the module directory, describing name/version/which files to load, etc
	var funFile = module.path + module.name + '.fun'
	if (fs.statSync(funFile).isFile()) { _importFile(funFile, context) }

	var jsFile = module.path + module.name + '.js'
	if (path.existsSync(jsFile)) { module.jsCode = fs.readFileSync(jsFile) }
	else { module.jsCode = '// No JS code for ' + module.name }
}

var handleFileImport = function(context, ast) {
	var filePath = __dirname + '/' + ast.path + '.fun'
	assert(ast, path.existsSync(filePath), 'Could not find file for import: "'+filePath+'"')
	context.fileDependencies.push(filePath)
	_importFile(filePath, context, true)
}

var _importFile = function(path, context, a) {
	var tokens = tokenizer.tokenize(path)
	var newAST = parser.parse(tokens)
	resolve(context, newAST)
}

/* Invocations
 **************/
var resolveInvocation = function(context, ast) {
	if (ast.alias) { ast.invocable = lookup(context, ast.alias) }
	assert(ast, ast.invocable, 'Found an invocation without a reference to a invocable')
	return ast
}

/* Mutations
 ************/
var resolveMutation = function(context, ast) {
	ast.value = lookup(context, ast.alias)
	ast.method = ast.alias.namespace.pop()
	delete ast.alias
	ast.args = map(ast.args, bind(this, lookup, context))
	Types.inferByMethod(ast.value, ast.method)
	return ast
}

/* For loops
 ************/
var resolveForLoop = function(context, ast) {
	ast.iteratorRuntimeName = ast.iterator.value.runtimeName = name('RUNTIME_ITERATOR_NAME')
	ast.iterable = lookup(context, ast.iterable)
	ast.iterator.value.iterable = ast.iterable
	Types.infer(ast.iterable, [Types.byName.List])
	var loopContext = _createScope(context)
	handleDeclaration(loopContext, ast.iterator)
	ast.block = resolve(loopContext, ast.block)
	return ast
}

var resolveRuntimeIterator = function(context, ast) {
	// TODO give types to runtime iterators, so that you can have complex items in lists
	// TODO Infer type of iterator from the iterable
	return Types.infer(ast, [Types.Text])
}

/* If statements
 ****************/
var resolveIfStatement = function(context, ast) {
	ast.condition.left = lookup(context, ast.condition.left)
	if (ast.condition.right) {
		ast.condition.right = lookup(context, ast.condition.right)
	}
	ast.ifBlock = resolve(_createScope(context), ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseBlock = resolve(_createScope(context), ast.elseBlock)
	}
	return ast
}

/* Declarations
 ***************/
var handleDeclaration = function(context, ast) {
	_declareAlias(context, ast)
	handleDeclarationsWithCompilation(context, ast.value)
}

// some types need compiled code just by being declared
var handleDeclarationsWithCompilation = function(context, ast) {
	switch(ast.type) {
		case 'TEMPLATE':
		case 'HANDLER':
			context.declarations.push(ast)
			resolve(_createScope(context), ast.block)
			break
		case 'MUTATION_ITEM_CREATION':
			each(ast.properties.content, function(prop) {
				prop.value = lookup(context, prop.value)
			})
		default:
			// do nothing
	}
}

var _declareAlias = function(context, ast) {
	var aliases = context.aliases,
		namespace = ast.namespace,
		valueAST = ast.value

	if (valueAST.type == 'NESTED_ALIAS') {
		var baseNamespace = ast.namespace
		for (var i=0, kvp; kvp = valueAST.content[i]; i++) {
			var nestedDeclarationAST = util.create(ast)
			nestedDeclarationAST.namespace = namespace.concat(kvp.name)
			nestedDeclarationAST.value = kvp.value
			handleDeclaration(context, nestedDeclarationAST)
		}
	} else {
		var namespaceKey = ast.namespace.join('.')
		assert(ast, !aliases[namespaceKey], 'Repeat declaration of "'+namespaceKey+'"')
		aliases[namespaceKey] = valueAST
	}
}

var _resolveAlias = function(context, ast, skipLast) {
	var lookupNamespace = [],
		aliases = context.aliases,
		len = ast.namespace.length - (skipLast ? 1 : 0)

	for (var i=0; i < len; i++) {
		lookupNamespace.push(ast.namespace[i])
		var namespaceKey = lookupNamespace.join('.'),
			value = context.aliases[namespaceKey]

		if (!value) { continue }

		switch(value.type) {
			case 'RUNTIME_ITERATOR':
				return util.shallowCopy(value, { iteratorProperty: ast.namespace.slice(i).join('.') })
			case 'ITEM':
				return util.shallowCopy(ast, { type: 'ITEM_PROPERTY', item:value, property:ast.namespace.slice(i+1) })
			default:
				return value
		}
	}

	halt(ast, 'Lookup of undeclared alias "'+ast.namespace.join('.')+'"')
}

/* Utility
 **********/
var ResolveError = function(file, ast, msg) {
	this.name = "ResolveError"
	this.message = ['on line', ast.line + ',', 'column', ast.column, 'of', '"'+file+'":', msg].join(' ')
}
ResolveError.prototype = Error.prototype

function _createScope(context) {
	// Creates a scope by prototypically inheriting from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// However, writes *will* shadow values up the prototype chain
	context = util.create(context)
	context.aliases = util.create(context.aliases)
	return context
}

var assert = function(ast, ok, msg) { if (!ok) halt(ast, msg) }
var halt = function(ast, msg) {
	if (ast.file) { sys.puts(util.grabLine(ast.file, ast.line, ast.column, ast.span)) }
	throw new ResolveError(ast.file, ast, msg)
}
<|endoftext|>


<commit_before>// Set up for file references
var scriptsFile = __dirname + '/scripts.json',
    competitionsFile = __dirname + '/competitions.json',
    contributionsFile = __dirname + '/contributions.json';

// Load in all the files
var scripts = require(scriptsFile),
    competitions = require(competitionsFile),
    contributions = require(contributionsFile),
    fs = require('fs'),
    fetchRepoStats = require('./fetchRepoStats'),
    fetchMDNStats = require('./fetchMDNStats');

// Generate a srcUrl and pageUrl for scripts, competitions, and contributions
scripts.forEach(saveSrcUrl);
scripts.forEach(savePageUrl);
competitions.forEach(saveSrcUrl);
competitions.forEach(savePageUrl);
contributions.forEach(saveSrcUrl);
contributions.forEach(savePageUrl);

// Now and every hour from now, update file stats
var second = 1000,
    everyHour = second * 60 * 60;
function noop() {}
function updateScript(script) {
  // If it not a gist, update it
  var github = script.github || '';
  if (github.indexOf('gist') === -1) {
    fetchRepoStats(github, function (err, data) {
      // If there is no error, update the script
      if (!err) {
        if (data.stars !== undefined) { script.stars = data.stars; }
        if (data.forks !== undefined) { script.forks = data.forks; }
      }
    });
  }
}
function updateCompetition(competition) {
  // If it an mdn file, update it
  var mdn = competition.mdn;
  if (mdn) {
    fetchMDNStats(mdn, function (err, data) {
      // If there is no error, update the competition
      if (!err) {
        if (data.views !== undefined) { competition.views = data.views; }
        if (data.likes !== undefined) { competition.likes = data.likes; }
      }
    });
  }
}
function updateStats() {
  // Update each of the types
  scripts.forEach(updateScript);
  competitions.forEach(updateCompetition);
  contributions.forEach(updateScript);

  // If we are being run standalone, save file
  if (!module.parent) {
    // Sort the scripts and contributions by stars then forks
    function sortRepos(a, b) {
      var aStars = a.stars,
          bStars = b.stars;
      if (aStars < bStars) { return 1; }
      if (aStars > bStars) { return -1; }

      var aForks = a.forks,
          bForks = b.forks;
      if (aForks < bForks) { return 1; }
      if (aForks > bForks) { return -1; }

      var aName = a.name,
          bName = b.name;
      if (aName > bName) { return 1; }
      if (aName < bName) { return -1; }
      return 0;
    }
    scripts.sort(sortRepos);
    contributions.sort(sortRepos);

    // In a minute, save the updates to their respective JSON files
    setTimeout(function () {
      fs.writeFile(scriptsFile, JSON.stringify(scripts, null, 2), noop);
      fs.writeFile(competitionsFile, JSON.stringify(competitions, null, 2), noop);
      fs.writeFile(contributionsFile, JSON.stringify(contributions, null, 2), noop);
    }, 1000);
  }
}
// console.log('*** WARNING: OFFLINE FETCH IS DISABLED ***');
setInterval(updateStats, everyHour);
process.nextTick(updateStats);

module.exports = {
  "scripts": scripts,
  "competitions": competitions,
  "contributions": contributions
};

function getSrcUrl(item) {
  // Get the github info and url fallback
  var github = item.github,
      srcUrl = item.srcUrl;

  // If there is a srcUrl, return now
  if (srcUrl !== undefined) {
    return srcUrl;
  }

  // If there is a github repo, use that
  if (github !== undefined) {
    // If the item is a gist, return it as such
    var isGist = github.indexOf('gist') > -1;
    if (isGist) {
      srcUrl = 'https://gist.github.com/' + github.slice(5);
    } else {
    // Otherwise, go with the normal github flow
      srcUrl = 'https://github.com/' + github;
    }
  }

  return srcUrl;
}

function getPageUrl(item) {
  var srcUrl = getSrcUrl(item),
      mdn = item.mdn,
      pageUrl = item.pageUrl;

  // If the pageUrl is not defined
  if (pageUrl === undefined) {
    // and mdn is defined, use that
    if (mdn !== undefined) {
      pageUrl = 'https://developer.mozilla.org/en-US/demos/detail/' + mdn;
    } else {
    // Otherwise, fallback to the srcUrl
      pageUrl = srcUrl;
    }
  }

  return pageUrl;
}

function saveSrcUrl(item) {
  var srcUrl = getSrcUrl(item);
  item.srcUrl = srcUrl;
}

function savePageUrl(item) {
  var pageUrl = getPageUrl(item);
  item.pageUrl = pageUrl;
}<commit_msg>Removed initial pre-fetch when not in development<commit_after>// Set up for file references
var config = require('../config'),
    scriptsFile = __dirname + '/scripts.json',
    competitionsFile = __dirname + '/competitions.json',
    contributionsFile = __dirname + '/contributions.json';

// Load in all the files
var scripts = require(scriptsFile),
    competitions = require(competitionsFile),
    contributions = require(contributionsFile),
    fs = require('fs'),
    fetchRepoStats = require('./fetchRepoStats'),
    fetchMDNStats = require('./fetchMDNStats');

// Generate a srcUrl and pageUrl for scripts, competitions, and contributions
scripts.forEach(saveSrcUrl);
scripts.forEach(savePageUrl);
competitions.forEach(saveSrcUrl);
competitions.forEach(savePageUrl);
contributions.forEach(saveSrcUrl);
contributions.forEach(savePageUrl);

// Now and every hour from now, update file stats
function noop() {}
function updateScript(script) {
  // If it not a gist, update it
  var github = script.github || '';
  if (github.indexOf('gist') === -1) {
    fetchRepoStats(github, function (err, data) {
      // If there is no error, update the script
      if (!err) {
        if (data.stars !== undefined) { script.stars = data.stars; }
        if (data.forks !== undefined) { script.forks = data.forks; }
      }
    });
  }
}
function updateCompetition(competition) {
  // If it an mdn file, update it
  var mdn = competition.mdn;
  if (mdn) {
    fetchMDNStats(mdn, function (err, data) {
      // If there is no error, update the competition
      if (!err) {
        if (data.views !== undefined) { competition.views = data.views; }
        if (data.likes !== undefined) { competition.likes = data.likes; }
      }
    });
  }
}
function updateStats() {
  // Update each of the types
  scripts.forEach(updateScript);
  competitions.forEach(updateCompetition);
  contributions.forEach(updateScript);

  // If we are being run standalone, save file
  if (!module.parent) {
    // Sort the scripts and contributions by stars then forks
    function sortRepos(a, b) {
      var aStars = a.stars,
          bStars = b.stars;
      if (aStars < bStars) { return 1; }
      if (aStars > bStars) { return -1; }

      var aForks = a.forks,
          bForks = b.forks;
      if (aForks < bForks) { return 1; }
      if (aForks > bForks) { return -1; }

      var aName = a.name,
          bName = b.name;
      if (aName > bName) { return 1; }
      if (aName < bName) { return -1; }
      return 0;
    }
    scripts.sort(sortRepos);
    contributions.sort(sortRepos);

    // In a minute, save the updates to their respective JSON files
    setTimeout(function () {
      fs.writeFile(scriptsFile, JSON.stringify(scripts, null, 2), noop);
      fs.writeFile(competitionsFile, JSON.stringify(competitions, null, 2), noop);
      fs.writeFile(contributionsFile, JSON.stringify(contributions, null, 2), noop);
    }, 1000);
  }
}

// Every hour, update the stats
// console.log('*** WARNING: OFFLINE FETCH IS DISABLED ***');
var second = 1000,
    everyHour = second * 60 * 60;
setInterval(updateStats, everyHour);

// If we are in production, fetch now
if (config.inProduction) {
  process.nextTick(updateStats);
}

module.exports = {
  "scripts": scripts,
  "competitions": competitions,
  "contributions": contributions
};

function getSrcUrl(item) {
  // Get the github info and url fallback
  var github = item.github,
      srcUrl = item.srcUrl;

  // If there is a srcUrl, return now
  if (srcUrl !== undefined) {
    return srcUrl;
  }

  // If there is a github repo, use that
  if (github !== undefined) {
    // If the item is a gist, return it as such
    var isGist = github.indexOf('gist') > -1;
    if (isGist) {
      srcUrl = 'https://gist.github.com/' + github.slice(5);
    } else {
    // Otherwise, go with the normal github flow
      srcUrl = 'https://github.com/' + github;
    }
  }

  return srcUrl;
}

function getPageUrl(item) {
  var srcUrl = getSrcUrl(item),
      mdn = item.mdn,
      pageUrl = item.pageUrl;

  // If the pageUrl is not defined
  if (pageUrl === undefined) {
    // and mdn is defined, use that
    if (mdn !== undefined) {
      pageUrl = 'https://developer.mozilla.org/en-US/demos/detail/' + mdn;
    } else {
    // Otherwise, fallback to the srcUrl
      pageUrl = srcUrl;
    }
  }

  return pageUrl;
}

function saveSrcUrl(item) {
  var srcUrl = getSrcUrl(item);
  item.srcUrl = srcUrl;
}

function savePageUrl(item) {
  var pageUrl = getPageUrl(item);
  item.pageUrl = pageUrl;
}<|endoftext|>


<commit_before>var layouts     = require("../layouts");
var Immutable   = require("immutable");
var _           = require("lodash");
var concat      = require("concat-stream");

/**
 * @param {{data: [object], key: string, content: string, cb: [function]}} opts
 * @returns {Function}
 */
module.exports = function compile (opts) {

    var compiler = this;

    //if (!compiler.frozen) {
        compiler.freeze();
    //}

    opts.item     = addItem(opts, compiler);

    compiler.item = opts.item;

    compiler.data = addData(opts, compiler);

    var content   = opts.item.get("content");

    var rs = readStream(content);
    rs.push(content);

        rs.pipe(s(function (chunk, enc, done) {
            this.push(runContentTransforms({
                scope: "before item parsed",
                compiler: compiler,
                item: opts.item,
                content: chunk.toString()
            }));
            done();
        }))

        .pipe(s(function (chunk, enc, done) {
            var stream = this;
            compiler.template.render(chunk.toString(), compiler.frozen, function (err, out) {
                if (err) {
                    compiler.error(err);
                    return done();
                }
                stream.push(_.isString(out) ? out : out.string || "");
                done();
            });
        }))

        .pipe(s(function (chunk, enc, done) {
            this.push(runContentTransforms({
                scope: "before item render",
                compiler: compiler,
                item: opts.item,
                content: chunk.toString()
            }));
            done();
        }))

        .pipe(s(function (chunk, enc, done) {
            compiler.template.addContent({
                content: chunk.toString(),
                config:  compiler.config,
                context: compiler.frozen
            });
            this.push(chunk);
            done();
        }))

        .pipe(s(function (chunk, enc, done) {

            var stream = this;

            var layoutPath = compiler.config.get("defaultLayout");
            var itemlayout = opts.item.getIn(["front","layout"]);

            /**
             * If no layout specified in front-matter
             * & no default layout file, exit early and don't modify item content
             */
            if (!itemlayout && !layoutPath) {
                stream.push(chunk);
                return done();
            }

            /**
             * If a layout was specified in the item, use that instead
             */
            if (itemlayout) {
                layoutPath = itemlayout;
            }

            /**
             * Recursively add layouts
             */
            layouts({
                compiler: compiler,
                content: chunk.toString(),
                layout: layoutPath,
                item: opts.item,
                cb: function (err, out) {
                    if (err) {
                        compiler.error(err);
                    }
                    stream.push(out.content);
                    done();
                }
            });
        }))

        .pipe(s(function (body) {
            var updated = opts.item.set("compiled", body.toString());
            compiler.cache.add(updated);
            opts.cb(null, updated);
            this.push(null);
            this.emit("end");
        }));
};

/**
 * Add site data.
 * If given as a string, read the file, or anything else, us as is. (for example, an object)
 * @param opts
 * @param compiler
 * @returns {any}
 */
function addData (opts, compiler) {

    opts.data = opts.data || {};
    compiler.mergeData(opts.data, compiler.frozen);
    var item = opts.item.toJS();
    item.compiled              = item.content;
    compiler.frozen[item.type] = item;
    compiler.frozen["page"]    = item;
}

/**
 * Get the to-be-compiled item
 * @param opts
 * @param compiler
 * @returns {*}
 */
function addItem (opts, compiler) {
    if (opts.item) {
        return opts.item;
    }
    var out = compiler.add(opts);
    return out;
}

/**
 * @param opts
 * @returns {*}
 */
function runContentTransforms(opts) {

    var transforms = opts.compiler.pluginManager.hook("contentTransforms", opts.compiler);

    var out = "";
    require("lodash").each(transforms, function (plugin) {
        if (plugin.when === opts.scope) {
            out = plugin.fn(opts);
        }
    });

    return out;
}

var Readable  = require("stream").Readable;
var Transform = require("stream").Transform;

function readStream (content) {
    var rs = new Readable({objectMode: true});
    rs._read = function () {};
    return rs;
}

function s (fn, fnend) {
    var ws = new Transform();
    ws._write = fn;
    if (fnend) {
        ws._flush = fnend;
    }
    return ws;
}
<commit_msg>Remove un-used module<commit_after>var layouts     = require("../layouts");
var Immutable   = require("immutable");
var _           = require("lodash");

/**
 * @param {{data: [object], key: string, content: string, cb: [function]}} opts
 * @returns {Function}
 */
module.exports = function compile (opts) {

    var compiler = this;

    //if (!compiler.frozen) {
        compiler.freeze();
    //}

    opts.item     = addItem(opts, compiler);

    compiler.item = opts.item;

    compiler.data = addData(opts, compiler);

    var content   = opts.item.get("content");

    var rs = readStream(content);
    rs.push(content);

        rs.pipe(s(function (chunk, enc, done) {
            this.push(runContentTransforms({
                scope: "before item parsed",
                compiler: compiler,
                item: opts.item,
                content: chunk.toString()
            }));
            done();
        }))

        .pipe(s(function (chunk, enc, done) {
            var stream = this;
            compiler.template.render(chunk.toString(), compiler.frozen, function (err, out) {
                if (err) {
                    compiler.error(err);
                    return done();
                }
                stream.push(_.isString(out) ? out : out.string || "");
                done();
            });
        }))

        .pipe(s(function (chunk, enc, done) {
            this.push(runContentTransforms({
                scope: "before item render",
                compiler: compiler,
                item: opts.item,
                content: chunk.toString()
            }));
            done();
        }))

        .pipe(s(function (chunk, enc, done) {
            compiler.template.addContent({
                content: chunk.toString(),
                config:  compiler.config,
                context: compiler.frozen
            });
            this.push(chunk);
            done();
        }))

        .pipe(s(function (chunk, enc, done) {

            var stream = this;

            var layoutPath = compiler.config.get("defaultLayout");
            var itemlayout = opts.item.getIn(["front","layout"]);

            /**
             * If no layout specified in front-matter
             * & no default layout file, exit early and don't modify item content
             */
            if (!itemlayout && !layoutPath) {
                stream.push(chunk);
                return done();
            }

            /**
             * If a layout was specified in the item, use that instead
             */
            if (itemlayout) {
                layoutPath = itemlayout;
            }

            /**
             * Recursively add layouts
             */
            layouts({
                compiler: compiler,
                content: chunk.toString(),
                layout: layoutPath,
                item: opts.item,
                cb: function (err, out) {
                    if (err) {
                        compiler.error(err);
                    }
                    stream.push(out.content);
                    done();
                }
            });
        }))

        .pipe(s(function (body) {
            var updated = opts.item.set("compiled", body.toString());
            compiler.cache.add(updated);
            opts.cb(null, updated);
            this.push(null);
            this.emit("end");
        }));
};

/**
 * Add site data.
 * If given as a string, read the file, or anything else, us as is. (for example, an object)
 * @param opts
 * @param compiler
 * @returns {any}
 */
function addData (opts, compiler) {

    opts.data = opts.data || {};
    compiler.mergeData(opts.data, compiler.frozen);
    var item = opts.item.toJS();
    item.compiled              = item.content;
    compiler.frozen[item.type] = item;
    compiler.frozen["page"]    = item;
}

/**
 * Get the to-be-compiled item
 * @param opts
 * @param compiler
 * @returns {*}
 */
function addItem (opts, compiler) {
    if (opts.item) {
        return opts.item;
    }
    var out = compiler.add(opts);
    return out;
}

/**
 * @param opts
 * @returns {*}
 */
function runContentTransforms(opts) {

    var transforms = opts.compiler.pluginManager.hook("contentTransforms", opts.compiler);

    var out = "";
    require("lodash").each(transforms, function (plugin) {
        if (plugin.when === opts.scope) {
            out = plugin.fn(opts);
        }
    });

    return out;
}

var Readable  = require("stream").Readable;
var Transform = require("stream").Transform;

function readStream (content) {
    var rs = new Readable({objectMode: true});
    rs._read = function () {};
    return rs;
}

function s (fn, fnend) {
    var ws = new Transform();
    ws._write = fn;
    if (fnend) {
        ws._flush = fnend;
    }
    return ws;
}
<|endoftext|>


<commit_before>"use strict"

var SVGElement = require("./SVGElement.js")
  , inherits = require("util").inherits

var SVGShape = module.exports = function (elementName) {
  SVGElement.call(this, elementName)
}
inherits(SVGShape, SVGElement)

SVGShape.prototype.setStrokeColor = function (color) {
  this.rawSVGElement().style.stroke = color
}

SVGShape.prototype.setStrokeOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("stroke-opacity", decimal)
}

SVGShape.prototype.setStrokeWidth = function (width) {
  this.rawSVGElement().style.strokeWidth = width
}

SVGShape.prototype.setStrokeLinecap = function (type) {
  this.rawSVGElement().setAttribute("stroke-linecap", type)
}

SVGShape.prototype.setFillColor = function (color) {
  this.rawSVGElement().setAttribute("fill", color)
}

SVGShape.prototype.setFillOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("fill-opacity", decimal)
}
<commit_msg>Fix .setStrokeColor() and .setStrokeWidth() using CSS instead of SVG attributes<commit_after>"use strict"

var SVGElement = require("./SVGElement.js")
  , inherits = require("util").inherits

var SVGShape = module.exports = function (elementName) {
  SVGElement.call(this, elementName)
}
inherits(SVGShape, SVGElement)

SVGShape.prototype.setStrokeColor = function (color) {
  this.rawSVGElement().setAttribute("stroke", color)
}

SVGShape.prototype.setStrokeOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("stroke-opacity", decimal)
}

SVGShape.prototype.setStrokeWidth = function (width) {
  this.rawSVGElement().setAttribute("stroke-width", width)
}

SVGShape.prototype.setStrokeLinecap = function (type) {
  this.rawSVGElement().setAttribute("stroke-linecap", type)
}

SVGShape.prototype.setFillColor = function (color) {
  this.rawSVGElement().setAttribute("fill", color)
}

SVGShape.prototype.setFillOpacity = function (decimal) {
  this.rawSVGElement().setAttribute("fill-opacity", decimal)
}
<|endoftext|>


<commit_before>// takes node and edge data and out node and edge svgs
//
var React = require('react')
var Url = require('url')
var r = require('r-dom')
var map = require('lodash.map')
var debug = require('debug')('holodex:tag-group')
var keys = require('lodash.keys')
var reduce = require('lodash.reduce')
var clone = require('lodash.clone')

var Tag = require('./tag')
var TagSelect = require('./tag-select')
var config = require('config')

var vars = require('ui/vars')

var prefix = Url.format(config.api)

var style = {
  index: {
    marginTop: vars.gutterWidth/2,
    marginLeft: vars.gutterWidth,
    display: 'flex',
    flexDirection: 'column'
  },
  triple: {
    order: 0,
    display: 'flex',
    flexDirection: 'row'
  },
  tripleTag: {
  },
  filters: {
    marginLeft: vars.gutterWidth/2,
    order: 1,
    display: 'flex',
    flexDirection: 'column'
  },
  filterTag: {
    marginTop: vars.gutterWidth/3
  },
  tagInputContainer: {
    order: 2,
    marginTop: vars.gutterWidth/3,
    marginLeft: vars.gutterWidth/2
  },
  tagContainer: {
    marginRight: vars.gutterWidth/4,
    display: 'flex',
    flexDirection: 'row'
  },
  iconStyle: {
    lineHeight: '40px',
    marginRight: vars.gutterWidth/4
  }
}

module.exports = React.createClass({

  propTypes: {
    style: React.PropTypes.object,
    tagData: React.PropTypes.object.isRequired,
    updateTag: React.PropTypes.func.isRequired,
    model: React.PropTypes.object,
    cancelIcon: React.PropTypes.node,
    Icon: React.PropTypes.node
  },

  render: function () {
    debug('props', this.props)
    var tagData = this.props.tagData
    var cancelIcon = this.props.cancelIcon
    debug('tagData', tagData)
    var withPred = tagData.triple.predicate.active
    var pluralSubj = tagData.triple.subject.isPlural
    this.style = style

    var filters = tagData.filters.reduce(function (memo, filter, i) {
      if (filter.active) {
        var icon = filter.type === 'Role' ? 'circle-outline' : 'long-arrow-right'
        var iconStyle = clone(this.style.iconStyle)
        iconStyle.color = filter.color
        memo.push(r.div({ className: 'tag-container', style: this.style.tagContainer }, [
          r.div({ className: 'icon-container', style: iconStyle }, [r(this.props.Icon, { icon: icon })]),
          r(Tag, {
            color: filter.color,
            icon: cancelIcon,
            label: filter.label,
            onIconClick: this.props.updateTag('filters', filter.id, false),
            trailingCancel: true
          })
        ])
        )
      }
      return memo
    }.bind(this), [])

    return r.div({ className: 'tags', style: this.style.index }, [
      r.div({ className: 'triple', style: this.style.triple },
        reduce(tagData.triple, function (memo, expression, key, triple) {
            if (expression.active) {
              debug('expression active', expression)
              var label
              var index
              debug('labeling', key, withPred, pluralSubj)
              if (key === 'subject') {
                index = 0
                label = withPred ? expression.withPredLabel : expression.label
              }
              if (key === 'predicate') {
                index = triple.subject.active ? 1 : 0
                label = pluralSubj ? expression.pluralLabel : expression.label
              }
              if (key === 'object') {
                label = expression.label
                index = triple.subject.active ? triple.predicate.active ? 2 : 1 : 0
              }

              // var style = {
              //   order: index
              // }
              // var style = style.tripleTag
              // style.order = index

              memo[index] = r.div({ className: 'triple-tag', style: {order: index} }, [r(Tag, {
                color: expression.color,
                icon: cancelIcon,
                label: label,
                onIconClick: this.props.updateTag('triple', expression.id, false),
                trailingCancel: (key === 'object')
              })])

            }
            debug('memo', memo)
            return memo
          }.bind(this), [])
      ),
      r.div({ className: 'tags-filters', style: this.style.filters },
        filters.map(function (filter, i) {
          var order = filters.length -1 - i
          var style = {order: order }
          return r.div({ className: 'filter-tag', style: this.style.filterTag }, [
            filter
          ])
        }.bind(this))
      ),
      r.div({ className: 'tag-input', style: this.style.tagInputContainer }, [
        r(TagSelect, {
          style: this.props.style,
          tagData: this.props.tagData,
          updateTag: this.props.updateTag
        })
      ])
    ])
  },
})
<commit_msg>tag-group component state holds options an tags<commit_after>// takes node and edge data and out node and edge svgs
//
var React = require('react')
var Url = require('url')
var r = require('r-dom')
var map = require('lodash.map')
var debug = require('debug')('holodex:tag-group')
var keys = require('lodash.keys')
var reduce = require('lodash.reduce')
var clone = require('lodash.clone')

var Tag = require('./tag')
var TagSelect = require('./tag-select')
var config = require('config')
var getHeaders = require('./get-headers')
var getContextTags = require('./get-context-tags')
var getOptions = require('./get-options')

var vars = require('ui/vars')

var prefix = Url.format(config.api)

var style = {
  index: {
    marginTop: vars.gutterWidth/2,
    marginLeft: vars.gutterWidth,
    display: 'flex',
    flexDirection: 'column'
  },
  triple: {
    order: 0,
    display: 'flex',
    flexDirection: 'row'
  },
  tripleTag: {
  },
  filters: {
    marginLeft: vars.gutterWidth/2,
    order: 1,
    display: 'flex',
    flexDirection: 'column'
  },
  filterTag: {
    marginTop: vars.gutterWidth/3
  },
  tagInputContainer: {
    order: 2,
    marginTop: vars.gutterWidth/3,
    marginLeft: vars.gutterWidth/2
  },
  tagContainer: {
    marginRight: vars.gutterWidth/4,
    display: 'flex',
    flexDirection: 'row'
  },
  iconStyle: {
    lineHeight: '40px',
    marginRight: vars.gutterWidth/4
  }
}

module.exports = React.createClass({

  propTypes: {
    style: React.PropTypes.object,
    tagData: React.PropTypes.object.isRequired,
    updateTag: React.PropTypes.func.isRequired,
    model: React.PropTypes.object,
    cancelIcon: React.PropTypes.node,
    Icon: React.PropTypes.node
  },

  getInitialState: function () {
    var headers
    var contextTags
    var options
    if (this.props.model.filtersByType) {
      headers = getHeaders(this.props.model.filtersByType.targetAgent)
      contextTags = getContextTags(this.props.model)
      options = getOptions(this.props.model)
    }

    return {
      headers: headers,
      contextTags: contextTags,
      options: options
    }
  },

  componentWillReceiveProps: function (nextProps) {
    if (nextProps.model) {
      var headers = getHeaders(this.props.model.filtersByType.targetAgent)
      var contextTags = this.props.model.filtersByType.context ? getContextTags(this.props.model) : []
      var options = getOptions(nextProps.model)
      this.setState({ header: headers, contextTags: contextTags, options: options })
    }
  },

  render: function () {
    debug('state', this.state)
    var tagData = this.props.tagData
    var cancelIcon = this.props.cancelIcon
    var withPred = tagData.triple.predicate.active
    var pluralSubj = tagData.triple.subject.isPlural
    this.style = style

    var filters = tagData.filters.reduce(function (memo, filter, i) {
      if (filter.active) {
        var icon = filter.type === 'Role' ? 'circle-outline' : 'long-arrow-right'
        var iconStyle = clone(this.style.iconStyle)
        iconStyle.color = filter.color
        memo.push(r.div({ className: 'tag-container', style: this.style.tagContainer }, [
          r.div({ className: 'icon-container', style: iconStyle }, [r(this.props.Icon, { icon: icon })]),
          r(Tag, {
            color: filter.color,
            icon: cancelIcon,
            label: filter.label,
            onIconClick: this.props.updateTag('filters', filter.id, false),
            trailingCancel: true
          })
        ])
        )
      }
      return memo
    }.bind(this), [])

    return r.div({ className: 'tags', style: this.style.index }, [
      r.div({ className: 'triple', style: this.style.triple },
        reduce(tagData.triple, function (memo, expression, key, triple) {
            if (expression.active) {
              debug('expression active', expression)
              var label
              var index
              debug('labeling', key, withPred, pluralSubj)
              if (key === 'subject') {
                index = 0
                label = withPred ? expression.withPredLabel : expression.label
              }
              if (key === 'predicate') {
                index = triple.subject.active ? 1 : 0
                label = pluralSubj ? expression.pluralLabel : expression.label
              }
              if (key === 'object') {
                label = expression.label
                index = triple.subject.active ? triple.predicate.active ? 2 : 1 : 0
              }

              // var style = {
              //   order: index
              // }
              // var style = style.tripleTag
              // style.order = index

              memo[index] = r.div({ className: 'triple-tag', style: {order: index} }, [r(Tag, {
                color: expression.color,
                icon: cancelIcon,
                label: label,
                onIconClick: this.props.updateTag('triple', expression.id, false),
                trailingCancel: (key === 'object')
              })])

            }
            debug('memo', memo)
            return memo
          }.bind(this), [])
      ),
      r.div({ className: 'tags-filters', style: this.style.filters },
        filters.map(function (filter, i) {
          var order = filters.length -1 - i
          var style = {order: order }
          return r.div({ className: 'filter-tag', style: this.style.filterTag }, [
            filter
          ])
        }.bind(this))
      ),
      r.div({ className: 'tag-input', style: this.style.tagInputContainer }, [
        r(TagSelect, {
          style: this.props.style,
          tagData: this.props.tagData,
          updateTag: this.props.updateTag
        })
      ])
    ])
  },
})
<|endoftext|>


<commit_before>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var gulp = require('gulp');
var path = require('path');
var srcGlobs = require('../config').srcGlobs;
var util = require('gulp-util');

// Directories to check for presubmit checks.
var srcGlobs = srcGlobs.concat([
  '!build-system/tasks/presubmit-checks.js',
  '!build/polyfills.js',
  '!gulpfile.js',
]);

var dedicatedCopyrightNoteSources = /(\.js|\.css)$/;

var es6polyfill = 'Not available because we do not currently' +
    ' ship with a needed ES6 polyfill.';

// Terms that must not appear in our source files.
var forbiddenTerms = {
  'DO NOT SUBMIT': '',
  'describe\\.only': '',
  'it\\.only': '',
  'console\\.\\w+\\(': 'If you run against this, use console/*OK*/.log to ' +
      'whitelist a legit case.',
  'cookie\\W': '',
  'eval\\(': '',
  'localStorage': '',
  'sessionStorage': '',
  'indexedDB': '',
  'openDatabase': '',
  'requestFileSystem': '',
  'webkitRequestFileSystem': '',
  'debugger': '',

  // ES6. These are only the most commonly used.
  'Array\\.from': es6polyfill,
  'Array\\.of': es6polyfill,
  // These currently depend on core-js/modules/web.dom.iterable which
  // we don't want. That decision could be reconsidered.
  'Promise\\.all': es6polyfill,
  'Promise\\.race': es6polyfill,
  '\\.startsWith': es6polyfill,
  '\\.endsWith': es6polyfill,
};

var bannedTermsHelpString = 'Please review viewport.js for a helper method ' +
    'or mark with `/*OK*/` or `/*REVIEW*/` and consult the AMP team. ' +
    'Most of the forbidden property/method access banned on the ' +
    '`forbiddenTermsSrcInclusive` object can be found in ' +
    '[What forces layout / reflow gist by Paul Irish]' +
    '(https://gist.github.com/paulirish/5d52fb081b3570c81e3a). ' +
    'These properties/methods when read/used require the browser ' +
    'to have the up-to-date value to return which might possibly be an ' +
    'expensive computation and could also be triggered multiple times ' +
    'if we are not careful. Please mark the call with ' +
    '`object./*OK*/property` if you explicitly need to read or update the ' +
    'forbidden property/method or mark it with `object./*REVIEW*/property` ' +
    'if you are unsure and so that it stands out in code reviews.';

var forbiddenTermsSrcInclusive = {
  '\\.innerHTML(?!_)': bannedTermsHelpString,
  '\\.outerHTML(?!_)': bannedTermsHelpString,
  '\\.postMessage(?!_)': bannedTermsHelpString,
  '\\.offsetLeft(?!_)': bannedTermsHelpString,
  '\\.offsetTop(?!_)': bannedTermsHelpString,
  '\\.offsetWidth(?!_)': bannedTermsHelpString,
  '\\.offsetHeight(?!_)': bannedTermsHelpString,
  '\\.offsetParent(?!_)': bannedTermsHelpString,
  '\\.clientLeft(?!_)(?!_)': bannedTermsHelpString,
  '\\.clientTop(?!_)': bannedTermsHelpString,
  '\\.clientWidth(?!_)': bannedTermsHelpString,
  '\\.clientHeight(?!_)': bannedTermsHelpString,
  '\\.getClientRects(?!_)': bannedTermsHelpString,
  '\\.getBoundingClientRect(?!_)': bannedTermsHelpString,
  '\\.scrollBy(?!_)': bannedTermsHelpString,
  '\\.scrollTo(?!_|p|p_)': bannedTermsHelpString,
  '\\.scrollIntoView(?!_)': bannedTermsHelpString,
  '\\.scrollIntoViewIfNeeded(?!_)': bannedTermsHelpString,
  '\\.scrollWidth(?!_)': 'please use `getScrollWidth()` from viewport',
  '\\.scrollHeight(?!_)': bannedTermsHelpString,
  '\\.scrollTop(?!_)': bannedTermsHelpString,
  '\\.scrollLeft(?!_)': bannedTermsHelpString,
  '\\.focus(?!_)': bannedTermsHelpString,
  '\\.computedRole(?!_)': bannedTermsHelpString,
  '\\.computedName(?!_)': bannedTermsHelpString,
  '\\.innerText(?!_)': bannedTermsHelpString,
  '\\.getComputedStyle(?!_)': bannedTermsHelpString,
  '\\.scrollX(?!_)': bannedTermsHelpString,
  '\\.scrollY(?!_)': bannedTermsHelpString,
  '\\.pageXOffset(?!_)': bannedTermsHelpString,
  '\\.pageYOffset(?!_)': bannedTermsHelpString,
  '\\.innerWidth(?!_)': bannedTermsHelpString,
  '\\.innerHeight(?!_)': bannedTermsHelpString,
  '\\.getMatchedCSSRules(?!_)': bannedTermsHelpString,
  '\\.scrollingElement(?!_)': bannedTermsHelpString,
  '\\.computeCTM(?!_)': bannedTermsHelpString,
  '\\.getBBox(?!_)': bannedTermsHelpString,
  '\\.webkitConvertPointFromNodeToPage(?!_)': bannedTermsHelpString,
  '\\.webkitConvertPointFromPageToNode(?!_)': bannedTermsHelpString,
};

// Terms that must appear in a source file.
var requiredTerms = {
  'Copyright 2015 The AMP HTML Authors\\.':
      dedicatedCopyrightNoteSources,
  'Licensed under the Apache License, Version 2\\.0':
      dedicatedCopyrightNoteSources,
  'http\\://www\\.apache\\.org/licenses/LICENSE-2\\.0':
      dedicatedCopyrightNoteSources,
};

/**
 * @param {!File} file file is a vinyl file object
 * @param {!Array<string, string>} terms
 * @return boolean
 */
function matchTerms(pathname, contents, terms) {
  return Object.keys(terms).map(function(term) {
    var fix;
    // we can't optimize building the `RegExp` objects early unless we build
    // another mapping of term -> regexp object to be able to get back to the
    // original term to get the possible fix value. This is ok as the
    // presubmit doesn't have to be blazing fast and this is most likely
    // negligible.
    var matches = contents.match(new RegExp(term));

    if (matches) {
      util.log(util.colors.red('Found forbidden: "' + matches[0] +
          '" in ' + pathname));
      fix = terms[term];

      if (fix) {
        util.log(util.colors.blue(fix));
      }
      util.log(util.colors.blue('=========='));
      return true;
    }
    return false;
  }).some(function(hasAnyTerm) {
    return hasAnyTerm;
  });
}

function hasAnyTerms(file) {
  var contents = file.contents.toString();
  var pathname = file.path;
  var basename = path.basename(pathname);
  var hasTerms = false;
  var hasSrcInclusiveTerms = false;

  hasTerms = matchTerms(pathname, contents, forbiddenTerms);

  var isTestFile = /^test-/.test(basename) || /^_init_tests/.test(basename);
  if (!isTestFile) {
    hasSrcInclusiveTerms = matchTerms(pathname, contents,
        forbiddenTermsSrcInclusive);
  }

  return hasTerms || hasSrcInclusiveTerms;
}

function hasAllTerms(file) {
  var contents = file.contents.toString();
  return Object.keys(requiredTerms).map(function(term) {
    var filter = requiredTerms[term];
    if (!filter.test(file.path)) {
      return false;
    }

    var matches = contents.match(new RegExp(term));
    if (!matches) {
      util.log(util.colors.red('Did not find required: "' + term +
          '" in ' + file.path));
      util.log(util.colors.blue('=========='));
      return true;
    }
    return false;
  }).some(function(hasAnyTerm) {
    return hasAnyTerm;
  });
}

function checkForbiddenAndRequiredTerms() {
  var forbiddenFound = false;
  var missingRequirements = false;
  return gulp.src(srcGlobs)
    .pipe(util.buffer(function(err, files) {
      forbiddenFound = files.map(hasAnyTerms).some(function(errorFound) {
        return errorFound;
      });
      missingRequirements = files.map(hasAllTerms).some(function(errorFound) {
        return errorFound;
      });
    }))
    .on('end', function() {
      if (forbiddenFound) {
        util.log(util.colors.blue(
            'Please remove these usages or consult with the AMP team.'));
      }
      if (missingRequirements) {
        util.log(util.colors.blue(
            'Adding these terms (e.g. by adding a required LICENSE ' +
            'to the file)'));
      }
      if (forbiddenFound || missingRequirements) {
        process.exit(1);
      }
    });
}

gulp.task('presubmit', checkForbiddenAndRequiredTerms);
<commit_msg>Adds some comments and refactors some of the presubmit-check for clarity<commit_after>/**
 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var gulp = require('gulp');
var path = require('path');
var srcGlobs = require('../config').srcGlobs;
var util = require('gulp-util');

// Directories to check for presubmit checks.
var srcGlobs = srcGlobs.concat([
  '!build-system/tasks/presubmit-checks.js',
  '!build/polyfills.js',
  '!gulpfile.js',
]);

var dedicatedCopyrightNoteSources = /(\.js|\.css)$/;

var es6polyfill = 'Not available because we do not currently' +
    ' ship with a needed ES6 polyfill.';

// Terms that must not appear in our source files.
var forbiddenTerms = {
  'DO NOT SUBMIT': '',
  'describe\\.only': '',
  'it\\.only': '',
  'console\\.\\w+\\(': 'If you run against this, use console/*OK*/.log to ' +
      'whitelist a legit case.',
  'cookie\\W': '',
  'eval\\(': '',
  'localStorage': '',
  'sessionStorage': '',
  'indexedDB': '',
  'openDatabase': '',
  'requestFileSystem': '',
  'webkitRequestFileSystem': '',
  'debugger': '',

  // ES6. These are only the most commonly used.
  'Array\\.from': es6polyfill,
  'Array\\.of': es6polyfill,
  // These currently depend on core-js/modules/web.dom.iterable which
  // we don't want. That decision could be reconsidered.
  'Promise\\.all': es6polyfill,
  'Promise\\.race': es6polyfill,
  '\\.startsWith': es6polyfill,
  '\\.endsWith': es6polyfill,
};

var bannedTermsHelpString = 'Please review viewport.js for a helper method ' +
    'or mark with `/*OK*/` or `/*REVIEW*/` and consult the AMP team. ' +
    'Most of the forbidden property/method access banned on the ' +
    '`forbiddenTermsSrcInclusive` object can be found in ' +
    '[What forces layout / reflow gist by Paul Irish]' +
    '(https://gist.github.com/paulirish/5d52fb081b3570c81e3a). ' +
    'These properties/methods when read/used require the browser ' +
    'to have the up-to-date value to return which might possibly be an ' +
    'expensive computation and could also be triggered multiple times ' +
    'if we are not careful. Please mark the call with ' +
    '`object./*OK*/property` if you explicitly need to read or update the ' +
    'forbidden property/method or mark it with `object./*REVIEW*/property` ' +
    'if you are unsure and so that it stands out in code reviews.';

var forbiddenTermsSrcInclusive = {
  '\\.innerHTML(?!_)': bannedTermsHelpString,
  '\\.outerHTML(?!_)': bannedTermsHelpString,
  '\\.postMessage(?!_)': bannedTermsHelpString,
  '\\.offsetLeft(?!_)': bannedTermsHelpString,
  '\\.offsetTop(?!_)': bannedTermsHelpString,
  '\\.offsetWidth(?!_)': bannedTermsHelpString,
  '\\.offsetHeight(?!_)': bannedTermsHelpString,
  '\\.offsetParent(?!_)': bannedTermsHelpString,
  '\\.clientLeft(?!_)(?!_)': bannedTermsHelpString,
  '\\.clientTop(?!_)': bannedTermsHelpString,
  '\\.clientWidth(?!_)': bannedTermsHelpString,
  '\\.clientHeight(?!_)': bannedTermsHelpString,
  '\\.getClientRects(?!_)': bannedTermsHelpString,
  '\\.getBoundingClientRect(?!_)': bannedTermsHelpString,
  '\\.scrollBy(?!_)': bannedTermsHelpString,
  '\\.scrollTo(?!_|p|p_)': bannedTermsHelpString,
  '\\.scrollIntoView(?!_)': bannedTermsHelpString,
  '\\.scrollIntoViewIfNeeded(?!_)': bannedTermsHelpString,
  '\\.scrollWidth(?!_)': 'please use `getScrollWidth()` from viewport',
  '\\.scrollHeight(?!_)': bannedTermsHelpString,
  '\\.scrollTop(?!_)': bannedTermsHelpString,
  '\\.scrollLeft(?!_)': bannedTermsHelpString,
  '\\.focus(?!_)': bannedTermsHelpString,
  '\\.computedRole(?!_)': bannedTermsHelpString,
  '\\.computedName(?!_)': bannedTermsHelpString,
  '\\.innerText(?!_)': bannedTermsHelpString,
  '\\.getComputedStyle(?!_)': bannedTermsHelpString,
  '\\.scrollX(?!_)': bannedTermsHelpString,
  '\\.scrollY(?!_)': bannedTermsHelpString,
  '\\.pageXOffset(?!_)': bannedTermsHelpString,
  '\\.pageYOffset(?!_)': bannedTermsHelpString,
  '\\.innerWidth(?!_)': bannedTermsHelpString,
  '\\.innerHeight(?!_)': bannedTermsHelpString,
  '\\.getMatchedCSSRules(?!_)': bannedTermsHelpString,
  '\\.scrollingElement(?!_)': bannedTermsHelpString,
  '\\.computeCTM(?!_)': bannedTermsHelpString,
  '\\.getBBox(?!_)': bannedTermsHelpString,
  '\\.webkitConvertPointFromNodeToPage(?!_)': bannedTermsHelpString,
  '\\.webkitConvertPointFromPageToNode(?!_)': bannedTermsHelpString,
};

// Terms that must appear in a source file.
var requiredTerms = {
  'Copyright 2015 The AMP HTML Authors\\.':
      dedicatedCopyrightNoteSources,
  'Licensed under the Apache License, Version 2\\.0':
      dedicatedCopyrightNoteSources,
  'http\\://www\\.apache\\.org/licenses/LICENSE-2\\.0':
      dedicatedCopyrightNoteSources,
};

/**
 * Logs any issues found in the contents of file based on terms (regex patterns),
 * and provides any possible fix information for matched terms if possible
 *
 * @param {!File} file file is a vinyl file object to match terms on content with
 * @param {!Array<string, string>} terms Pairs of regex patterns and possible fix messages.
 * @return boolean true if any of the terms match the file content, false otherwise
 */
function matchTerms(file, terms) {
  var pathname = file.path;
  var contents = file.contents.toString();
  return Object.keys(terms).map(function(term) {
    var fix;
    // we can't optimize building the `RegExp` objects early unless we build
    // another mapping of term -> regexp object to be able to get back to the
    // original term to get the possible fix value. This is ok as the
    // presubmit doesn't have to be blazing fast and this is most likely
    // negligible.
    var matches = contents.match(new RegExp(term));

    if (matches) {
      util.log(util.colors.red('Found forbidden: "' + matches[0] +
          '" in ' + pathname));
      fix = terms[term];

	  // log the possible fix information if provided for the term.
      if (fix) {
        util.log(util.colors.blue(fix));
      }
      util.log(util.colors.blue('=========='));
      return true;
    }
    return false;
  }).some(function(hasAnyTerm) {
    return hasAnyTerm;
  });
}

/**
 * Test if a file's contents match any of the
 * forbidden terms
 *
 * @param {!File} file file is a vinyl file object
 * @return boolean true if any of the terms match the file content, false otherwise
 */
function hasAnyTerms(file) {
  var pathname = file.path;
  var basename = path.basename(pathname);
  var hasTerms = false;
  var hasSrcInclusiveTerms = false;

  hasTerms = matchTerms(file, forbiddenTerms);

  var isTestFile = /^test-/.test(basename) || /^_init_tests/.test(basename);
  if (!isTestFile) {
    hasSrcInclusiveTerms = matchTerms(file, forbiddenTermsSrcInclusive);
  }

  return hasTerms || hasSrcInclusiveTerms;
}

/**
 * Test if a file's contents fail to match any of the required terms and log any missing terms
 *
 * @param {!File} file file is a vinyl file object
 * @return boolean true if any of the terms are not matched in the file content, false otherwise
 */
function isMissingTerms(file) {
  var contents = file.contents.toString();
  return Object.keys(requiredTerms).map(function(term) {
    var filter = requiredTerms[term];
    if (!filter.test(file.path)) {
      return false;
    }

    var matches = contents.match(new RegExp(term));
    if (!matches) {
      util.log(util.colors.red('Did not find required: "' + term +
          '" in ' + file.path));
      util.log(util.colors.blue('=========='));
      return true;
    }
    return false;
  }).some(function(hasMissingTerm) {
    return hasMissingTerm;
  });
}

/**
 * Check a file for all the required terms and
 * any forbidden terms and log any errors found.
 */
function checkForbiddenAndRequiredTerms() {
  var forbiddenFound = false;
  var missingRequirements = false;
  return gulp.src(srcGlobs)
    .pipe(util.buffer(function(err, files) {
      forbiddenFound = files.map(hasAnyTerms).some(function(errorFound) {
        return errorFound;
      });
      missingRequirements = files.map(isMissingTerms).some(function(errorFound) {
        return errorFound;
      });
    }))
    .on('end', function() {
      if (forbiddenFound) {
        util.log(util.colors.blue(
            'Please remove these usages or consult with the AMP team.'));
      }
      if (missingRequirements) {
        util.log(util.colors.blue(
            'Adding these terms (e.g. by adding a required LICENSE ' +
            'to the file)'));
      }
      if (forbiddenFound || missingRequirements) {
        process.exit(1);
      }
    });
}

gulp.task('presubmit', checkForbiddenAndRequiredTerms);
<|endoftext|>


<commit_before>'use strict';

const {ipcRenderer} = require('electron');
const ipcPlus = require('electron-ipc-plus');
const panel = require('./panel');
const PanelFrame = require('./panel-frame');

module.exports = panel;

// ========================================
// dom events
// ========================================

window.addEventListener('beforeunload', event => {
  let frameELs = panel.panels;
  let stopUnload = false;

  for ( let i = 0; i < frameELs.length; ++i ) {
    let el = frameELs[i];
    if ( el.beforeunload ) {
      stopUnload = el.beforeunload();
    }

    if ( stopUnload ) {
      event.returnValue = true;
      break;
    }
  }

  ipcRenderer.send('electron-panel:clear');
});

// ==========================
// custom elements
// ==========================

document.addEventListener('readystatechange', () => {
  if ( document.readyState === 'interactive' ) {
    window.customElements.define('ui-panel-frame', PanelFrame);
  }
});

// ========================================
// ipc messages
// ========================================

ipcRenderer.on(`${ipcPlus.id}:main2panel`, (event, panelID, message, ...args) => {
  // process waitForReply option
  let opts = ipcPlus.internal._popOptions(args);
  if ( opts && opts.waitForReply ) {
    // NOTE: do not directly use event, message in event.reply, it will cause Electron devtools crash
    let sender = event.sender;
    let msg = message;
    event.reply = function (...replyArgs) {
      if ( ipcPlus.internal._wrapError(replyArgs) === false ) {
        console.warn(`Invalid argument for event.reply of "${msg}": the first argument must be an instance of Error or null`);
        // return; // TEMP DISABLE
      }

      let replyOpts = ipcPlus.option({
        sessionId: opts.sessionId
      });
      replyArgs = [`${ipcPlus.id}:reply`, ...replyArgs, replyOpts];
      return sender.send.apply( sender, replyArgs );
    };
  }

  // refine the args
  args = [panelID, message, event, ...args];
  panel._dispatch.apply( panel, args );
});
<commit_msg>change beforeunload => beforeUnload<commit_after>'use strict';

const {ipcRenderer} = require('electron');
const ipcPlus = require('electron-ipc-plus');
const panel = require('./panel');
const PanelFrame = require('./panel-frame');

module.exports = panel;

// ========================================
// dom events
// ========================================

window.addEventListener('beforeunload', event => {
  let frameELs = panel.panels;
  let stopUnload = false;

  for ( let i = 0; i < frameELs.length; ++i ) {
    let el = frameELs[i];
    if ( el.beforeUnload ) {
      stopUnload = el.beforeUnload();
    }

    if ( stopUnload ) {
      event.returnValue = true;
      break;
    }
  }

  ipcRenderer.send('electron-panel:clear');
});

// ==========================
// custom elements
// ==========================

document.addEventListener('readystatechange', () => {
  if ( document.readyState === 'interactive' ) {
    window.customElements.define('ui-panel-frame', PanelFrame);
  }
});

// ========================================
// ipc messages
// ========================================

ipcRenderer.on(`${ipcPlus.id}:main2panel`, (event, panelID, message, ...args) => {
  // process waitForReply option
  let opts = ipcPlus.internal._popOptions(args);
  if ( opts && opts.waitForReply ) {
    // NOTE: do not directly use event, message in event.reply, it will cause Electron devtools crash
    let sender = event.sender;
    let msg = message;
    event.reply = function (...replyArgs) {
      if ( ipcPlus.internal._wrapError(replyArgs) === false ) {
        console.warn(`Invalid argument for event.reply of "${msg}": the first argument must be an instance of Error or null`);
        // return; // TEMP DISABLE
      }

      let replyOpts = ipcPlus.option({
        sessionId: opts.sessionId
      });
      replyArgs = [`${ipcPlus.id}:reply`, ...replyArgs, replyOpts];
      return sender.send.apply( sender, replyArgs );
    };
  }

  // refine the args
  args = [panelID, message, event, ...args];
  panel._dispatch.apply( panel, args );
});
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Per Eckerdal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var _ = require('lodash');

/**
 * Timer is a reporter for internal use by the suite runner. It forwards all
 * messages to another reporter, but also adds "time", "slow" and "halfSlow"
 * fields to finish messages.
 *
 * This is used in the suite runner as a way to move logic out of that file.
 */
function Timer(reporter, defaultSlowThreshold, clock) {
  if (typeof defaultSlowThreshold !== 'number') {
    throw new Error('Invalid or missing default slow threshold parameter');
  }

  this._reporter = reporter;
  this._defaultSlowThreshold = defaultSlowThreshold;
  this._slowThresholdOverrides = {};  // Hash from test path JSON to overriden slow threshold
  this._testStartTimes = {};  // Hash from test path JSON to test start times (as given by the clock)
  this._testDoneTimes = {};  // Hash from test path JSON to test done times (as given by the clock)
  this._clock = clock || function() { return (new Date()).getTime(); };
}

Timer.prototype._getSlowThresholdForTest = function(key) {
  return key in this._slowThresholdOverrides ?
    this._slowThresholdOverrides[key] :
    this._defaultSlowThreshold;
};

Timer.prototype._setSlowThresholdForTest = function(key, value) {
  this._slowThresholdOverrides[key] = value;
};

Timer.prototype._forwardCall = function(message, args) {
  if (this._reporter[message]) {
    this._reporter[message].apply(this._reporter, args);
  }
};

['registrationFailed', 'registerTests', 'done'].forEach(function(message) {
  Timer.prototype[message] = function() {
    this._forwardCall(message, arguments);
  };
});

Timer.prototype.gotMessage = function(testPath, message) {
  var key = JSON.stringify(testPath);
  if (message.type === 'startedTest') {
    this._testStartTimes[key] = this._clock();
  } else if (message.type === 'startedAfterHooks') {
    this._testDoneTimes[key] = this._clock();
  } else if (message.type === 'setSlowThreshold') {
    this._setSlowThresholdForTest(key, message.value);
  } else if (message.type === 'finish') {
    if (key in this._testDoneTimes && key in this._testStartTimes) {
      var time = this._testDoneTimes[key] - this._testStartTimes[key];
      message.time = time;

      var slowThreshold = this._getSlowThresholdForTest(key);
      message.halfSlow = time >= slowThreshold / 2;
      message.slow = time >= slowThreshold;
    }
  }

  this._forwardCall('gotMessage', [testPath, message]);
};

module.exports = Timer;
<commit_msg>Fix linter issue<commit_after>/*
 * Copyright 2014 Per Eckerdal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

/**
 * Timer is a reporter for internal use by the suite runner. It forwards all
 * messages to another reporter, but also adds "time", "slow" and "halfSlow"
 * fields to finish messages.
 *
 * This is used in the suite runner as a way to move logic out of that file.
 */
function Timer(reporter, defaultSlowThreshold, clock) {
  if (typeof defaultSlowThreshold !== 'number') {
    throw new Error('Invalid or missing default slow threshold parameter');
  }

  this._reporter = reporter;
  this._defaultSlowThreshold = defaultSlowThreshold;
  this._slowThresholdOverrides = {};  // Hash from test path JSON to overriden slow threshold
  this._testStartTimes = {};  // Hash from test path JSON to test start times (as given by the clock)
  this._testDoneTimes = {};  // Hash from test path JSON to test done times (as given by the clock)
  this._clock = clock || function() { return (new Date()).getTime(); };
}

Timer.prototype._getSlowThresholdForTest = function(key) {
  return key in this._slowThresholdOverrides ?
    this._slowThresholdOverrides[key] :
    this._defaultSlowThreshold;
};

Timer.prototype._setSlowThresholdForTest = function(key, value) {
  this._slowThresholdOverrides[key] = value;
};

Timer.prototype._forwardCall = function(message, args) {
  if (this._reporter[message]) {
    this._reporter[message].apply(this._reporter, args);
  }
};

['registrationFailed', 'registerTests', 'done'].forEach(function(message) {
  Timer.prototype[message] = function() {
    this._forwardCall(message, arguments);
  };
});

Timer.prototype.gotMessage = function(testPath, message) {
  var key = JSON.stringify(testPath);
  if (message.type === 'startedTest') {
    this._testStartTimes[key] = this._clock();
  } else if (message.type === 'startedAfterHooks') {
    this._testDoneTimes[key] = this._clock();
  } else if (message.type === 'setSlowThreshold') {
    this._setSlowThresholdForTest(key, message.value);
  } else if (message.type === 'finish') {
    if (key in this._testDoneTimes && key in this._testStartTimes) {
      var time = this._testDoneTimes[key] - this._testStartTimes[key];
      message.time = time;

      var slowThreshold = this._getSlowThresholdForTest(key);
      message.halfSlow = time >= slowThreshold / 2;
      message.slow = time >= slowThreshold;
    }
  }

  this._forwardCall('gotMessage', [testPath, message]);
};

module.exports = Timer;
<|endoftext|>


<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * Model which fetches the information of license
 */
var License = Backbone.Model.extend({
	url: 'api/license',

	initialize: function() {
		_.bindAll(this, 'fetch_license');
	},

	fetch_license: function(path, callback) {
		this.fetch({
			success: function(res) {
				if (callback && typeof(callback) === 'function') {
					callback({status: 'success', data: res});
				}
			},
			error: function(err) {
				if (callback && typeof(callback) === 'function') {
					callback({status: 'error', data: err});
				}
			}
		});
	}
});

var LicenseUserModel = Backbone.Model.extend({
	url: 'api/license/users',

    initialize: function (args, options) {
        if (options && options.dialog) {
            this.dialog = options.dialog;
        }
    },

    parse: function (response) {
        if (this.dialog) {
            this.dialog.populate(response);
        }
        return response;
    }
});

var LicenseUsersCollection = Backbone.Collection.extend({
	url: 'api/license/users',
    model: LicenseUserModel
});<commit_msg>Update License.js<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * Model which fetches the information of license
 */
var License = Backbone.Model.extend({
	url: 'api/license',

	initialize: function() {
		_.bindAll(this, 'fetch_license');
	},

	fetch_license: function(path, callback) {
		this.fetch({
			success: function(res) {
				if (callback && typeof(callback) === 'function') {
					callback({status: 'success', data: res});
				}
			},
			error: function(err) {
				if (callback && typeof(callback) === 'function') {
					callback({status: 'error', data: err});
				}
			}
		});
	}
});

var LicenseUserModel = Backbone.Model.extend({
	url: 'api/license/users'
});

var LicenseUsersCollection = Backbone.Collection.extend({
	url: 'api/license/users',
    model: LicenseUserModel
});<|endoftext|>


<commit_before>/**
 * Turnpike porcelain.
 *
 * This constructor contains all of the invariants expected of a controller for a REST endpoint.
 *
 * Turnpike's "Endpoint" controller system assumes a few things about your app.
 * 1. You want to be REST based.
 * 2. All URLs are of the form [controller]/[itemId]
 * 3. EVERYTHING gets a REST endpoint. Even controllers only intended to provide small widgets
 *    that don't need to be stand-alone pages.
 *
 * @constructor
 */

var AutoLoader = require('./AutoLoader');
var hooks = require('hooks');

function EndpointController(connection) {
  var $this = this;
  this.connection = connection;
  this.view = this.name = this.constructor.name;
  this.data = {};
  this.title = '';
  this.mode = 'main';

  this.prepare = (function(readyCallback) {
    switch (connection.method) {
      case "GET":
        this._GET(readyCallback);
        break;
      case "HEAD":
        this._HEAD(readyCallback);
        break;
      case "PUT":
        this._PUT(readyCallback);
        break;
      case "POST":
        this._POST(readyCallback);
        break;
      case "DELETE":
        this._DELETE(readyCallback);
        break;
    }
  }).bind(this);

  this.deliver = (function() {
    var type
      , types = [];
    for (type in $this.deliver){
      types.push(type);
    }
    console.log($this.name + ' can send types:\n' + types);

    type = connection.bestMimeType(types);

    if (type !== undefined) {
      $this.deliver[type]();
    }
    else {
      $this.connection.die(406, "No acceptable return format available.");
    }
  }).bind(this);

  this.deliver['text/html'] = function() {
    var Page = AutoLoader.invoke('Page');
    var View = AutoLoader.invokeView($this.view);

    View = new View();
    View.mode($this.mode).data($this.data).render(function(html){
      $this.connection.header({'content-type' : 'text/html'}).
        response(html);
      Page = new Page(connection);
      Page.prepare(Page.deliver);
    });
  };

  this.deliver['application/json'] = function() {
    var View = AutoLoader.invokeView('JSONout');
    View = new View();
    View.mode('main').data($this.data).render(function(output){
      $this.connection.header({'content-type' : 'application/json'}).
        response(output).send();
    });
  };

  for (var k in hooks) {
    this[k] = hooks[k];
  }

}

EndpointController.prototype._GET    = function(readyCallback){this.connection.die(405, "Method GET not available for this endpoint")};
EndpointController.prototype._HEAD   = function(readyCallback){this.connection.die(405, "Method HEAD not available for this endpoint")};
EndpointController.prototype._PUT    = function(readyCallback){this.connection.die(405, "Method PUT not available for this endpoint")};
EndpointController.prototype._POST   = function(readyCallback){this.connection.die(405, "Method POST not available for this endpoint")};
EndpointController.prototype._DELETE = function(readyCallback){this.connection.die(405, "Method DELETE not available for this endpoint")};

module.exports = EndpointController;
<commit_msg>Remove some no-longer needed $this's<commit_after>/**
 * Turnpike porcelain.
 *
 * This constructor contains all of the invariants expected of a controller for a REST endpoint.
 *
 * Turnpike's "Endpoint" controller system assumes a few things about your app.
 * 1. You want to be REST based.
 * 2. All URLs are of the form [controller]/[itemId]
 * 3. EVERYTHING gets a REST endpoint. Even controllers only intended to provide small widgets
 *    that don't need to be stand-alone pages.
 *
 * @constructor
 */

var AutoLoader = require('./AutoLoader');
var hooks = require('hooks');

function EndpointController(connection) {
  var $this = this;
  this.connection = connection;
  this.view = this.name = this.constructor.name;
  this.data = {};
  this.title = '';
  this.mode = 'main';

  this.prepare = (function(readyCallback) {
    switch (connection.method) {
      case "GET":
        this._GET(readyCallback);
        break;
      case "HEAD":
        this._HEAD(readyCallback);
        break;
      case "PUT":
        this._PUT(readyCallback);
        break;
      case "POST":
        this._POST(readyCallback);
        break;
      case "DELETE":
        this._DELETE(readyCallback);
        break;
    }
  }).bind(this);

  this.deliver = (function() {
    var type
      , types = [];
    for (type in $this.deliver){
      types.push(type);
    }
    console.log($this.name + ' can send types:\n' + types);

    type = connection.bestMimeType(types);

    if (type !== undefined) {
      $this.deliver[type]();
    }
    else {
      $this.connection.die(406, "No acceptable return format available.");
    }
  }).bind(this);

  this.deliver['text/html'] = (function() {
    var Page = AutoLoader.invoke('Page');
    var View = AutoLoader.invokeView(this.view);

    View = new View();
    View.mode(this.mode).data(this.data).render((function(html){
        this.connection.header({'content-type' : 'text/html'}).
          response(html);
        Page = new Page(connection);
        Page.prepare(Page.deliver);
      }).bind(this));
  }).bind(this);

  this.deliver['application/json'] = (function() {
    var View = AutoLoader.invokeView('JSONout');
    View = new View();
    View.mode('main').data(this.data).render((function(output){
      this.connection.header({'content-type' : 'application/json'}).
        response(output).send();
    }).bind(this));
  }).bind(this);

  for (var k in hooks) {
    this[k] = hooks[k];
  }

}

EndpointController.prototype._GET    = function(readyCallback){this.connection.die(405, "Method GET not available for this endpoint")};
EndpointController.prototype._HEAD   = function(readyCallback){this.connection.die(405, "Method HEAD not available for this endpoint")};
EndpointController.prototype._PUT    = function(readyCallback){this.connection.die(405, "Method PUT not available for this endpoint")};
EndpointController.prototype._POST   = function(readyCallback){this.connection.die(405, "Method POST not available for this endpoint")};
EndpointController.prototype._DELETE = function(readyCallback){this.connection.die(405, "Method DELETE not available for this endpoint")};

module.exports = EndpointController;
<|endoftext|>


<commit_before>'use strict';

var d        = require('es5-ext/lib/Object/descriptor')
  , extendEl = require('dom-ext/lib/Element/prototype/extend')

  , prototype;

prototype = Object.create(Function.prototype, {
	remove: d(function () {
		var parent = this.el.parentNode;
		if (!parent) return;
		parent.removeChild(this.el);
	}),
	takeOut: d(function () {
		if (this._isTakenOut || !this.el.parentNode) return;
		if (!this._domLocation) {
			this._domLocation =
				this.el.parentNode.insertBefore(
					this.el.ownerDocument.createTextNode(''),
					this.el.nextSibling
				);
		}
		this.remove();
		this._isTakenOut = true;
	}),
	takeIn: d(function () {
		if (!this._isTakenOut) return;
		this._domLocation.parentNode.insertBefore(this.el, this._domLocation);
		delete this._isTakenOut;
	}),
	toDOM: d(function () {
		return this.el;
	})
});

module.exports = exports = function (domjs, name, proto) {
	var document = domjs.document;
	return function () {
		var el, self;
		el = domjs.current.appendChild(extendEl.apply(document.createElement(name),
			 arguments));
		self = function () {
			extendEl.apply(el, arguments);
			return self;
		};
		self.__proto__ = proto || prototype;
		self.el = el;
		return self;
	};
};
exports.proto = prototype;
<commit_msg>Seclude domify function<commit_after>'use strict';

var d        = require('es5-ext/lib/Object/descriptor')
  , extendEl = require('dom-ext/lib/Element/prototype/extend')

  , defineProperty = Object.defineProperty

  , prototype, domify;

domify = function (el, proto) {
	var self = function () {
		extendEl.apply(el, arguments);
		return self;
	};
	self.__proto__ = proto || prototype;
	self.el = defineProperty(el, '_domjs', d(self));
	return self;
};

prototype = Object.create(Function.prototype, {
	remove: d(function () {
		var parent = this.el.parentNode;
		if (!parent) return;
		parent.removeChild(this.el);
	}),
	takeOut: d(function () {
		if (this._isTakenOut || !this.el.parentNode) return;
		if (!this._domLocation) {
			this._domLocation =
				this.el.parentNode.insertBefore(
					this.el.ownerDocument.createTextNode(''),
					this.el.nextSibling
				);
		}
		this.remove();
		this._isTakenOut = true;
	}),
	takeIn: d(function () {
		if (!this._isTakenOut) return;
		this._domLocation.parentNode.insertBefore(this.el, this._domLocation);
		delete this._isTakenOut;
	}),
	toDOM: d(function () {
		return this.el;
	})
});

module.exports = exports = function (domjs, name, proto) {
	var document = domjs.document;
	return function () {
		return domify(domjs.current.appendChild(extendEl.apply(document
			.createElement(name), arguments)), proto);
	};
};
exports.proto = prototype;
<|endoftext|>


<commit_before>var chessground = require('chessground');
var partial = chessground.util.partial;
var editor = require('./editor');
var m = require('mithril');
var keyboard = require('./keyboard');

module.exports = function(cfg) {

  this.data = editor.init(cfg);
  this.options = cfg.options || {};
  this.embed = cfg.embed;

  this.trans = partial(editor.trans, this.data.i18n);

  this.vm = {
    redirecting: false,
    selected: m.prop(null)
  };

  this.extraPositions = [{
    fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -',
    name: this.trans('startPosition')
  }, {
    fen: '8/8/8/8/8/8/8/8 w - -',
    name: this.trans('clearBoard')
  }, {
    fen: 'prompt',
    name: this.trans('loadPosition')
  }];

  this.positionIndex = {};
  cfg.positions && cfg.positions.forEach(function(p, i) {
    this.positionIndex[p.fen.split(' ')[0]] = i;
  }.bind(this));

  this.chessground = new chessground.controller({
    fen: cfg.fen,
    orientation: this.options.orientation || 'white',
    coordinates: !this.embed,
    movable: {
      free: true,
      color: 'both',
      dropOff: 'trash'
    },
    animation: {
      duration: cfg.animation.duration
    },
    premovable: {
      enabled: false
    },
    drawable: {
      enabled: true
    },
    draggable: {
      showGhost: false,
      distance: 0,
      autoDistance: false
    },
    selectable: {
      enabled: false
    },
    events: {
      change: function() {
        onChange();
        m.redraw();
      }.bind(this)
    },
    disableContextMenu: true
  });

  var onChange = function() {
    this.options.onChange && this.options.onChange(this.computeFen());
  }.bind(this);

  this.computeFen = partial(editor.computeFen, this.data, this.chessground.getFen);

  this.setColor = function(letter) {
    this.data.color(letter);
    onChange();
  }.bind(this);

  this.setCastle = function(id, value) {
    this.data.castles[id](value);
    onChange();
  }.bind(this);

  this.startPosition = function() {
    this.chessground.set({
      fen: 'start'
    });
    this.data.castles = editor.castlesAt(true);
    this.data.color('w');
    onChange();
  }.bind(this);

  this.clearBoard = function() {
    this.chessground.set({
      fen: '8/8/8/8/8/8/8/8'
    });
    this.data.castles = editor.castlesAt(false);
    onChange();
  }.bind(this);

  this.loadNewFen = function(fen) {
    if (fen === 'prompt') {
      fen = prompt('Paste FEN position').trim();
      if (!fen) return;
    }
    this.changeFen(fen);
  }.bind(this);

  this.changeFen = function(fen) {
    this.vm.redirecting = true;
    window.location = editor.makeUrl(this.data.baseUrl, fen);
  }.bind(this);

  this.positionLooksLegit = function() {
    var kings = {
      white: 0,
      black: 0
    };
    var pieces = this.chessground.data.pieces;
    for (var pos in pieces) {
      if (pieces[pos] && pieces[pos].role === 'king') kings[pieces[pos].color]++;
    }
    return kings.white === 1 && kings.black === 1;
  }.bind(this);

  this.setOrientation = function(o) {
    this.options.orientation = o;
    if (this.chessground.getOrientation() !== o)
      this.chessground.toggleOrientation();
    m.redraw();
  }.bind(this);

  keyboard(this);
};
<commit_msg>Automatically select the pointer.<commit_after>var chessground = require('chessground');
var partial = chessground.util.partial;
var editor = require('./editor');
var m = require('mithril');
var keyboard = require('./keyboard');

module.exports = function(cfg) {

  this.data = editor.init(cfg);
  this.options = cfg.options || {};
  this.embed = cfg.embed;

  this.trans = partial(editor.trans, this.data.i18n);

  this.vm = {
    redirecting: false,
    selected: m.prop('pointer')
  };

  this.extraPositions = [{
    fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -',
    name: this.trans('startPosition')
  }, {
    fen: '8/8/8/8/8/8/8/8 w - -',
    name: this.trans('clearBoard')
  }, {
    fen: 'prompt',
    name: this.trans('loadPosition')
  }];

  this.positionIndex = {};
  cfg.positions && cfg.positions.forEach(function(p, i) {
    this.positionIndex[p.fen.split(' ')[0]] = i;
  }.bind(this));

  this.chessground = new chessground.controller({
    fen: cfg.fen,
    orientation: this.options.orientation || 'white',
    coordinates: !this.embed,
    movable: {
      free: true,
      color: 'both',
      dropOff: 'trash'
    },
    animation: {
      duration: cfg.animation.duration
    },
    premovable: {
      enabled: false
    },
    drawable: {
      enabled: true
    },
    draggable: {
      showGhost: false,
      distance: 0,
      autoDistance: false
    },
    selectable: {
      enabled: false
    },
    events: {
      change: function() {
        onChange();
        m.redraw();
      }.bind(this)
    },
    disableContextMenu: true
  });

  var onChange = function() {
    this.options.onChange && this.options.onChange(this.computeFen());
  }.bind(this);

  this.computeFen = partial(editor.computeFen, this.data, this.chessground.getFen);

  this.setColor = function(letter) {
    this.data.color(letter);
    onChange();
  }.bind(this);

  this.setCastle = function(id, value) {
    this.data.castles[id](value);
    onChange();
  }.bind(this);

  this.startPosition = function() {
    this.chessground.set({
      fen: 'start'
    });
    this.data.castles = editor.castlesAt(true);
    this.data.color('w');
    onChange();
  }.bind(this);

  this.clearBoard = function() {
    this.chessground.set({
      fen: '8/8/8/8/8/8/8/8'
    });
    this.data.castles = editor.castlesAt(false);
    onChange();
  }.bind(this);

  this.loadNewFen = function(fen) {
    if (fen === 'prompt') {
      fen = prompt('Paste FEN position').trim();
      if (!fen) return;
    }
    this.changeFen(fen);
  }.bind(this);

  this.changeFen = function(fen) {
    this.vm.redirecting = true;
    window.location = editor.makeUrl(this.data.baseUrl, fen);
  }.bind(this);

  this.positionLooksLegit = function() {
    var kings = {
      white: 0,
      black: 0
    };
    var pieces = this.chessground.data.pieces;
    for (var pos in pieces) {
      if (pieces[pos] && pieces[pos].role === 'king') kings[pieces[pos].color]++;
    }
    return kings.white === 1 && kings.black === 1;
  }.bind(this);

  this.setOrientation = function(o) {
    this.options.orientation = o;
    if (this.chessground.getOrientation() !== o)
      this.chessground.toggleOrientation();
    m.redraw();
  }.bind(this);

  keyboard(this);
};
<|endoftext|>