TINKERPOP-2054 Added traversalstrategy support to Javascript
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 2f5b903..37df276 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,7 +31,8 @@
 * Deprecated `withGraph()` in favor of `withEmbedded()` on `AnonymousTraversalSource`.
 * Fixed bug in Javascript `Translator` that wasn't handling child traversals well.
 * Fixed an iterator leak in `HasContainer`.
-* Avoid creating unnecessary detached objects in JVM
+* Avoid creating unnecessary detached objects in JVM.
+* Added support for `TraversalStrategy` usage in Javascript.
 * Added `Traversal.getTraverserSetSupplier()` to allow providers to supply their own `TraverserSet` instances.
 
 [[release-3-4-8]]
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index 9359e07..a1bf501 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -57,6 +57,19 @@
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2413[TINKERPOP-2413]
 
+==== TraversalStrategy in Javascript
+
+Using `SubgraphStrategy`, `PartitionStrategy` and other `TraversalStrategy` implementations is now possible in
+Javascript.
+
+[source,javascript]
+----
+const sg = g.withStrategies(
+          new SubgraphStrategy({vertices:__.hasLabel("person"), edges:__.hasLabel("created")}));
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2054[TINKERPOP-2054]
+
 === Upgrading for Providers
 
 ==== Graph System Providers
diff --git a/gremlin-javascript/glv/GraphTraversalSource.template b/gremlin-javascript/glv/GraphTraversalSource.template
index 80476f1..fc55316 100644
--- a/gremlin-javascript/glv/GraphTraversalSource.template
+++ b/gremlin-javascript/glv/GraphTraversalSource.template
@@ -71,7 +71,9 @@
    * @returns {GraphTraversalSource}
    */
   withComputer(graphComputer, workers, result, persist, vertices, edges, configuration) {
-    return this.withStrategies(new VertexProgramStrategy(graphComputer, workers, result, persist, vertices, edges, configuration));
+    return this.withStrategies(new VertexProgramStrategy({graphComputer: graphComputer,
+                           workers: workers, result: result, persist: persist, vertices: vertices, edges: edges,
+                           configuration: configuration}));
   }
 
   /**
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index efa097b..b0e91f2 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -71,7 +71,9 @@
    * @returns {GraphTraversalSource}
    */
   withComputer(graphComputer, workers, result, persist, vertices, edges, configuration) {
-    return this.withStrategies(new VertexProgramStrategy(graphComputer, workers, result, persist, vertices, edges, configuration));
+    return this.withStrategies(new VertexProgramStrategy({graphComputer: graphComputer,
+                           workers: workers, result: result, persist: persist, vertices: vertices, edges: edges,
+                           configuration: configuration}));
   }
 
   /**
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
index 2e234d9..301eb07 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
@@ -22,8 +22,6 @@
  */
 'use strict';
 
