Custom LEADVIEW Functionality

Binding

Although LEADVIEW was designed to be an out-of-the-box DocumentViewer solution, we understand that users would like the ability to overwrite, or bind over our UI components. The BindingManager provides a simple, streamlined API for developers to customize LEADVIEW's UI components. This is a global object that exists on the window–meaning bindings can be wired before LEADVIEW is ever instantiated.

The BindingManager works off a key system. Bindings are associated with a string key by calling BindingManager.add(). The BindingManager will accept all key string values. However, a key is only considered supported by LEADVIEW if an internal component consumes the key. This can be checked by calling BindingManager.isSupported().

A full list of the keys that are available for binding in the version of LEADVIEW you are working with can be found by calling BindingManager.getAllAvailableKeys(). It is best to store any key values you are using from LEADVIEW.

About Dialog

A simple example of the BindingManager is to replace the About dialog that is displayed from the Main Menu.

ReplaceAboutBoxTutorial.ts
HTML

ReplaceAboutBoxTutorial.ts

export class ReplaceAboutBoxTutorial { 
   private key = 'aboutMenu'; 
   public constructor() { 
      if (lt.RasterSupport.kernelExpired) 
         lt.RasterSupport.setLicenseUri("https://demo.leadtools.com/licenses/v200/LEADTOOLSEVAL.txt", "EVAL", null); 
 
      this.rebind(); 
   } 
 
   public run = (divID: string): void => { 
      const lv = new lt.LEADVIEW.Viewer(); 
 
      lv.run(null, { 
         'rootDivId': divID 
      }); 
   } 
 
   public rebind = () => { 
      /** 
       * The BindingManager handles overwriting any default LEADVIEW button functionality. 
       * It is best to set up bindings before calling LEADVIEW.run(); 
       */ 
      const manager = lt.LEADVIEW.BindingManager.Instance; 
 
      /** 
       * A full list of all keys available for binding can be retrieved by calling 
       * BindingManager.getAllAvailableKeys(). This will return an array of all available keys. 
       * To see if a key is supported in LEADVIEW, just call isSupported(). 
       * Any string key value can be set in the manager, but the key will only be consumed by internally by LEADVIEW 
       * if isSupported = true. 
       */ 
      if (!manager.isSupported(this.key)) return; 
 
      const binding: lt.LEADVIEW.LVBinding = { 
         onClick: () => alert('About: \n Custom about message!'), 
      }; 
 
      /** 
       * Once the binding is registered in the manager, it will be consumed by LEADVIEW. 
       */ 
      manager.add(this.key, binding, true); 
   } 
} 

HTML

<!doctype html> 
<html lang="en"> 
<title>LV Tutorials | About</title> 
 
<head> 
    <script src="https://code.jquery.com/jquery-2.2.4.min.js" 
        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> 
 
    <script src="../../LT/Leadtools.js"></script> 
    <script src="../../LT/Leadtools.Controls.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Engine.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Designers.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Rendering.Javascript.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Automation.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Main.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Color.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Core.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Effects.js"></script> 
    <script src="../../LT/Leadtools.Document.js"></script> 
    <script src="../../LT/Leadtools.Document.Viewer.js"></script> 
    <script src="../../LT/Leadtools.LEADVIEW.js" defer></script> 
    <link href="../../css/Leadtools.LEADVIEW.css" type="text/css" rel="stylesheet"> 
 
    <!-- All tutorial scripts are bundled, and tutorials classes are placed on the window variable--> 
    <script src="../../bundle.js" type="text/javascript"></script> 
</head> 
 
<body> 
    <div id="runDemo" style="width: 100%; height: 100vh;"></div> 
</body> 
 
<script> 
    window.onload = () => { 
        const example = new window.tutorials.ReplaceAboutBoxTutorial(); 
        example.run("runDemo"); 
    }; 
</script> 
</html> 

LoadFromUri

The above example is rather trivial: just overwriting the click event to display an alert. Using the BindingManager, you can easily replace entire dialogs. For instance, the entire LoadFromUri dialog can be replaced with a minimalistic version, as shown in the following code.

LoadFromUriDlg.ts
ReplaceLoadFromUri.ts
HTML

LoadFromUriDlg.ts

export interface IUriDlg { 
   rootId: string; 
   content: { 
      inputId: string; 
      loadButtonId: string; 
      loadSpinnerId: string; 
   } 
} 
 
