internal static IDisposable SetupSearchModelEventsObserver(WebBrowser webview, NodeSearchModel model, LibraryViewController controller, ILibraryViewCustomization customization, int throttleTime = 200)
        {
            customization.SpecificationUpdated += (o, e) =>
            {
                controller.RefreshLibraryView(webview);
                controller.CloseNodeTooltip(true);
            };


            var observer = new EventObserver <NodeSearchElement, IEnumerable <NodeSearchElement> >(
                elements => NotifySearchModelUpdate(customization, elements), CollectToList
                ).Throttle(TimeSpan.FromMilliseconds(throttleTime));

            Action <NodeSearchElement> handler = (searchElement) =>
            {
                var libraryViewController = (controller as LibraryViewController);
                if ((libraryViewController != null) && (libraryViewController.disableObserver))
                {
                    return;
                }

                observer.OnEvent(searchElement);
            };
            Action <NodeSearchElement> onRemove = e => handler(null);

            //Set up the event callback
            model.EntryAdded   += handler;
            model.EntryRemoved += onRemove;
            model.EntryUpdated += handler;

            //Set up the dispose event handler
            observer.Disposed += () =>
            {
                model.EntryAdded   -= handler;
                model.EntryRemoved -= onRemove;
                model.EntryUpdated -= handler;
            };

            return(observer);
        }
        private void scriptNotifyHandler(string dataFromjs)
        {
            if (string.IsNullOrEmpty(dataFromjs))
            {
                return;
            }

            try
            {
                //a simple refresh of the libary is requested from js context.
                if (dataFromjs == "requestUpdateLibrary")
                {
                    controller.RefreshLibraryView(controller.browser);
                    return;
                }
                //a more complex action needs to be taken on the c# side.

                /*dataFromjs will be an object like:
                 *
                 * {func:funcName,
                 * data:"string" | data:object[] | bool}
                 */

                var simpleRPCPayload = JsonConvert.DeserializeObject <Dictionary <string, object> >(dataFromjs);
                var funcName         = simpleRPCPayload["func"] as string;
                if (funcName == "createNode")
                {
                    var data = simpleRPCPayload["data"] as string;
                    controller.CreateNode(data);
                }
                else if (funcName == "showNodeTooltip")
                {
                    var data = (simpleRPCPayload["data"] as JArray).Children();
                    controller.ShowNodeTooltip(data.ElementAt(0).Value <string>(), data.ElementAt(1).Value <double>());
                }
                else if (funcName == "closeNodeTooltip")
                {
                    var data = (bool)simpleRPCPayload["data"];
                    controller.CloseNodeTooltip(data);
                }
                else if (funcName == "importLibrary")
                {
                    controller.ImportLibrary();
                }
                else if (funcName == "logEventsToInstrumentation")
                {
                    var data = (simpleRPCPayload["data"] as JArray).Children();
                    controller.LogEventsToInstrumentation(data.ElementAt(0).Value <string>(), data.ElementAt(1).Value <string>());
                }
                else if (funcName == "performSearch")
                {
                    var data         = simpleRPCPayload["data"] as string;
                    var extension    = string.Empty;
                    var searchStream = controller.searchResultDataProvider.GetResource(data, out extension);
                    var searchReader = new StreamReader(searchStream);
                    var results      = searchReader.ReadToEnd();
                    //send back results to libjs
                    controller.browser.
                    InvokeScript("completeSearch", results);
                    searchReader.Dispose();
                }
            }
            catch (Exception e)
            {
                this.controller.LogToDynamoConsole($"Error while parsing command data from javascript{Environment.NewLine}{e.Message}");
            }
        }