Source: SyntaxAnalizer.js

/*
	* @package jscodesniffer
	* @author sheiko
	* @license MIT
	* @copyright (c) Dmitry Sheiko http://www.dsheiko.com
	* jscs standard:Jquery
	* jshint unused:false
	* Code style: http://docs.jquery.com/JQuery_Core_Style_Guidelines
	*/
// UMD boilerplate according to https://github.com/umdjs/umd
if ( typeof module === "object" && typeof define !== "function" ) {
	/**
	* Override AMD `define` function for RequireJS
	* @param {function( function, Object, Object )} factory
	*/
	var define = function ( factory ) {
		module.exports = factory( require, exports, module );
	};
}
/**
	* A module representing a SyntaxAnalizer.
	* @module lib/SyntaxAnalizer
	* @param {function( string )} require
	*/
define(function( require ) {
	"use strict";

	/**
		* Syntax analyze methods
		* @type {string[]}
		*/
	var sniffTypes = [
			"sourceCode",
			"syntaxTree",
			"token"
		],
		/**
		* Replica of PHP ucfirst
		* @access private
		* @param {string} str
		* @returns {string}
		*/
		ucfirst = function( str ) {
			return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
		},
		/**
		* Available sniffs names
		* @namespace
		*/
		registeredSniffs = {
			/** @type {string[]} */
			sourceCode: [
				"Indentation",
				"LineLength",
				"LineSpacing"
			],
			/** @type {string[]} */
			token: [
				"CommaPunctuatorSpacing",
				"QuoteConventions",
				"SemicolonPunctuatorSpacing"
			],
			/** @type {string[]} */
			syntaxTree: [
				"ArgumentsSpacing",
				"ArrayLiteralSpacing",
				"ChainedMethodCallsSpacing",
				"ChainedMethodCallsPerLineConventions",
				"CompoundStatementConventions",
				"EmptyConstructsSpacing",
				"FunctionNamingConventions",
				"NewExpressionCalleeNamingConventions",
				"ObjectLiteralSpacing",
				"OperatorSpacing",
				"ParametersSpacing",
				"TernaryConditionalPunctuatorsSpacing",
				"UnaryExpressionIdentifierSpacing",
				"VariableDeclarationPerScopeConventions",
				"VariableNamingConventions",
				"ObjectLiteralConventions",
				"ArrayLiteralConventions"
			]
		};

		/**
		* Workaround for http://requirejs.org/docs/errors.html#notloaded
		*/
		if ( typeof requirejs === "function" ) {
			require( "./Sniff/SourceCode/Indentation.js" );
			require( "./Sniff/SourceCode/LineLength.js" );
			require( "./Sniff/SourceCode/LineSpacing.js" );
			require( "./Sniff/SyntaxTree/ArgumentsSpacing.js" );
			require( "./Sniff/SyntaxTree/ArrayLiteralSpacing.js" );
			require( "./Sniff/SyntaxTree/ChainedMethodCallsSpacing.js" );
			require( "./Sniff/SyntaxTree/ChainedMethodCallsPerLineConventions.js" );
			require( "./Sniff/SyntaxTree/CompoundStatementConventions.js" );
			require( "./Sniff/SyntaxTree/EmptyConstructsSpacing.js" );
			require( "./Sniff/SyntaxTree/FunctionNamingConventions.js" );
			require( "./Sniff/SyntaxTree/NewExpressionCalleeNamingConventions.js" );
			require( "./Sniff/SyntaxTree/ObjectLiteralSpacing.js" );
			require( "./Sniff/SyntaxTree/OperatorSpacing.js" );
			require( "./Sniff/SyntaxTree/ParametersSpacing.js" );
			require( "./Sniff/SyntaxTree/TernaryConditionalPunctuatorsSpacing.js" );
			require( "./Sniff/SyntaxTree/UnaryExpressionIdentifierSpacing.js" );
			require( "./Sniff/SyntaxTree/VariableDeclarationPerScopeConventions.js" );
			require( "./Sniff/SyntaxTree/VariableNamingConventions.js" );
			require( "./Sniff/SyntaxTree/ObjectLiteralConventions.js" );
			require( "./Sniff/SyntaxTree/ArrayLiteralConventions.js" );
			require( "./Sniff/Token/CommaPunctuatorSpacing.js" );
			require( "./Sniff/Token/QuoteConventions.js" );
			require( "./Sniff/Token/SemicolonPunctuatorSpacing.js" );
		}
	/**
		* Provides methods to analyze a given syntax (tree, tokens, code) with sniffs associated to a
		* given rules (standard)
		*
		* @constructor
		* @alias module:lib/SyntaxAnalizer
		* @param {Object} standard
		*/
	return function( standard ) {
		var sniffs = {
			sourceCode: {},
			syntaxTree: {},
			token: {}
		};
		/** @lends module:lib/SyntaxAnalizer.prototype */
		return {
		/**
			* Populate sniffs repository with instances
			* @access public
			* @param {Object} sourceCode
			* @param {Object} mediator
			* @param {module:lib/TokenIterator} tokenIterator
			*/
		loadSniffs: function( sourceCode, mediator, tokenIterator ) {
			sniffTypes.forEach(function( type ){
				registeredSniffs[ type ].forEach(function( name ){
					var Proto = require( "./Sniff/" + ucfirst( type ) + "/" + name + ".js" );
					sniffs[ type ][ name ] = new Proto( sourceCode, mediator, tokenIterator );
				});
			});
		},
		/**
			* Validate assigned rules (of the given standard)
			* @access public
			*/
		validateStandard: function() {
			var rule,
				/**
				*
				* @param {type} type
				* @returns {undefined}@callback validate
				* @param {string} type
				*/
				validate = function( type ) {
					if ( sniffs[ type ].hasOwnProperty( this.rule ) && standard[ this.rule ] ) {
						sniffs[ type ][ this.rule ].validateRule( standard[ this.rule ] );
					}
				};
			for( rule in standard ) {
				if ( standard.hasOwnProperty( rule ) ) {
					sniffTypes.forEach( validate, { rule: rule });
				}
			}
		},

		/**
			* Analyze the source code for the registered rules and available sniffs
			* @access public
			*/
		applySourceCodeSniffs: function() {
			var rule;
			for( rule in standard ) {
				if ( standard.hasOwnProperty( rule ) && sniffs.sourceCode.hasOwnProperty( rule ) &&
					standard[ rule ] ) {
					sniffs.sourceCode[ rule ].run( standard[ rule ] );
				}
			}
		},
		/**
			* Go through the tokens list and apply registered sniffs to every token
			* @access public
			* @param {Object[]} tokens
			*/
		applyTokenSniffs: function( tokens ) {
			/**
			* @callback anonymous
			* @param {string} token
			*/
			tokens.forEach(function( token ){
			var rule;
			for( rule in standard ) {
				if ( standard.hasOwnProperty( rule ) && sniffs.token.hasOwnProperty( rule ) &&
					standard[ rule ] ) {
					sniffs.token[ rule ].run( standard[ rule ], token );
				}
			}
			});
		},
		/**
			* Go through the syntax tree and apply registered sniffs to every node
			* @access public
			* @param {Object} syntaxTree
			*/
		applySyntaxTreeSniffs: function( syntaxTree ) {
			/**
			* @callback sniffSyntaxTree
			* @param {Object} node
			* @param {Object} [pNode={ type: null }]
			*/
			this.traverseSyntaxTree( syntaxTree, function( node, pNode ){
				var rule;
				for( rule in standard ) {
					if ( standard.hasOwnProperty( rule ) && sniffs.syntaxTree.hasOwnProperty( rule ) &&
						standard[ rule ] ) {
						sniffs.syntaxTree[ rule ].run( standard[ rule ], node, pNode || { type: null } );
					}
				}
			});
		},
		/**
		* Flag all the statements inside an outter call expression
		* @access protected
		* @param {Object} syntaxTree
		*/
		markNestings: function( syntaxTree ){
			/**
			* Implementing callback
			* @param {Object} node
			*/
			var that = this,
				cb = function( node ) {
					if ( node.type === "CallExpression" ) {
						node[ "arguments" ].forEach(function( arg ){
							that.traverseSyntaxTree( arg, function( el ){
								el.isNesting = true;
							}, node );
						});
					}
				};
			this.traverseSyntaxTree( syntaxTree, cb );
		},

		/**
		* Esprima syntax tree traverser
		* Inspiread by
		* https://github.com/mdevils/node-jscs
		* @access public
		* @param {Object} node
		* @param {sniffSyntaxTree} fn
		* @param {Object} parentNode
		*/
		traverseSyntaxTree: function( node, fn, parentNode ) {
			var that = this,
				propName,
				contents,
				/**
				* @callback traverseEvery
				* @param {Object} member
				*/
				traverseEvery = function( member ) {
					that.traverseSyntaxTree( member, this.fn, this.node );
				};

			if ( node && node.hasOwnProperty( "type" ) ) {
				fn( node, parentNode );
			}

			for ( propName in node ) {
				if ( propName !== "parent" && node.hasOwnProperty( propName ) &&
					propName !== "tokens" && propName !== "comments" ) {
					contents = node[ propName ];
					if ( contents && typeof contents === "object" ) {
						if ( Array.isArray( contents ) ) {
							contents.forEach( traverseEvery, { fn: fn, node: node });
						} else {
							that.traverseSyntaxTree( contents, fn, node );
						}
					}
				}
			}
		}

		};
	};
});