-const utils = require('../utils');
-
 class TraversalStrategies {
   /**
    * Creates a new instance of TraversalStrategies.
@@ -61,6 +59,15 @@
 class TraversalStrategy {
 
   /**
+   * @param {String} fqcn fully qualified class name in Java of the strategy
+   * @param {Map} configuration for the strategy
+   */
+  constructor(fqcn, configuration = new Map()) {
+    this.fqcn = fqcn;
+    this.configuration = configuration;
+  }
+
+  /**
    * @abstract
    * @param {Traversal} traversal
    * @returns {Promise}
@@ -70,32 +77,266 @@
   }
 }
 
+class ConnectiveStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy");
+  }
+}
+
+class ElementIdStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy");
+  }
+}
+
+class HaltedTraverserStrategy extends TraversalStrategy {
+
+  /**
+   * @param {String} haltedTraverserFactory full qualified class name in Java of a {@code HaltedTraverserFactory} implementation
+   */
+  constructor(haltedTraverserFactory) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy");
+    if (haltedTraverserFactory !== undefined)
+      this.configuration.set("haltedTraverserFactory", haltedTraverserFactory);
+  }
+}
+
+class OptionsStrategy extends TraversalStrategy {
+  constructor(options = new Map()) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy", options);
+  }
+}
+
+class PartitionStrategy extends TraversalStrategy {
+  /**
+   * @param {Object} [options]
+   * @param {String} [options.partitionKey] name of the property key to partition by
+   * @param {String} [options.writePartition] the value of the currently write partition
+   * @param {Array<String>} [options.readPartitions] list of strings representing the partitions to include for reads
+   * @param {boolean} [options.includeMetaProperties] determines if meta-properties should be included in partitioning defaulting to false
+   */
+  constructor(options) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy");
+    if (options.partitionKey !== undefined)
+      this.configuration.set("partitionKey", options.partitionKey);
+    if (options.writePartition !== undefined)
+      this.configuration.set("writePartition", options.writePartition);
+    if (options.readPartitions !== undefined)
+      this.configuration.set("readPartitions", options.readPartitions);
+    if (options.includeMetaProperties !== undefined)
+      this.configuration.set("includeMetaProperties", options.includeMetaProperties);
+  }
+}
+
+class SubgraphStrategy extends TraversalStrategy {
+  /**
+   * @param {Object} [options]
+   * @param {GraphTraversal} [options.vertices] name of the property key to partition by
+   * @param {GraphTraversal} [options.edges] the value of the currently write partition
+   * @param {GraphTraversal} [options.vertexProperties] list of strings representing the partitions to include for reads
+   * @param {boolean} [options.checkAdjacentVertices] enables the strategy to apply the {@code vertices} filter to the adjacent vertices of an edge.
+   */
+  constructor(options) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy");
+    if (options.vertices !== undefined)
+      this.configuration.set("vertices", options.vertices);
+    if (options.edges !== undefined)
+      this.configuration.set("edges", options.edges);
+    if (options.vertexProperties !== undefined)
+      this.configuration.set("vertexProperties", options.vertexProperties);
+    if (options.checkAdjacentVertices !== undefined)
+      this.configuration.set("checkAdjacentVertices", options.checkAdjacentVertices);
+  }
+}
+
 class VertexProgramStrategy extends TraversalStrategy {
 
-  constructor(graphComputer, workers, persist, result, vertices, edges, configuration) {
-    super();
+  constructor(options) {
+    super("org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy");
     this.configuration = new Map();
-    if (graphComputer !== undefined)
-      this.configuration.set("graphComputer", graphComputer);
-    if (workers !== undefined)
-      this.configuration.set("workers", workers);
-    if (persist !== undefined)
-      this.configuration.set("persist", persist);
-    if (result !== undefined)
-      this.configuration.set("result", result);
-    if (vertices !== undefined)
-      this.configuration.set("vertices", vertices);
-    if (edges !== undefined)
-      this.configuration.set("edges", edges);
-    if (configuration !== undefined)
-      configuration.forEach(function(k,v) {
+    if (options.graphComputer !== undefined)
+      this.configuration.set("graphComputer", options.graphComputer);
+    if (options.workers !== undefined)
+      this.configuration.set("workers", options.workers);
+    if (options.persist !== undefined)
+      this.configuration.set("persist", options.persist);
+    if (options.result !== undefined)
+      this.configuration.set("result", options.result);
+    if (options.vertices !== undefined)
+      this.configuration.set("vertices", options.vertices);
+    if (options.edges !== undefined)
+      this.configuration.set("edges", options.edges);
+    if (options.configuration !== undefined)
+      options.configuration.forEach(function(k,v) {
         this.configuration.set(k, v);
       });
   }
 }
 
+class MatchAlgorithmStrategy extends TraversalStrategy {
+  /**
+   * @param matchAlgorithm
+   */
+  constructor(matchAlgorithm) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy");
+    if (graphComputer !== undefined)
+      this.configuration.set("matchAlgorithm", matchAlgorithm);
+  }
+}
+
+class AdjacentToIncidentStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy");
+  }
+}
+
+class FilterRankingStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy");
+  }
+}
+
+class IdentityRemovalStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy");
+  }
+}
+
+class IncidentToAdjacentStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy");
+  }
+}
+
+class InlineFilterStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy");
+  }
+}
+
+class LazyBarrierStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.LazyBarrierStrategy");
+  }
+}
+
+class MatchPredicateStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy");
+  }
+}
+
+class OrderLimitStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy");
+  }
+}
+
+class PathProcessorStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy");
+  }
+}
+
+class PathRetractionStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy");
+  }
+}
+
+class CountStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy");
+  }
+}
+
+class RepeatUnrollStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy");
+  }
+}
+
+class GraphFilterStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.GraphFilterStrategy");
+  }
+}
+
+class EarlyLimitStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy");
+  }
+}
+
+class LambdaRestrictionStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy");
+  }
+}
+
+class ReadOnlyStrategy extends TraversalStrategy {
+  constructor() {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy");
+  }
+}
+
+class EdgeLabelVerificationStrategy extends TraversalStrategy {
+  /**
+   * @param {boolean} logWarnings determines if warnings should be written to the logger when verification fails
+   * @param {boolean} throwException determines if exceptions should be thrown when verifications fails
+   */
+  constructor(logWarnings = false, throwException=false) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
+    this.configuration.set("logWarnings", logWarnings);
+    this.configuration.set("throwException", throwException);
+  }
+}
+
+class ReservedKeysVerificationStrategy extends TraversalStrategy {
+  /**
+   * @param {boolean} logWarnings determines if warnings should be written to the logger when verification fails
+   * @param {boolean} throwException determines if exceptions should be thrown when verifications fails
+   * @param {Array<String>} keys the list of reserved keys to verify
+   */
+  constructor(logWarnings = false, throwException=false, keys=["id", "label"]) {
+    super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
+    this.configuration.set("logWarnings", logWarnings);
+    this.configuration.set("throwException", throwException);
+    this.configuration.set("keys", keys);
+  }
+}
+
 module.exports = {
   TraversalStrategies: TraversalStrategies,
   TraversalStrategy: TraversalStrategy,
-  VertexProgramStrategy: VertexProgramStrategy
+  // decoration
+  ConnectiveStrategy: ConnectiveStrategy,
+  ElementIdStrategy: ElementIdStrategy,
+  HaltedTraverserStrategy: HaltedTraverserStrategy,
+  OptionsStrategy: OptionsStrategy,
+  PartitionStrategy: PartitionStrategy,
+  SubgraphStrategy: SubgraphStrategy,
+  VertexProgramStrategy: VertexProgramStrategy,
+  // finalization
+  MatchAlgorithmStrategy: MatchAlgorithmStrategy,
+  // optimization
+  AdjacentToIncidentStrategy: AdjacentToIncidentStrategy,
+  FilterRankingStrategy: FilterRankingStrategy,
+  IdentityRemovalStrategy: IdentityRemovalStrategy,
+  IncidentToAdjacentStrategy: IncidentToAdjacentStrategy,
+  InlineFilterStrategy: InlineFilterStrategy,
+  LazyBarrierStrategy: LazyBarrierStrategy,
+  MatchPredicateStrategy: MatchPredicateStrategy,
+  OrderLimitStrategy: OrderLimitStrategy,
+  PathProcessorStrategy: PathProcessorStrategy,
+  PathRetractionStrategy: PathRetractionStrategy,
+  CountStrategy: CountStrategy,
+  RepeatUnrollStrategy: RepeatUnrollStrategy,
+  GraphFilterStrategy: GraphFilterStrategy,
+  EarlyLimitStrategy: EarlyLimitStrategy,
+  // verification
+  EdgeLabelVerificationStrategy: EdgeLabelVerificationStrategy,
+  LambdaRestrictionStrategy: LambdaRestrictionStrategy,
+  ReadOnlyStrategy: ReadOnlyStrategy,
+  ReservedKeysVerificationStrategy: ReservedKeysVerificationStrategy
 };
