using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Data;
using System.Collections;
using System.Web.UI.WebControls;
using CMS.CMSHelper;
using CMS.ExtendedControls;
using CMS.GlobalHelper;
using CMS.PortalEngine;
using CMS.SettingsProvider;
using CMS.DocumentEngine;
using CMS.UIControls;
using TreeNode = CMS.DocumentEngine.TreeNode;
public partial class CMSAdminControls_UI_Macros_MacroEditor : CMSUserControl, ICallbackEventHandler
{
#region "Variables"
///
/// List of macro namespaces for which the methods are permanently available (visible in AutoCompletion without any context).
///
protected List mNamespaceUsings = null;
private List mUsingList = null;
private ContextResolver mResolver = null;
private ContextResolver mASCXResolver = null;
private static List mMethodList = null;
private bool mUseAutoComplete = true;
private bool mShowAutoCompletionAbove = false;
private bool mMixedMode = true;
private int mTopOffset = 0;
private int mLeftOffset = 0;
private string mEditorFocusLostScript = null;
#endregion
#region "Properties"
///
/// Gets or sets the root object the fields of which will be used as root help in ASCX mode.
///
public object ASCXRootObject
{
get;
set;
}
///
/// Indicates whether the editor is in ASCX transformation mode (i.e. AutoCompletion fires only when requested with CTRL+SPACE and only fields are shown)
///
private bool ASCXMode
{
get
{
return (ASCXRootObject != null);
}
}
///
/// Gets the list of all available special commands.
///
private static List CommandsList
{
get
{
if (mMethodList == null)
{
mMethodList = new List();
// Add flow control commands
mMethodList.Add("for\nfor (i = 0; i < 10; i++) { | }");
mMethodList.Add("foreach\nforeach (x in collection) { | }");
mMethodList.Add("if\nif (|) { }");
mMethodList.Add("while\nwhile( | ) { }");
mMethodList.Add("break\nbreak;");
mMethodList.Add("continue\ncontinue;");
mMethodList.Add("return\nreturn;");
mMethodList.Add("print\nprint(|);");
mMethodList.Add("println\nprintln(|);");
mMethodList.Add("null\nnull");
}
return mMethodList;
}
}
///
/// Gets the list of all available special commands.
///
private List UsingList
{
get
{
if (mUsingList == null)
{
mUsingList = new List();
if (NamespaceUsings != null)
{
foreach (string name in NamespaceUsings)
{
object namespaceInstance = Resolver.GetNamespaceInstance(name);
if (namespaceInstance != null)
{
List methods = MacroMethods.GetMethodsForObject(namespaceInstance, false, true);
FillFromMethodList(mUsingList, methods);
}
}
}
}
return mUsingList;
}
}
///
/// Gets or sets the list of macro namespaces for which the methods are permanently available (visible in AutoCompletion without any context).
///
public List NamespaceUsings
{
get
{
return mNamespaceUsings;
}
set
{
mNamespaceUsings = value;
}
}
///
/// Gets or sets the list of macro namespaces which are just method containers (Math, Countries, ...).
///
private List NamespacesInUse
{
get
{
return new List() { "Math", "Transformation", "Countries", "Visitor" };
}
}
///
/// Gets or sets the name of the resolver to use. This property is used if Resolver property is not explicitly set.
///
public string ResolverName
{
get
{
return ValidationHelper.GetString(GetValue("ResolverName"), "");
}
set
{
SetValue("ResolverName", value);
}
}
///
/// Gets or sets context resolver used for getting autocomplete options.
///
public ContextResolver Resolver
{
get
{
if (mResolver == null)
{
if (!string.IsNullOrEmpty(ResolverName))
{
mResolver = ResolverFactory.GetResolver(ResolverName);
}
else
{
// Setup the default resolver
mResolver = CMSContext.CurrentResolver.CreateContextChild();
}
mResolver.Context.VirtualMode = true;
}
return mResolver;
}
set
{
mResolver = value;
}
}
///
/// Gets or sets context resolver used for getting autocomplete options in ASCX Mode.
///
private ContextResolver ASCXResolver
{
get
{
if (ASCXRootObject == null)
{
return Resolver;
}
if (mASCXResolver == null)
{
mASCXResolver = Resolver.CreateContextChild();
mASCXResolver.Context.VirtualMode = true;
mASCXResolver.CheckSecurity = false;
mASCXResolver.SetNamedSourceData("ASCXRootObject", ASCXRootObject);
}
return mASCXResolver;
}
}
///
/// Indicates whether the autocompletion feature is enabled or not.
///
public bool UseAutoComplete
{
get
{
return this.mUseAutoComplete && Editor.SyntaxHighlightingEnabled;
}
set
{
mUseAutoComplete = value;
}
}
///
/// Gets or sets the code which is executed when editor lost focus.
///
public string EditorFocusLostScript
{
get
{
return mEditorFocusLostScript;
}
set
{
mEditorFocusLostScript = value;
}
}
///
/// Indicates whether the editor is in pure macro editing mode (whole text is considered as macro) or mixed mode, where auto completion is fired only inside {%%} environment.
///
public bool MixedMode
{
get
{
return mMixedMode;
}
set
{
mMixedMode = value;
}
}
///
/// Gets or sets the left offset of the autocomplete control (to position it correctly).
///
public int LeftOffset
{
get
{
return mLeftOffset;
}
set
{
mLeftOffset = value;
}
}
///
/// Gets or sets the top offset of the autocomplete control (to position it correctly).
///
public int TopOffset
{
get
{
return mTopOffset;
}
set
{
mTopOffset = value;
}
}
///
/// If true, tree is shown above the editor, otherwise it is below (default position is below).
///
public bool ShowAutoCompletionAbove
{
get
{
return mShowAutoCompletionAbove;
}
set
{
mShowAutoCompletionAbove = value;
}
}
///
/// Gets the name of java script object of the auto completion extender.
///
public string AutoCompletionObject
{
get
{
return "autoCompleteObj_" + ClientID;
}
}
///
/// If true, no global objects are shown, only inner sources (named data sources, etc.) are shown as hints.
///
public bool ShowOnlyInnerSources
{
get;
set;
}
#endregion
#region "Mirrored editor properties"
///
/// Gets the editor control.
///
public ExtendedTextArea Editor
{
get
{
return txtCode;
}
}
///
/// Gets if syntax highlighting is enabled for this control.
///
public bool SyntaxHighlightingEnabled
{
get
{
return Editor.SyntaxHighlightingEnabled;
}
}
///
/// Gets or sets whether the control is read only
///
public bool ReadOnly
{
get
{
return Editor.ReadOnly;
}
set
{
Editor.ReadOnly = value;
}
}
///
/// Gets or sets the width of the editor
///
public Unit Width
{
get
{
return Editor.Width;
}
set
{
Editor.Width = value;
}
}
///
/// Gets or sets the height of the editor
///
public Unit Height
{
get
{
return Editor.Height;
}
set
{
Editor.Height = value;
}
}
///
/// Gets or sets the editor text.
///
public string Text
{
get
{
return Editor.Text;
}
set
{
Editor.Text = value;
}
}
#endregion
#region "Auto complete methods"
///
/// Handles autocomplete action.
///
/// Macro expression
/// Caret position
/// If true, method context should be returned, otherwise hints are returned
/// Text of previous lines
private string AutoComplete(string macro, int position, bool isShowContext, string previousLines)
{
// Do lexical analysis, supress errors
List tokens = MacroElement.ParseExpression(macro, true);
// Variables declared on actual line;
List inlineVariables = new List();
// Locate current element, find all created variables
MacroElement currentElement = null;
int currentElementIndex = -1;
for (int i = 0; i < tokens.Count; i++)
{
MacroElement el = tokens[i];
MacroElement elNext = (i < tokens.Count - 1 ? tokens[i + 1] : null);
if (elNext == null || ((el.StartIndex <= position) && (elNext.StartIndex > position)))
{
currentElement = el;
currentElementIndex = i;
break;
}
if ((el.Type == ElementType.Operator) && (el.Expression == "=") && (i > 0))
{
// Add variable declaration to special fields
MacroElement elPrev = tokens[i - 1];
if (elPrev.Type == ElementType.Identifier)
{
inlineVariables.Add(elPrev.Expression);
}
}
}
if (isShowContext)
{
return GetContext(tokens, currentElement, currentElementIndex);
}
else
{
// If we are in the middle of the comment or a constant, do not show any help
if ((currentElement != null) && ((currentElement.Type == ElementType.Boolean) || (currentElement.Type == ElementType.Double) ||
(currentElement.Type == ElementType.Integer) || (currentElement.Type == ElementType.String)))
{
return "";
}
else
{
return GetHints(tokens, currentElement, currentElementIndex, previousLines, inlineVariables);
}
}
}
///
/// Returns list of possible hints.
///
/// Text passed from JS
/// Position within the text
private string AutoCompleteASCX(string text, int position)
{
List members = new List();
// Only part up to the cursor is interesting
text = text.Substring(0, position);
// Parse parent expression
string parentExpr = text;
for (int i = text.Length - 1; i >= 0; i--)
{
char c = text[i];
if (!(Char.IsLetterOrDigit(c) || (c == '.')))
{
parentExpr = text.Substring(i + 1, text.Length - i - 1);
break;
}
}
bool innerHints = false;
if (!string.IsNullOrEmpty(parentExpr))
{
int index = parentExpr.LastIndexOfCSafe('.');
if (index > 0)
{
parentExpr = parentExpr.Substring(0, index);
object obj = null;
bool match = false;
ASCXResolver.ResolveDataMacro("ASCXRootObject." + parentExpr, ref obj, out match, true);
if (obj != null)
{
FillObjectProperties(members, null, obj);
}
innerHints = true;
}
else
{
FillObjectProperties(members, null, ASCXRootObject);
FillWithResolverFields(members);
}
}
else
{
FillObjectProperties(members, null, ASCXRootObject);
FillWithResolverFields(members);
}
// Sort everything
members.Sort();
// Add prioritized items at the beginning
if (!innerHints && (Resolver.NamedSourceDataPriority.Count > 0))
{
List priorities = Resolver.NamedSourceDataPriority;
priorities.Sort();
members.Insert(0, "----");
members.InsertRange(0, priorities);
}
// Remove duplicities
members = RemoveDuplicities(members);
StringBuilder sb = new StringBuilder();
foreach (string member in members)
{
sb.Append("$" + member);
}
return sb.ToString().TrimStart('$');
}
///
/// Returns context help (current method parameters).
///
/// Lexems from lexical analysis
/// Current lexem
/// Index of the current lexem
private string GetContext(List tokens, MacroElement currentElement, int currentElementIndex)
{
string methodName = "";
int paramNumber = 0;
int brackets = 0;
bool withoutFirstParam = false;
for (int i = currentElementIndex; i >= 0; i--)
{
ElementType type = tokens[i].Type;
if (brackets == 0)
{
// Count number of commas before current element (it's the current number of parameter)
if (type == ElementType.Comma)
{
paramNumber++;
}
// We need to take context to the left side of current lexem
// It needs to preserve the structure (deepnes withing the brackets)
if (type == ElementType.LeftBracket)
{
if ((i > 0) && (tokens[i - 1].Type == ElementType.Identifier))
{
methodName = tokens[i - 1].Expression;
if (i > 2)
{
bool isNamespaceCall = false;
if (tokens[i - 3].Type == ElementType.Identifier)
{
string exprToCheck = tokens[i - 3].Expression;
foreach (string item in NamespacesInUse)
{
if (item.EqualsCSafe(exprToCheck))
{
isNamespaceCall = true;
break;
}
}
}
withoutFirstParam = (tokens[i - 2].Type == ElementType.Dot) && !isNamespaceCall;
}
}
break;
}
else if (type == ElementType.LeftIndexer)
{
return "Indexer: Type number or name of the element.";
}
}
// Ensure correct deepnes within the brackets
if ((type == ElementType.RightBracket) || (type == ElementType.RightIndexer))
{
brackets++;
}
else if ((type == ElementType.LeftBracket) || (type == ElementType.LeftIndexer))
{
brackets--;
}
}
MacroMethod method = MacroMethods.GetMethod(methodName);
if (method != null)
{
return GetMethodString(method, paramNumber, withoutFirstParam);
}
// Method not known or there is syntax error in the expression
return "";
}
///
/// Returns method help (return type, name and parameters), highlites current parameter.
///
/// Method object
/// Number of the parameter which should be highlighted
/// If true, first parameter is not rendered
private static string GetMethodString(MacroMethod method, int paramNumber, bool withoutFirstParam)
{
string currentParamComment = "";
string currentParamName = "";
string parameters = "";
int startParam = (withoutFirstParam ? 1 : 0);
if (method.Parameters != null)
{
for (int i = startParam; i < method.Parameters.Count; i++)
{
MacroMethodParam p = method.Parameters[i];
string paramtype = null;
if (p.IsParams)
{
paramtype = "params " + p.Type.Name + "[]";
}
else
{
paramtype = p.Type.Name;
}
string param = paramtype + " " + p.Name;
if (i >= method.MinimumParameters)
{
param = "" + param + "";
}
int index = i - startParam;
if ((index == paramNumber) || (p.IsParams && (index <= paramNumber)))
{
currentParamName = p.Name;
currentParamComment = p.Comment;
param = "" + param + "";
}
parameters += ", " + param;
}
}
if (parameters != "")
{
parameters = parameters.Substring(2);
}
string type = (method.Type == null ? "object" : method.Type.Name);
string methodComment = (string.IsNullOrEmpty(method.Comment) ? "" : "" + method.Comment + "
");
string paramComment = (string.IsNullOrEmpty(currentParamComment) ? "" : "
" + currentParamName + ": " + currentParamComment);
return methodComment + type + " " + method.Name + "(" + parameters + ")" + paramComment;
}
///
/// Returns list of possible hints.
///
/// Lexems from lexical analysis
/// Current lexem
/// Index of the current lexem
/// Previous lines to get previous variable declarations
private string GetHints(List tokens, MacroElement currentElement, int currentElementIndex, string previousLines, IEnumerable inlineVariables)
{
// Get lists with methods and properties
List members = new List();
string dataMemeberContext = "";
object obj = null;
List dataProperties = new List();
if (currentElement == null)
{
GetPropertiesFromContext(members, null, "");
}
else
{
// Properties autocomplete
bool showAllProperties = (currentElement.Type == ElementType.LeftBracket) || (currentElement.Type == ElementType.LeftIndexer) ||
(currentElement.Type == ElementType.BlockStart) || (currentElement.Type == ElementType.BlockEnd) ||
(currentElement.Type == ElementType.Comma) || MacroElement.IsValidOperator(currentElement.Expression);
if (!showAllProperties)
{
// We need to find current datamember context
int brackets = 0;
for (int i = currentElementIndex - 1; i >= 0; i--)
{
ElementType type = tokens[i].Type;
if (brackets == 0)
{
// We need to take context to the left side of current lexem
// It needs to preserve the structure (deepnes withing the brackets)
if ((type == ElementType.Comma) || (type == ElementType.Operator) ||
(type == ElementType.LeftBracket) || (type == ElementType.LeftIndexer) ||
(type == ElementType.BlockStart) || (type == ElementType.BlockEnd))
{
break;
}
}
// Append this part
dataMemeberContext = tokens[i].Expression + dataMemeberContext;
// Ensure correct deepnes within the brackets
if ((type == ElementType.RightBracket) || (type == ElementType.RightIndexer) || (type == ElementType.BlockEnd))
{
brackets++;
}
else if ((type == ElementType.LeftBracket) || (type == ElementType.LeftIndexer) || (type == ElementType.BlockStart))
{
brackets--;
}
}
dataMemeberContext = dataMemeberContext.TrimEnd('.');
}
// Analyze code before current line to find variable declarations (makes sense only for root hints - i.e. datamember context is empty)
if (string.IsNullOrEmpty(dataMemeberContext))
{
List vartokens = MacroElement.ParseExpression(previousLines, true);
List variables = new List();
for (int i = 0; i < vartokens.Count; i++)
{
MacroElement el = vartokens[i];
if ((el.Type == ElementType.Operator) && (el.Expression == "=") && (i > 0))
{
// Add variable declaration to special fields
MacroElement elPrev = vartokens[i - 1];
if (elPrev.Type == ElementType.Identifier)
{
variables.Add(elPrev.Expression);
}
}
}
// Add variables
members.AddRange(variables);
members.AddRange(inlineVariables);
}
obj = GetPropertiesFromContext(members, dataProperties, dataMemeberContext);
}
// Do not process further if only prioritized properties should be handled
if (string.IsNullOrEmpty(dataMemeberContext) && Resolver.ShowOnlyPrioritized)
{
members.AddRange(Resolver.NamedSourceDataPriority);
members.Sort();
return MakeFinalList(members);
}
// Add proper methods
if (string.IsNullOrEmpty(dataMemeberContext))
{
// Add commands only for empty contexts
members.AddRange(CommandsList);
members.Add("Math");
// Add namespaces objects
if (!ShowOnlyInnerSources)
{
members.Add("Transformation");
members.Add("Countries");
members.Add("Visitor");
}
// List of methods for any type
FillFromMethodList(members, MacroMethods.GetMethodsForObject(null));
}
else
{
if (obj == null)
{
// If the object is null we will offer string methods because these are the most common methods
obj = "";
}
// Get the list of suitable methods for resulting type
FillFromMethodList(members, MacroMethods.GetMethodsForObject(obj));
}
// Add methods which belongs to permanently registered namespaces
members.AddRange(UsingList);
// Remove items which should not be displayed
if (Resolver.Context.DisablePageContextMacros)
{
if (members.Contains("CurrentDocument"))
{
members.Remove("CurrentDocument");
}
if (members.Contains("CurrentPageInfo"))
{
members.Remove("CurrentPageInfo");
}
}
// Remove context objects if not needed
if (Resolver.Context.DisableContextObjectMacros)
{
for (int i = members.Count - 1; i >= 0; i--)
{
if (members[i].EndsWithCSafe("context", true))
{
members.RemoveAt(i);
}
}
}
// Sort everything
members.Sort();
// Insert prioritized items at the beginning
if (string.IsNullOrEmpty(dataMemeberContext) && (Resolver.NamedSourceDataPriority.Count > 0))
{
List priorities = Resolver.NamedSourceDataPriority;
priorities.Sort();
members.Insert(0, "----");
members.InsertRange(0, priorities);
}
// Add data properties at the end
if (dataProperties.Count > 0)
{
members.Add("----");
members.AddRange(dataProperties);
}
// Remove duplicities
return MakeFinalList(members);
}
private static string MakeFinalList(List members)
{
List finalList = RemoveDuplicities(members);
StringBuilder sb = new StringBuilder();
foreach (string member in finalList)
{
sb.Append("$" + member);
}
return sb.ToString().TrimStart('$');
}
///
/// Removes duplicit records from the list.
///
/// List to process
private static List RemoveDuplicities(List members)
{
List finalList = new List();
foreach (string member in members)
{
if (!finalList.Contains(member))
{
finalList.Add(member);
}
}
return finalList;
}
///
/// Resolves the data member context and adds correct properties options to a given list.
///
/// List of properties where to add
/// List of data properties
/// Data member context (macro part)
private object GetPropertiesFromContext(List properties, List dataProperties, string dataMemberContext)
{
if (string.IsNullOrEmpty(dataMemberContext))
{
if (Resolver.ShowOnlyPrioritized)
{
return null;
}
if (!ShowOnlyInnerSources)
{
// Add CMSContext.Current
if (!Resolver.Context.DisableContextMacros)
{
properties.AddRange(CMSContext.Current.Properties);
}
// Add all resolver special values
properties.AddRange(MacroResolver.SpecialValues);
}
// Add all members contained in given resolver
FillWithResolverFields(properties);
return null;
}
else
{
object obj = null;
bool match = false;
bool securityCheckPassed = false;
// Disable security check and resolve the macro
Resolver.UserName = CMSContext.CurrentUser.UserName;
Resolver.CheckIntegrity = false;
Resolver.ResolveDataMacro(dataMemberContext, ref obj, out match, true, out securityCheckPassed);
if (securityCheckPassed)
{
if (match)
{
// Fill at most MaxMacroNodes properties to AutoCompletion data
FillObjectProperties(properties, dataProperties, obj);
if (dataProperties.Count > MacroResolver.MaxMacroNodes)
{
dataProperties.RemoveRange(MacroResolver.MaxMacroNodes, dataProperties.Count - MacroResolver.MaxMacroNodes);
}
}
}
else
{
properties.Add(GetString("macrodesigner.accessdenied"));
}
return obj;
}
}
///
/// Adds to given list all the data contained in resolver (sourcetables, namedsourcedata, etc.)
///
/// Properties to add
private void FillWithResolverFields(List properties)
{
if (Resolver.SourceTables != null)
{
foreach (Hashtable table in Resolver.SourceTables)
{
foreach (string key in table.Keys)
{
properties.Add(key);
}
}
}
// Special macros
if (Resolver.SpecialMacros != null)
{
int length = Resolver.SpecialMacros.GetUpperBound(0);
for (int i = 0; i <= length; i++)
{
properties.Add(Resolver.SpecialMacros[i, 0]);
}
}
// Source parameters
if (Resolver.SourceParameters != null)
{
int length = Resolver.SourceParameters.GetUpperBound(0);
for (int i = 0; i <= length; i++)
{
properties.Add(ValidationHelper.GetString(Resolver.SourceParameters[i, 0], ""));
}
}
// Named source data
properties.AddRange(Resolver.NamedSourceDataKeys);
}
///
/// Fills given list with properties of given object.
///
/// List to fill
/// List of data properties
/// Object the properties of which should be added to the list
private static void FillObjectProperties(List properties, List dataProperties, object obj)
{
if (obj != null)
{
if (obj is DataRow)
{
// DataRow source, bind column names
DataRow dr = (DataRow)obj;
foreach (DataColumn col in dr.Table.Columns)
{
properties.Add(col.ColumnName);
}
}
else if (obj is DataRowView)
{
// DataRowView source, bind column names
DataRowView dr = (DataRowView)obj;
foreach (DataColumn col in dr.DataView.Table.Columns)
{
properties.Add(col.ColumnName);
}
}
else if (obj is IHierarchicalObject)
{
// Data container source
IHierarchicalObject hc = (IHierarchicalObject)obj;
properties.AddRange(hc.Properties);
}
else if (obj is IDataContainer)
{
// Data container source
IDataContainer dc = (IDataContainer)obj;
properties.AddRange(dc.ColumnNames);
}
// Named enumerable objects
if (obj is INamedEnumerable)
{
if (dataProperties != null)
{
INamedEnumerable namedCol = (INamedEnumerable)obj;
if (namedCol.ItemsHaveNames)
{
IEnumerator enumerator = namedCol.GetNamedEnumerator();
while (enumerator.MoveNext())
{
// Named item
string name = namedCol.GetObjectName(enumerator.Current);
if (ValidationHelper.IsIdentifier(name))
{
dataProperties.Add(name);
}
else
{
dataProperties.Add("[\"" + name + "\"]");
}
}
}
}
}
// Special case for TreeNode and PageInfo - append ediable parts
if ((obj is TreeNode) || (obj is PageInfo))
{
EditableItems eItems = null;
if (obj is TreeNode)
{
// TreeNode editable fields
TreeNode node = (TreeNode)obj;
eItems = node.DocumentContent;
}
else
{
PageInfo pi = (PageInfo)obj;
eItems = pi.EditableItems;
}
// Editable regions
foreach (string item in eItems.EditableRegions.Keys)
{
properties.Add(item);
}
// Editable webparts
foreach (string item in eItems.EditableWebParts.Keys)
{
properties.Add(item);
}
}
// Remove user password from IntelliSense
if (obj is IUserInfo)
{
properties.Remove("UserPassword");
}
}
}
///
/// Fills the autoCompletionList with strings formated for AutoCompletion.
///
/// Target string list
/// Source MacroMethod list
private static void FillFromMethodList(List autoCompletionList, IEnumerable methodList)
{
foreach (MacroMethod method in methodList)
{
string m = method.Name.Trim().ToLowerCSafe();
if (!MacroElement.IsValidOperator(m) && !MacroElement.IsWordOperator(m))
{
if ((m != "logtodebug") || CMSContext.CurrentUser.IsGlobalAdministrator)
{
// At sign at the end is to distinguish between method and property in the script afterwards
if (string.IsNullOrEmpty(method.Snippet))
{
autoCompletionList.Add(method.Name + "\n");
}
else
{
autoCompletionList.Add(method.Name + "\n" + method.Snippet);
}
}
}
}
}
///
/// Returns JS code which registers help for all the methods.
///
private string GetJSMethodsHashTable()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("var mc = methodComments;");
foreach (MacroMethod m in MacroMethods.Methods.Values)
{
string name = m.Name;
string comment = GetMethodString(m, -1, false);
sb.AppendLine("mc[" + ScriptHelper.GetString(name + "\n") + "] = " + ScriptHelper.GetString(comment) + ";");
}
return sb.ToString();
}
#endregion
#region "Page events"
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
// Hide all the hints at the beginning
pnlContext.Style.Add("display", "none");
pnlQuickContext.Style.Add("display", "none");
pnlHints.Style.Add("display", "none");
if (UseAutoComplete && txtCode.Enabled)
{
string editorObjName = "autoCompleteObj_" + ClientID;
// Register main macro editor scripts
ScriptHelper.RegisterJQuery(Page);
ScriptHelper.RegisterScriptFile(Page, "Macros/MacroEditor.js");
ScriptHelper.RegisterClientScriptBlock(Page, typeof(string), "MacroEditorConstants",
"var METHOD_IMG = '" + GetImageUrl("/Design/Controls/MacroEditor/method.png") + "';" +
"var SNIPPET_IMG = '" + GetImageUrl("/Design/Controls/MacroEditor/snippet.png") + "';" +
"var PROPERTY_IMG = '" + GetImageUrl("/Design/Controls/MacroEditor/property.png") + "';", true);
ScriptHelper.RegisterClientScriptBlock(Page, typeof(string), "MacroEditorCallbackScript_" + ClientID,
"function handleCallback_" + ClientID + "(value, context) {" +
" if (context == 'context') {" +
" " + editorObjName + ".fillContext(value);" +
" } else {" +
" " + editorObjName + ".fillHints(value);" +
" }" +
"}", true);
}
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
if (UseAutoComplete && txtCode.Enabled)
{
// We need to generate this script on Render since extended area does that that late as well
// and editor object is not available before
string cbRefShowHint = Page.ClientScript.GetCallbackEventReference(this, "this.callbackArgument", "handleCallback_" + ClientID, "'hint'", true);
string cbRefShowContext = Page.ClientScript.GetCallbackEventReference(this, "this.callbackArgument", "handleCallback_" + ClientID, "'context'", true);
string script =
"var ac = " + AutoCompletionObject + " = new AutoCompleteExtender(" + txtCode.EditorID + ", document.getElementById('" + pnlHints.ClientID + "'), document.getElementById('" + pnlContext.ClientID + "'), document.getElementById('" + pnlQuickContext.ClientID + "'), " + (MixedMode ? "true" : "false") + ", " + TopOffset + ", " + LeftOffset + ", " + (ASCXMode ? "true" : "false") + "); \n" +
"ac.callbackContext = function() { " + cbRefShowContext + "; }\n" +
"ac.callbackHint = function() { " + cbRefShowHint + "; }\n" +
"ac.editorFocusLost = function editorFocusLost_" + ClientID + "() {" + EditorFocusLostScript + "}\n";
if (ShowAutoCompletionAbove)
{
script += AutoCompletionObject + ".forceAbove = true; \n";
}
ScriptHelper.RegisterStartupScript(Page, typeof(string), "MacroEditorScript_" + ClientID, script, true);
ScriptHelper.RegisterStartupScript(Page, typeof(string), "MacroEditorMethodsScript", GetJSMethodsHashTable(), true);
}
}
#endregion
#region ICallbackEventHandler Members
private string wholeText;
private string text = "";
private int position = 0;
private bool isShowContext = false;
public string GetCallbackResult()
{
if (ASCXMode)
{
if (isShowContext)
{
return "";
}
else
{
return AutoCompleteASCX(text, position);
}
}
else
{
if (position == 0)
{
return AutoComplete(text, text.Length, isShowContext, wholeText);
}
else
{
return AutoComplete(text, position, isShowContext, wholeText);
}
}
}
public void RaiseCallbackEvent(string eventArgument)
{
string[] args = eventArgument.Split(new char[] { '\n' });
if (args.Length == 5)
{
isShowContext = (args[3].ToLowerCSafe() == "context");
wholeText = args[4];
position = ValidationHelper.GetInteger(args[2], 0);
if (args[1] == "")
{
text = args[0];
}
else
{
// Normal character - needs to be placed to correct position
text = args[0].Substring(0, position) + args[1] + args[0].Substring(position);
position++;
}
}
}
#endregion
}