/**
* The ACE Editor TemplateProcessor base
*
* @module aui-ace-editor
* @submodule aui-ace-autocomplete-templateprocessor
*/
var Lang = A.Lang,
AArray = A.Array,
AObject = A.Object,
Base = A.AceEditor.AutoCompleteBase,
MATCH_DIRECTIVES = 0,
MATCH_VARIABLES = 1,
TOKEN_PUNCTUATOR_DOT = 1,
TOKEN_UNRECOGNIZED = -1,
TOKEN_VARIABLE = 0,
/**
* A base class for TemplateProcessor.
*
* @class A.AceEditor.TemplateProcessor
* @extends Base
* @param {Object} config Object literal specifying widget configuration
* properties.
* @constructor
*/
TemplateProcessor = A.Base.create('aui-ace-autocomplete-templateprocessor', A.Base, [
], {
/**
* Accepts match and depending on its type processes directives or
* variables. In case of success, calls the provided success callback,
* or the error callback otherwise.
*
* @method getResults
* @param {Object} match The provided match. It should contain at least
* type and content properties
* @param {Function} callbackSuccess The function to be called in case
* of success
* @param {Function} callbackError The function to be called in case of
* error
*/
getResults: function(match, callbackSuccess, callbackError) {
var instance = this,
content,
host,
matchDirectives,
matches,
type;
type = match.type;
if (type === MATCH_DIRECTIVES) {
matchDirectives = instance.get('directives');
content = match.content.toLowerCase();
if (content.length) {
host = instance.get('host');
matchDirectives = host._filterResults(content, matchDirectives);
}
callbackSuccess(matchDirectives);
}
else if (type === MATCH_VARIABLES) {
matches = instance._getVariableMatches(match.content);
callbackSuccess(matches);
}
else {
callbackError();
}
},
/**
* Formats the selected suggestion depending on the match type and
* currently selected editor mode. The match type can be one of:
* MATCH_DIRECTOVES or MATCH_VARIABLES. The selected editor mode can be
* one of the following: INSERT or OVERWRITE. See {{#crossLink
* "AceEditor.AutoCompleteBase/fillMode:attribute"}}{{/crossLink}}
*
* @method getSuggestion
* @param {Object} match The provided match. It should contain at least
* type and content properties
* @param {String} selectedSuggestion The selected suggestion from the
* list with suggestions
* @return {String} The final suggestion which should be inserted to the
* editor
*/
getSuggestion: function(match, selectedSuggestion) {
var instance = this,
fillMode,
lastEntry,
result,
type,
variables;
result = selectedSuggestion || '';
if (selectedSuggestion) {
fillMode = instance.get('host').get('fillMode');
type = match.type;
if (fillMode === Base.FILL_MODE_INSERT) {
if (type === MATCH_DIRECTIVES) {
if (match.content && selectedSuggestion.indexOf(match.content) === 0) {
result = selectedSuggestion.substring(match.content.length);
}
}
else if (type === MATCH_VARIABLES) {
variables = match.content.split('.');
lastEntry = variables[variables.length - 1];
if (lastEntry && selectedSuggestion.indexOf(lastEntry) === 0) {
result = selectedSuggestion.substring(lastEntry.length);
}
}
}
else if (type === MATCH_VARIABLES) {
variables = match.content.split('.');
variables[variables.length - 1] = selectedSuggestion;
result = variables.join('.');
}
}
return result;
},
/**
* Checks if the the provided index is the last token in the list of
* tokens.
*
* @method _isLastToken
* @param {Number} index The index which should be checked
* @param {Array} tokens The array with tokens
* @protected
* @return {Boolean} True if the provided index is the last token in the
* list
*/
_isLastToken: function(index, tokens) {
return index === tokens.length - 1;
},
/**
* Retrieves the type of a token. It can be one of these:
* TOKEN_PUNCTUATOR_DOT = 1
* TOKEN_UNRECOGNIZED = -1
* TOKEN_VARIABLE = 0
*
* @method _getTokenType
* @param {Number} token The type of the token
* @protected
* @return {Number} The token type
*/
_getTokenType: function(token) {
var tokenType = TOKEN_UNRECOGNIZED;
if (Lang.isString(token)) {
if (token.length) {
tokenType = TOKEN_VARIABLE;
}
else {
tokenType = TOKEN_PUNCTUATOR_DOT;
}
}
return tokenType;
},
/**
* Scans the content and extracts variables.
*
* @method _getVariableMatches
* @param {String} content The content from which variable matches will
* be extracted
* @protected
* @return {Array} List with variable matches
*/
_getVariableMatches: function(content) {
var instance = this,
curVariableData,
data,
host,
i,
isLastToken,
lastEntry,
leftPartheseIndex,
matches,
results,
resultsData,
token,
tokens,
tokenType,
variableData,
variableType;
results = [];
data = instance.get('variables');
resultsData = {};
curVariableData = data.variables;
if (content) {
tokens = content.split('.');
lastEntry = tokens[tokens.length - 1];
for (i = 0; i < tokens.length; i++) {
token = tokens[i];
tokenType = instance._getTokenType(token);
if (tokenType === TOKEN_PUNCTUATOR_DOT) {
if (i === 0) {
curVariableData = {};
}
else {
resultsData = curVariableData;
}
}
else if (tokenType === TOKEN_VARIABLE) {
isLastToken = instance._isLastToken(i, tokens);
if (isLastToken) {
resultsData = curVariableData;
break;
}
leftPartheseIndex = token.indexOf('(');
if (leftPartheseIndex !== -1) {
token = token.substring(0, leftPartheseIndex);
}
variableData = curVariableData[token];
if (variableData) {
if (i === 0) {
variableType = variableData.type;
}
else {
variableType = variableData.returnType;
}
curVariableData = data.types[variableType] || {};
}
else if (isLastToken) {
resultsData = curVariableData;
break;
}
else {
resultsData = {};
break;
}
}
}
}
else {
resultsData = data.variables;
}
results = AObject.keys(resultsData);
matches = results.sort();
if (lastEntry) {
host = instance.get('host');
matches = host._filterResults(lastEntry, matches);
}
if (matches.length) {
matches = AArray.map(
matches,
function(item) {
var args,
data;
data = resultsData[item];
if (data.type === 'Method') {
args = AArray.map(
data.argumentTypes,
function(item) {
var parts = item.split('.');
return parts[parts.length - 1];
}
);
return item + '(' + args.join(', ') + ')';
}
else {
return item;
}
}
);
}
return matches;
},
/**
* Normalizes a regualr expression value. If the value is String, it
* will be converted to an RegExp.
*
* @method _setRegexValue
* @param {String|RegExp} value The provided regualr expression value
* @protected
* @return {RegExp} The final instance of RegExp object
*/
_setRegexValue: function(value) {
var result = A.AttributeCore.INVALID_VALUE;
if (Lang.isString(value)) {
result = new RegExp(value);
}
else if (value instanceof RegExp) {
result = value;
}
return result;
}
}, {
/**
* Static property which provides a string to identify the class.
*
* @property NAME
* @type String
* @static
*/
NAME: 'aui-ace-autocomplete-templateprocessor',
/**
* Static property provides a string to identify the namespace.
*
* @property NS
* @type String
* @static
*/
NS: 'aui-ace-autocomplete-templateprocessor',
/**
* Static property used to define the default attribute
* configuration for the TemplateProcessor.
*
* @property ATTRS
* @type Object
* @static
*/
ATTRS: {
/**
* Contains an array of all possible directives for the
* corresponding language.
*
* @attribute directives
* @type Array
*/
directives: {
validator: Lang.isArray
},
/**
* The Editor in which the current instance is plugged.
*
* @attribute host
* @type Object
*/
host: {
validator: Lang.isObject
},
/**
* Contains the supported variables for the corresponding language.
*
* @attribute variables
* @type Object
*/
variables: {
validator: Lang.isObject
}
}
});
A.AceEditor.TemplateProcessor = TemplateProcessor;