export class UriDlg { 
   private props: IUriDlg; 
   private viewer: lt.Document.Viewer.DocumentViewer; 
 
   public constructor(props: IUriDlg) { 
      this.props = props; 
      this.viewer = null; 
 
      $(`#${props.content.loadButtonId}`).click(this.loadButtonClick); 
   } 
 
   loadButtonClick = () => { 
      const url = $(`#${this.props.content.inputId}`).val().toString(); 
      const options = new lt.Document.LoadDocumentOptions(); 
 
      this.toggleVisibility(this.props.content.loadSpinnerId); 
      const promise = lt.Document.DocumentFactory.loadFromUri(url, options); 
      promise.done(document => { 
         this.viewer.setDocument(document) 
         this.cleanup(); 
         this.toggle(); 
      }); 
      promise.fail(this.errorHandler); 
      promise.always(() => this.toggleVisibility(this.props.content.loadSpinnerId)); 
   } 
 
   toggle = () => $(`#${this.props.rootId}`).modal('toggle'); 
   setViewer = (viewer: lt.Document.Viewer.DocumentViewer) => this.viewer = viewer; 
 
   private errorHandler = (jqXHR, statusText, errorThrown) => { 
      const serviceError = lt.Document.ServiceError.parseError(jqXHR, statusText, errorThrown); 
      alert(`There was an error loading in the document. ${serviceError.message}`); 
   } 
 
   private toggleVisibility = (id: string) => $(`#${id}`).toggle(); 
   private cleanup = () => $(`#${this.props.content.inputId}`).val(''); 
} 

ReplaceLoadFromUri.ts

import { UriDlg } from "./LoadFromUriDlg"; 
 
export class ReplaceLoadFromUriTutorial { 
   private keys = [ 
      'openUrlToolbar', 
      'openUrlFileMenu' 
   ] 
   private dlg: UriDlg; 
   public constructor() { 
      if (lt.RasterSupport.kernelExpired) 
         lt.RasterSupport.setLicenseUri("https://demo.leadtools.com/licenses/v200/LEADTOOLSEVAL.txt", "EVAL", null); 
 
      this.dlg = this.initDialog(); 
      this.rebind(); 
   } 
 
   public run = (divID: string): void => { 
      const lv = new lt.LEADVIEW.Viewer(); 
      lv.onActiveViewerChanged = (viewer) => this.dlg.setViewer(viewer); 
 
      lv.run(null, { 
         'rootDivId': divID 
      }); 
   } 
 
   public rebind = () => { 
      /** 
       * The BindingManager handles overwriting any default LEADVIEW button functionality. 
       * It is best to set up bindings before calling LEADVIEW.run(); 
       */ 
      const manager = lt.LEADVIEW.BindingManager.Instance; 
 
 
      const binding: lt.LEADVIEW.LVBinding = { 
         onClick: () => this.dlg.toggle() 
      }; 
 
      /** 
       * Once the binding is registered in the manager, it will be consumed by LEADVIEW. 
       */ 
      this.keys.forEach(key => { 
         /** 
          * A full list of all keys available for binding can be retrieved by calling 
          * BindingManager.getAllAvailableKeys(). This will return an array of all available keys. 
          * To see if a key is supported in LEADVIEW, just call isSupported(). 
          * Any string key value can be set in the manager, but the key will only be consumed internally by LEADVIEW 
          * if isSupported = true. 
         */ 
         if (!manager.isSupported(key)) return; 
 
         manager.add(key, binding, true); 
      }) 
   } 
 
   public initDialog = (): UriDlg => { 
      const props = { 
         rootId: 'uriLoadDlg', 
         content: { 
            inputId: 'uriInput', 
            loadButtonId: 'uriLoad', 
            loadSpinnerId: 'loadSpinner' 
         } 
      }; 
 
      return new UriDlg(props); 
   } 
} 

HTML

<!doctype html> 
<html lang="en"> 
<title>LV Tutorials | LoadFromUri</title> 
 