\ No newline at end of file
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
index 453423a..cfa99dd 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
@@ -23,10 +23,12 @@
 'use strict';
 
 const assert = require('assert');
+const expect = require('chai').expect;
 const { Vertex } = require('../../lib/structure/graph');
 const { traversal } = require('../../lib/process/anonymous-traversal');
-const { GraphTraversalSource } = require('../../lib/process/graph-traversal');
-const { GraphTraversal, statics } = require('../../lib/process/graph-traversal');
+const { GraphTraversalSource, GraphTraversal, statics } = require('../../lib/process/graph-traversal');
+const { SubgraphStrategy, ReadOnlyStrategy,
+        ReservedKeysVerificationStrategy, EdgeLabelVerificationStrategy } = require('../../lib/process/traversal-strategy');
 const Bytecode = require('../../lib/process/bytecode');
 const helper = require('../helper');
 const __ = statics;
@@ -134,7 +136,7 @@
   });
   describe("more complex traversals", function() {
     it('should return paths of value maps', function() {
-      var g = traversal().withRemote(connection);
+      const g = traversal().withRemote(connection);
       return g.V(1).out().in_().limit(1).path().by(__.valueMap('name')).toList().then(function (list) {
         assert.ok(list);
         assert.strictEqual(list.length, 1);
@@ -144,4 +146,42 @@
       });
     });
   });
