/// <summary> /// JavaScript callback. Returns absolute path to the file that is referenced from the file in the /// editor. Implementation copied from the Emmet project source code. /// </summary> public InternalHandle LocateFile( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile locateFile called with invalid number of arguments."); return(engine.CreateValue(false)); } string editorFile = args[0].AsString; string targetFile = args[1].AsString; if (targetFile.StartsWith("HTTP", System.StringComparison.InvariantCultureIgnoreCase)) { return(engine.CreateValue(targetFile)); } string folder = Path.GetDirectoryName(editorFile); do { string retVal = Path.Combine(folder, targetFile); if (File.Exists(retVal)) { return(engine.CreateValue(retVal)); } }while (folder.Length > 3); return(engine.CreateValue(string.Empty)); }
/// <summary> /// JavaScript callback. Returns character indexes of selected text: object with <code>start</code> /// and <code>end</code> properties.If there's no selection, should return object with /// <code>start</code> and <code>end</code> properties referring to current caret position. /// </summary> public InternalHandle GetSelectionRange( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { var selection = _editor.GetSelectionRange(); ObjectHandle retVal = engine.CreateObject(); retVal.SetProperty("start", engine.CreateValue(selection.Start)); retVal.SetProperty("end", engine.CreateValue(selection.End)); return(retVal); }
/// <summary> /// JavaScript callback. Returns current selection. /// </summary> public InternalHandle GetSelection( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string selection = _editor.GetSelection(); if (string.IsNullOrEmpty(selection)) { return(engine.CreateValue(string.Empty)); } return(engine.CreateValue(selection)); }
/// <summary> /// JavaScript callback. Returns file extension in lower case. /// </summary> public InternalHandle GetExtension( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 1) { this.TraceError("IEmmetFile getExt called with invalid number of arguments."); return(engine.CreateValue(false)); } string filePath = args[0].AsString; return(engine.CreateValue(Path.GetExtension(filePath).ToLowerInvariant())); }
public bool Invoke(string method, params object[] args) { try { InternalHandle h = engine.GlobalObject.GetProperty(method); if (h.IsError) { Logger.LogError("[V8] Failed to invoke " + method); return(false); } if (h.IsFunction) { InternalHandle[] handles = new InternalHandle[args.Length]; for (int i = 0; i < args.Length; ++i) { handles.SetValue(engine.CreateValue(args[i]), i); } h.Call(handles); return(true); } } catch (Exception ex) { Logger.LogError("[V8] Plugin error: " + pluginInfo.Name + " (" + pluginInfo.Author + ")\nJavascript error:\n " + ex.Message + "\n----\nStack trace:\n" + ex.StackTrace); Logger.LogError("[V8] Invoking method: " + method + "\nParams (" + args.Length + "):"); for (int i = 0; i < args.Length; ++i) { Logger.LogError(i + "=>" + args[i].GetType()); } return(false); } return(false); }
/// <summary> /// JavaScript callback. Returns current editor's syntax mode. /// </summary> public InternalHandle GetSyntax( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { return(engine.CreateValue(_syntax)); }
/// <summary> /// JavaScript callbacks. Returns the content of the current editor window. /// </summary> public InternalHandle GetContent( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { return(engine.CreateValue(_editor.GetContent())); }
public void UpdateKeyboardState(KeyboardDevice currentKeyboard) { if (v8.IsDisposed) { return; } // Copy keyboard state //TODO: copy state quicker. Should be able to transfer over in O(1) InternalHandle[] keyboard = new InternalHandle[(int)Key.LastKey]; for (int i = 0; i < keyboard.Length; i++) { int k = currentKeyboard[(Key)i] ? 1 : 0; keyboard[i] = v8.CreateValue(k); } v8.GlobalObject.SetProperty("Keyboard", v8.CreateArray(keyboard)); }
/// <summary> /// JavaScript callback. Saves the specified content to the file with the specified name. /// </summary> public InternalHandle Save( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile save called with invalid number of arguments."); return(engine.CreateValue(false)); } string filePath = args[0].AsString; string content = args[1].AsString; File.WriteAllText(filePath, content); return(engine.CreateValue(true)); }
/// <summary> /// JavaScript callback. Set new caret position. /// </summary> public InternalHandle SetCarretPos( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { _editor.SetCaretPosition(args[0].AsInt32); return(engine.CreateValue(true)); }
/// <summary> /// JavaScript callback. Returns the content of the current line. /// </summary> public InternalHandle GetCurrentLine( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string txt = _editor.GetCurrentLine(); return(engine.CreateValue(txt)); }
/// <summary> /// Loads JavaScript extensions and preferences from the specified directory. /// </summary> /// <param name="extensionsDirectory">Pathname of the extensions directory.</param> public void LoadExtensions(string extensionsDirectory) { List <InternalHandle> extensions = new List <InternalHandle>(); var files = Directory.EnumerateFiles(extensionsDirectory, "*.*"); ObjectHandle emmet = _engine.DynamicGlobalObject.window.emmet; foreach (string filePath in files) { if (0 != string.Compare(Path.GetFileName(filePath), PreferencesFileName, true)) { extensions.Add(_engine.CreateValue(filePath)); continue; } string content = File.ReadAllText(filePath); Handle parameter = _engine.CreateValue(content); Handle result = emmet.Call("loadUserData", emmet, parameter); if (result.IsError) { this.TraceError($"Failed to load Emmet preferences from {filePath}: {result.AsString}"); } else { this.Trace($"Successfully loaded Emmet preferences from {filePath}"); } } if (extensions.Count > 0) { var parameter = _engine.CreateArray(extensions.ToArray()); Handle result = emmet.Call("loadExtensions", emmet, parameter); if (result.IsError) { this.TraceError($"Failed to load Emmet extensions: {result.AsString}"); } else { this.Trace($"Successfully loaded {extensions.Count} Emmet extensions"); } } }
public InternalHandle TestJSFunction1(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args) { // ... there can be two different returns based on the call mode! ... // (tip: if a new object is created and returned instead (such as V8ManagedObject or an object derived from it), then that object will be the new object (instead of "_this")) if (isConstructCall) { var obj = engine.GetObject(_this); obj.AsDynamic.x = args[0]; ((dynamic)obj).y = 0; // (native objects in this case will always be V8NativeObject dynamic objects) obj.SetProperty(0, engine.CreateValue(100)); obj.SetProperty("1", engine.CreateValue(100.2)); obj.SetProperty("2", engine.CreateValue("300")); obj.SetProperty(4, engine.CreateValue(new DateTime(2013, 1, 2, 3, 4, 5, DateTimeKind.Utc))); return(_this); } else { return(args.Length > 0 ? args[0] : InternalHandle.Empty); } }
public InternalHandle setInterval(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args) { if (isConstructCall) { return(_this); } else { if (args != null && args.Length == 2) { InternalHandle timerCallback = args[0]; InternalHandle milliseconds = args[1]; if (milliseconds != null && milliseconds.IsInt32 && timerCallback != null && timerCallback.IsFunction) { int ms = milliseconds.AsInt32; string sourceFragment = timerCallback.Value.ToString(); System.Timers.Timer tmr = new System.Timers.Timer(); tmr.Interval = milliseconds.AsInt32; tmr.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => { if (!engine.IsDisposed) { try { engine.Execute("____$=" + sourceFragment + ";____$();", ScriptingEngine.SOURCE_NAME, false); } catch { } } else { tmr.Stop(); } }; tmr.Start(); timers.Add(tmr); Handle timerHandle = engine.CreateValue(timers.Count - 1); return(timerHandle); } } } return(InternalHandle.Empty); }
/// <summary> /// JavaScript callback. Creates absolute path by concatenating two arguments. /// </summary> public InternalHandle CreatePath( V8Engine engine, bool isConstructorCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 2) { this.TraceError("IEmmetFile createPath called with invalid number of arguments."); return(engine.CreateValue(false)); } string parent = args[0].AsString; string fileName = args[1].AsString; if (Path.HasExtension(parent)) { parent = Path.GetDirectoryName(parent); } return(engine.CreateValue(Path.Combine(parent, fileName))); }
/// <summary> /// JavaScript callback. Asks user to enter something. /// </summary> public InternalHandle Prompt( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string input = _editor.Prompt(); if (string.IsNullOrWhiteSpace(input)) { return(engine.CreateNullValue()); } return(engine.CreateValue(input)); }
/// <summary> /// JavaScript callback. Creates selection from <code>start</code> to <code>end</code> character /// indexes. If <code>end</code> is omitted, this method should place caret and <code>start</code> /// index. /// </summary> public InternalHandle CreateSelection( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length == 2) { int start = args[0].AsInt32; int end = args[0].AsInt32; _editor.CreateSelection(start, end); } else { return(SetCarretPos(engine, isConstructCall, self, args)); } return(engine.CreateValue(true)); }
/// <summary> /// Looks for tab stops in the specified content and returns a processed version with expanded /// placeholders and tab stops found. /// </summary> /// <param name="engine">V8 instance with Emmet engine compiled in it.</param> /// <param name="content">Expanded abbreviation content.</param> /// <exception cref="Exception{EmmetEngineExceptionArgs}"> /// Indicates that Emmet engine has failed to parse the specified content. /// </exception> public static TabStopsParser ParseContent(V8Engine engine, string content) { ObjectHandle tabStopsUtil = engine.DynamicGlobalObject.window.emmet.tabStops; Handle extractResult = tabStopsUtil.Call("extract", null, engine.CreateValue(content)); if (extractResult.IsError) { var ex = new EmmetEngineExceptionArgs( "Error while trying to extract tab stops.", extractResult); throw new Exception <EmmetEngineExceptionArgs>(ex); } TabStopsParser retVal = new TabStopsParser(); ObjectHandle tabStopsObj = (ObjectHandle)extractResult; retVal.Content = tabStopsObj.GetProperty(@"text").AsString; ObjectHandle tabStopsList = tabStopsObj.GetProperty(@"tabstops"); // Tab stops should be added before modifying document so that editor can track their position. int tabStopsCount = tabStopsList.ArrayLength; if (tabStopsCount > 0) { retVal.TabStops = new Range[tabStopsCount]; retVal.TabStopGroups = new int[tabStopsCount]; for (int i = 0; i < tabStopsCount; i++) { ObjectHandle tabStopObj = tabStopsList.GetProperty(i.ToString()); int start = tabStopObj.GetProperty("start").AsInt32; int end = tabStopObj.GetProperty("end").AsInt32; int group = tabStopObj.GetProperty("group").AsInt32; retVal.TabStops[i] = new Range(start, end); retVal.TabStopGroups[i] = group; } } return(retVal); }
/// <summary> /// JavaScript callback. Replace editor's content or it's part (from <code>start</code> to /// <code>end</code> index). If <code>value</code> contains <code>caret_placeholder</code>, the editor /// will put caret into this position. If you skip <code>start</code> and <code>end</code> arguments, /// the whole target's content will be replaced with <code>value</code>. /// If you pass <code>start</code> argument only, the <code>value</code> will be placed at /// <code>start</code> string index of current content. /// If you pass <code>start</code> and <code>end</code> arguments, the corresponding substring of /// current target's content will be replaced with <code>value</code>. /// </summary> public InternalHandle ReplaceContent( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { string rawContent = args[0].AsString; int regionStart = args.Length > 1 ? args[1].AsInt32 : -1; int regionLength = args.Length > 2 ? args[2].AsInt32 - regionStart : 0; bool indentContent = args.Length == 4 ? args[3].AsBoolean : true; this.Trace($"Received new content for the editor: {rawContent}"); // Extract tab stops placeholders from the specified content. var tabStops = TabStopsParser.ParseContent(engine, rawContent); _editor.ReplaceContentRange(tabStops.Content, regionStart, regionStart + regionLength); if (null != tabStops.TabStops) { Range[] tabStopRanges = tabStops.TabStops; // Tab stop offsets are relative to the newly generated content ranges, we need to convert // them to the document-wide offsets. if (regionStart > 0) { tabStopRanges = tabStopRanges.Select( item => new Range(item.Start + regionStart, item.End + regionStart)).ToArray(); } _editor.TrackTabStops(tabStopRanges, tabStops.TabStopGroups); } if (indentContent) { _editor.FormatRegion(regionStart, regionStart + tabStops.Content.Length); } return(engine.CreateValue(true)); }
/// <summary> /// JavaScript callback. Reads the specified file content and returns it as string. /// </summary> public InternalHandle Read( V8Engine engine, bool isConstructCall, InternalHandle self, params InternalHandle[] args) { if (args.Length != 3) { this.TraceError("IEmmetFile read called with invalid number of arguments."); return(engine.CreateValue(false)); } string targetFilePath = args[0].AsString; int chunkSize = args[1].AsInt32; ObjectHandle callback = args[2]; if (!File.Exists(targetFilePath)) { this.TraceError($"Emmet requested file {targetFilePath} that does not exist."); callback.StaticCall(engine.CreateValue(true), engine.CreateNullValue()); return(engine.CreateValue(false)); } char[] buf = new char[chunkSize]; FileStream stream = File.OpenRead(targetFilePath); using (StreamReader reader = new StreamReader(stream)) { chunkSize = reader.ReadBlock(buf, 0, chunkSize); } string retVal = new string(buf, 0, chunkSize); callback.StaticCall(engine.CreateValue(false), engine.CreateValue(retVal)); return(engine.CreateValue(true)); }
static void Main(string[] args) { V8Engine v8Engine = new V8Engine(); var result = v8Engine.Execute( @"var gs = (function () { // NOTE: 'result' WILL ALWAYS BE 'undefined' BECAUSE OF 'var' before 'gs'. var myClassObject = function() {}; myClassObject.prototype.getCompanyInfo = function (a, b, c) { var staff = [ { 'name': 'John', 'address': '1 Walk Way', 'dob': 'July '+a+', 1970' }, { 'name': 'Peter', 'address': '241 Otoforwan Rd', 'dob': 'January '+b+', 1953' }, { 'name': 'Marry', 'address': '1 Contrary Lane', 'dob': 'August '+c+', 1984' } ]; var result = { 'isEnabled': true, 'staff': staff }; return result; }; return new myClassObject(); })();" ); //create parameter Handle day1 = v8Engine.CreateValue("1"); Handle day2 = v8Engine.CreateValue("2"); Handle day3 = v8Engine.CreateValue("3"); var gs = v8Engine.GlobalObject.GetProperty("gs"); var resultHandle = gs.Call("getCompanyInfo", null, day1, day2, day3); // NOTE: The object context is already known, so pass 'null' for '_this'. Company completion = v8Engine.GetObject <Company>(resultHandle); //examine result var test0 = resultHandle.GetProperty("isEnable"); Handle test1 = resultHandle.GetProperty("staff"); // NOTE: "ObjectHandle" is a special handle for objects (which also obviously includes arrays, etc.). var arrayLength = test1._.ArrayLength; Handle arrayItem1 = test1._.GetProperty(0); var arrayItem1_name = arrayItem1._.GetProperty("name"); var arrayItem1_address = arrayItem1._.GetProperty("address"); var arrayItem1_dob = (~arrayItem1).GetProperty("dob"); Handle arrayItem2 = test1._.GetProperty(1); // (arrays are treated same as objects here) Handle arrayItem3 = test1._.GetProperty(2); // (arrays are treated same as objects here) // ==================================================================== OR ==================================================================== v8Engine.RegisterType <Company2>(null, true, ScriptMemberSecurity.Locked); // (this line is NOT required, but allows more control over the settings) v8Engine.GlobalObject.SetProperty(typeof(Company2)); // <= THIS IS IMPORTANT! It sets the type on the global object (though you can put this anywhere like any property) var gs2 = v8Engine.Execute( @"(function () { var myClassObject = function() {}; myClassObject.prototype.getCompanyInfo = function (a, b, c) { return new Company2(a, b, c); }; return new myClassObject(); })();" ); var resultHandle2 = gs2.Call("getCompanyInfo", null, day1, day2, day3); // NOTE: The object context is already known, so pass 'null' for '_this'. var objectBindingModeIfNeeded = resultHandle2.BindingMode; var ci2 = (Company2)resultHandle2.BoundObject; // (when a CLR class is bound, it is tracked by the handle in a special way) // ============================================================================================================================================= // Take your pick. ;) System.Console.WriteLine("Script executions completed. Press any key to exit."); System.Console.ReadKey(true); }
/// <summary> /// Name of the X3D Browser /// </summary> public InternalHandle GetName(V8Engine engine, bool isConstructCall, InternalHandle _this, params InternalHandle[] args) { return(engine.CreateValue(AppInfo)); }