<head> 
    <script src="https://code.jquery.com/jquery-2.2.4.min.js" 
        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> 
 
    <!--Include Bootstrap dependencies for modal JQuery extensions--> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" 
        integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" 
        crossorigin="anonymous"></script> 
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" 
        integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" 
        crossorigin="anonymous"></script> 
 
    <script src="../../LT/Leadtools.js"></script> 
    <script src="../../LT/Leadtools.Controls.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Engine.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Designers.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Rendering.Javascript.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Automation.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Main.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Color.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Core.js"></script> 
    <script src="../../LT/Leadtools.ImageProcessing.Effects.js"></script> 
    <script src="../../LT/Leadtools.Document.js"></script> 
    <script src="../../LT/Leadtools.Document.Viewer.js"></script> 
    <script src="../../LT/Leadtools.LEADVIEW.js" defer></script> 
    <link href="../../css/Leadtools.LEADVIEW.css" type="text/css" rel="stylesheet"> 
 
    <!-- All tutorial scripts are bundled, and tutorials classes are placed on the window variable--> 
    <script src="../../bundle.js" type="text/javascript"></script> 
</head> 
<style> 
    .pad { 
        padding: 10px; 
    } 
 
    .row { 
        display: flex; 
        flex-direction: row; 
    } 
 
    .fill { 
        width: 90%; 
    } 
 
    .vcenter { 
        margin-top: auto; 
        margin-bottom: auto; 
    } 
 
    .hidden { 
        visibility: hidden; 
    } 
</style> 
 
<body> 
    <div id="runDemo" style="width: 100%; height: 100vh;"></div> 
    <div class="modal fade" id="uriLoadDlg" role="dialog" aria-hidden="true"> 
        <div class="modal-dialog" role="document"> 
            <div class="modal-content bg-dark row"> 
                <div class="input-group pad fill"> 
                    <input type="text" class="form-control bg-dark text-light" placeholder="" id="uriInput"> 
                    <div class="input-group-append"> 
                        <button class="btn btn-outline-secondary text-light" type="button" id="uriLoad">Load</button> 
                    </div> 
                </div> 
 
                <div class="spinner-border vcenter hidden" role="status" id="loadSpinner"> 
                    <span class="sr-only">Loading...</span> 
                </div> 
            </div> 
        </div> 
    </div> 
</body> 
 
<script> 
    window.onload = () => { 
        const example = new window.tutorials.ReplaceLoadFromUriTutorial(); 
        example.run("runDemo"); 
    }; 
</script> 
 
</html> 

Injection

Simply overwriting bindings is not enough. The LEADVIEW SDK has incorporated the concept of an injector—these are just HTML elements located on different components throughout LEADVIEW. The InjectionManager is responsible for the lifecycle of all injectors throughout LEADVIEW. The InjectionManager exposes a single method: inject(), which will fire every time the lifecycle for an injector changes. Just like the BindingManager, the InjectionManager exists as a global variable on the window. Injections should be set up before LEADVIEW is instantiated.

The inject() method will be passed a single argument that implements InjectionArgs.

Property Description
state Describes the lifecycle state of the injector.
type The type of injector.
data Holds the data for the injector. Cast this according to the injector type. For instance, if the type is type.toolbar, data should be cast to a ToolbarInjector instance.

Document Counter

Using the InjectionManager, it is very straightforward to add custom UI components. For instance, the following code adds a counter to track how many documents have been loaded.

DocumentCounter.ts
CounterTutorial.ts
HTML

DocumentCounter.ts

export interface DocumentCounterProps { 
   id: string; 
   viewer: lt.Document.Viewer.DocumentViewer; 
} 
 
export class DocumentCounter{ 
   private counter: number; 
   private viewer: lt.Document.Viewer.DocumentViewer; 
   private container: HTMLElement; 
 
   constructor(props: DocumentCounterProps) { 
      this.counter = 0; 
 
      if(props.viewer) 
         props.viewer.operation.add(this.operation); 
 
      this.viewer = props.viewer; 
      this.container = this.createCounter(props.id); 
   } 
 
   updateViewer = (viewer: lt.Document.Viewer.DocumentViewer) => { 
      if(this.viewer === viewer) return; 
 
      if(this.viewer) 
         this.viewer.operation.remove(this.operation); 
 
      if(viewer) 
         viewer.operation.add(this.operation); 
 
      this.viewer = viewer; 
   } 
 