+  describe("should allow TraversalStrategy definition", function() {
+    it('should allow SubgraphStrategy', function() {
+      const g = traversal().withRemote(connection).withStrategies(
+          new SubgraphStrategy({vertices:__.hasLabel("person"), edges:__.hasLabel("created")}));
+      g.V().count().next().then(function (item1) {
+        assert.ok(item1);
+        assert.strictEqual(item1.value, 4);
+      });
+      g.E().count().next().then(function (item1) {
+        assert.ok(item1);
+        assert.strictEqual(item1.value, 0);
+      });
+      g.V().label().dedup().count().next().then(function (item1) {
+        assert.ok(item1);
+        assert.strictEqual(item1.value, 1);
+      });
+      g.V().label().dedup().next().then(function (item1) {
+        assert.ok(item1);
+        assert.strictEqual(item1.value, "person");
+      });
+    });
+    it('should allow ReadOnlyStrategy', function() {
+      const g = traversal().withRemote(connection).withStrategies(new ReadOnlyStrategy());
+      return g.addV().iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+    });
+    it('should allow ReservedKeysVerificationStrategy', function() {
+      const g = traversal().withRemote(connection).withStrategies(new ReservedKeysVerificationStrategy(false, true));
+      return g.addV().property("id", "please-don't-use-id").iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+    });
+    it('should allow EdgeLabelVerificationStrategy', function() {
+      const g = traversal().withRemote(connection).withStrategies(new EdgeLabelVerificationStrategy(false, true));
+      g.V().outE("created", "knows").count().next().then(function (item1) {
+        assert.ok(item1);
+        assert.strictEqual(item1.value, 6);
+      });
+      return g.V().out().iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+    });
+  });
 });
\ No newline at end of file
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
index 91c903e..a714ef5 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
@@ -20,11 +20,8 @@
 'use strict';
 
 const assert = require('assert');
-const expect = require('chai').expect;
 const graph = require('../../lib/structure/graph');
 const t = require('../../lib/process/traversal');
-const TraversalStrategies = require('../../lib/process/traversal-strategy').TraversalStrategies;
-const Bytecode = require('../../lib/process/bytecode');
 const Translator = require('../../lib/process/translator');
 const graphTraversalModule = require('../../lib/process/graph-traversal');
 const __ = graphTraversalModule.statics;