Browse Source

use a 1:1 mapping

S. Chris Colbert 7 years ago
parent
commit
faa8d75586
1 changed files with 66 additions and 97 deletions
  1. 66 97
      packages/rendermime/src/rendermime.ts

+ 66 - 97
packages/rendermime/src/rendermime.ts

@@ -7,7 +7,7 @@ import {
 } from '@jupyterlab/services';
 
 import {
-  ArrayExt, find
+  find
 } from '@phosphor/algorithm';
 
 import {
@@ -22,6 +22,10 @@ import {
   IClientSession, ISanitizer, defaultSanitizer
 } from '@jupyterlab/apputils';
 
+import {
+  ReadonlyJSONObject
+} from '@phosphor/coreutils';
+
 
 /**
  * An object which manages mime renderer factories.
@@ -29,6 +33,9 @@ import {
  * This object is used to render mime models using registered mime
  * renderers, selecting the preferred mime renderer to render the
  * model into a widget.
+ *
+ * #### Notes
+ * This class is not intended to be subclassed.
  */
 export
 class RenderMime {
@@ -43,10 +50,10 @@ class RenderMime {
     this.linkHandler = options.linkHandler || null;
     this.sanitizer = options.sanitizer || defaultSanitizer;
 
-    // Initialize the factory map.
+    // Add the initial factories.
     if (options.initialFactories) {
       for (let factory of options.initialFactories) {
-        Private.addToMap(this._factories, factory, 100);
+        this.addFactory(factory);
       }
     }
   }
@@ -70,7 +77,29 @@ class RenderMime {
    * The ordered list of mimeTypes.
    */
   get mimeTypes(): ReadonlyArray<string> {
-    return this._types || (this._types = Private.sortedTypes(this._factories));
+    return this._types || (this._types = Private.sortedTypes(this._ranks));
+  }
+
+  /**
+   * Find the preferred mime type for a mime bundle.
+   *
+   * @param bundle - The bundle of mime data.
+   *
+   * @param safe - Whether to only consider safe factories.
+   *
+   * @returns The preferred mime type from the available factories,
+   *   or `undefined` if the mime type cannot be rendered.
+   */
+  preferredMimeType(bundle: ReadonlyJSONObject, safe: boolean): string | undefined {
+    return find(this.mimeTypes, mt => {
+      if (!(mt in bundle)) {
+        return false;
+      }
+      if (safe && !this._factories[mt].safe) {
+        return false;
+      }
+      return true;
+    });
   }
 
   /**
@@ -97,19 +126,7 @@ class RenderMime {
     };
 
     // Invoke the best factory for the given mime type.
-    return this._factories[mimeType][0].factory.createRenderer(options);
-  }
-
-  /**
-   * Find the preferred mime type for a collection of types.
-   *
-   * @param types - The mime types from which to choose.
-   *
-   * @returns The preferred mime type from the available factories,
-   *   or `undefined` if the mime type cannot be rendered.
-   */
-  preferredMimeType(types: string[]): string | undefined {
-    return find(this.mimeTypes, mt => types.indexOf(mt) !== -1);
+    return this._factories[mimeType].createRenderer(options);
   }
 
   /**
@@ -127,26 +144,24 @@ class RenderMime {
       linkHandler: options.linkHandler || this.linkHandler
     });
 
-    // Update the clone with a copy of the factory map.
-    clone._factories = Private.cloneMap(this._factories);
-
-    // Update the clone with a copy of the sorted types.
-    clone._types = this.mimeTypes.slice();
+    // Clone the internal state.
+    clone._factories = { ...this._factories };
+    clone._ranks = { ...this._ranks };
+    clone._id = this._id;
 
     // Return the cloned object.
     return clone;
   }
 
   /**
-   * Get the renderer factories registered for a mime type.
+   * Get the renderer factory registered for a mime type.
    *
    * @param mimeType - The mime type of interest.
    *
-   * @returns A new array of the factories for the given mime type.
+   * @returns The factory for the mime type, or `undefined`.
    */
-  getFactories(mimeType: string): IRenderMime.IRendererFactory[] {
-    let pairs = this._factories[mimeType];
-    return pairs ? pairs.map(p => p.factory) : [];
+  getFactory(mimeType: string): IRenderMime.IRendererFactory | undefined {
+    return this._factories[mimeType];
   }
 
   /**
@@ -157,36 +172,31 @@ class RenderMime {
    * @param rank - The rank of the renderer. A lower rank indicates
    *   a higher priority for rendering. The default is `100`.
    *
-   *
    * #### Notes
    * The renderer will replace an existing renderer for the given
    * mimeType.
    */
   addFactory(factory: IRenderMime.IRendererFactory, rank = 100): void {
-    Private.addToMap(this._factories, factory, rank);
-    this._types = null;
-  }
-
-  /**
-   * Remove a specific renderer factory.
-   *
-   * @param factory - The renderer factory of interest.
-   */
-  removeFactory(factory: IRenderMime.IRendererFactory): void {
-    Private.removeFromMap(this._factories, factory);
+    for (let mt of factory.mimeTypes) {
+      this._factories[mt] = factory;
+      this._ranks[mt] = { rank, id: this._id++ };
+    }
     this._types = null;
   }
 
   /**
-   * Remove all renderer factories for a mime type.
+   * Remove the factory for a mime type.
    *
    * @param mimeType - The mime type of interest.
    */
-  removeFactories(mimeType: string): void {
+  removeFactory(mimeType: string): void {
     delete this._factories[mimeType];
+    delete this._ranks[mimeType];
     this._types = null;
   }
 
+  private _id = 0;
+  private _ranks: Private.RankMap = {};
   private _types: string[] | null = null;
   private _factories: Private.FactoryMap = {};
 }
@@ -309,76 +319,35 @@ namespace RenderMime {
  */
 namespace Private {
   /**
-   * A pair which holds a rank and renderer factory.
-   */
-  export
-  type FactoryPair = {
-    readonly rank: number;
-    readonly factory: IRenderMime.IRendererFactory;
-  };
-
-  /**
-   * A type alias for a mapping of mime type -> ordered factories.
+   * A type alias for a mime rank and tie-breaking id.
    */
   export
-  type FactoryMap = { [key: string]: FactoryPair[] };
-
-  /**
-   * Add a factory to a factory map.
-   *
-   * This inserts the factory in each bucket according to rank.
-   */
-  export
-  function addToMap(map: FactoryMap, factory: IRenderMime.IRendererFactory, rank: number): void {
-    for (let key of factory.mimeTypes) {
-      let pairs = map[key] || (map[key] = []);
-      let i = ArrayExt.lowerBound(pairs, rank, rankCmp);
-      ArrayExt.insert(pairs, i, { rank, factory });
-    }
-  }
+  type RankPair = { readonly id: number, readonly rank: number };
 
   /**
-   * Remove a factory from a factory map.
-   *
-   * This removes all instances of the factory from the map.
-   *
-   * Empty buckets are also removed.
+   * A type alias for a mapping of mime type -> rank pair.
    */
   export
-  function removeFromMap(map: FactoryMap, factory: IRenderMime.IRendererFactory): void {
-    for (let key in map) {
-      let pairs = map[key];
-      ArrayExt.removeAllWhere(pairs, pair => pair.factory === factory);
-      if (pairs.length === 0) {
-        delete map[key];
-      }
-    }
-  }
+  type RankMap = { [key: string]: RankPair };
 
   /**
-   * Create a deep clone of a factory map.
+   * A type alias for a mapping of mime type -> ordered factories.
    */
   export
-  function cloneMap(map: FactoryMap): FactoryMap {
-    let clone: FactoryMap = {};
-    for (let key in map) {
-      clone[key] = map[key].slice();
-    }
-    return clone;
-  }
+  type FactoryMap = { [key: string]: IRenderMime.IRendererFactory };
 
   /**
    * Get the mime types in the map, ordered by rank.
    */
   export
-  function sortedTypes(map: FactoryMap): string[] {
-    return Object.keys(map).sort((a, b) => map[a][0].rank - map[b][0].rank);
-  }
-
-  /**
-   * A pair/rank comparison function.
-   */
-  function rankCmp(pair: FactoryPair, rank: number): number {
-    return pair.rank - rank;
+  function sortedTypes(map: RankMap): string[] {
+    return Object.keys(map).sort((a, b) => {
+      let p1 = map[a];
+      let p2 = map[b];
+      if (p1.rank !== p2.rank) {
+        return p1.rank - p2.rank;
+      }
+      return p1.id - p2.id;
+    });
   }
 }