   dispose = () => { 
      if(this.viewer) 
         this.viewer.operation.remove(this.operation); 
 
      this.viewer = null; 
      this.counter = 0; 
   } 
 
 
   private operation = (sender, e: lt.Document.Viewer.DocumentViewerOperationEventArgs) => { 
      if(e.operation === lt.Document.Viewer.DocumentViewerOperation.setDocument && e.isPostOperation) { 
         this.counter ++; 
         if(this.container) 
            this.container.innerText = `Docs Loaded: ${this.counter}`; 
      } 
   } 
 
   private createCounter = (id: string) => { 
      const container = document.getElementById(id); 
      container.style.width = '130px'; 
      container.style.height = '20px'; 
      container.style.marginTop = 'auto'; 
      container.style.marginBottom = 'auto'; 
      container.style.marginRight = '10px'; 
      container.style.color = 'inherit'; 
      container.style.backgroundColor = 'inherit'; 
 
      container.innerText = `Docs Loaded: ${this.counter}`; 
 
      return container; 
   } 
} 

CounterTutorial.ts

import { DocumentCounter } from "./DocumentCounter"; 
 
export class DocumentCounterTutorial { 
   private counter: DocumentCounter = null; 
   private viewer: lt.Document.Viewer.DocumentViewer = null; 
 
   public constructor() { 
      if (lt.RasterSupport.kernelExpired) 
         lt.RasterSupport.setLicenseUri("https://demo.leadtools.com/licenses/v200/LEADTOOLSEVAL.txt", "EVAL", null); 
 
      lt.LEADVIEW.InjectionManager.Instance.inject = this.inject; 
   } 
 
   public run = (divID: string): void => { 
      const lv = new lt.LEADVIEW.Viewer(); 
 
      lv.onActiveViewerChanged = viewer => { 
         this.viewer = viewer; 
         if (this.counter) 
            this.counter.updateViewer(viewer) 
      }; 
 
      lv.run(null, { 
         'rootDivId': divID 
      }); 
   } 
 
   public inject = (args: lt.LEADVIEW.InjectionArgs) => { 
      if (args.type !== lt.LEADVIEW.InjectionType.toolbar) 
         return; 
 
      const injector = args.data as lt.LEADVIEW.ToolbarInjector; 
      switch (args.state) { 
         case lt.LEADVIEW.InjectionState.mounting: 
            this.counter = new DocumentCounter({ 
               id: injector.start, 
               viewer: this.viewer 
            }); 
            break; 
         case lt.LEADVIEW.InjectionState.unmounting: 
            if (this.counter) 
               this.counter.dispose(); 
 
            this.counter = null; 
            break; 
      } 
   } 
} 

HTML

<!doctype html> 
<html lang="en"> 
<title>LV Tutorials | LoadFromUri</title> 
 
<head> 
    <script src="https://code.jquery.com/jquery-2.2.4.min.js" 
        integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" 
        integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" 
        crossorigin="anonymous"></script> 
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" 
        integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" 
        crossorigin="anonymous"></script> 
 
    <script src="../../LT/Leadtools.js"></script> 
    <script src="../../LT/Leadtools.Controls.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Engine.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Designers.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Rendering.Javascript.js"></script> 
    <script src="../../LT/Leadtools.Annotations.Automation.js"></script> 
    <script src="../../LT/Leadtools.Document.js"></script> 
    <script src="../../LT/Leadtools.Document.Viewer.js"></script> 
    <script src="../../LT/Leadtools.LEADVIEW.js" defer></script> 
    <link href="../../css/Leadtools.LEADVIEW.css" type="text/css" rel="stylesheet"> 
 
    <!-- All tutorial scripts are bundled, and tutorials classes are placed on the window variable--> 
    <script src="../../bundle.js" type="text/javascript"></script> 
</head> 
 
<body> 
    <div id="runDemo" style="width: 100%; height: 100vh;"></div> 
</body> 
 
<script> 
    window.onload = () => { 
        const example = new window.tutorials.DocumentCounterTutorial(); 
        example.run("runDemo"); 
    }; 
</script> 
 
</html> 

See Also

Introduction

Help Version 22.0.2023.1.18
Products | Support | Contact Us | Intellectual Property Notices
© 1991-2023 LEAD Technologies, Inc. All Rights Reserved.

LEADTOOLS HTML5 JavaScript
Products | Support | Contact Us | Intellectual Property Notices
© 1991-2023 LEAD Technologies, Inc. All Rights Reserved.