<commit_before>//Author-Albert Sanchez
//Description-Create a Metric Hex Nut
function run(context) {
  "use strict";
  var metricHexNutMatrix = [
    {nominalSize: 'M1.6', d: 1.6, af: 3.2, k: 1.3},
    {nominalSize: 'M2',   d: 2,   af: 4,   k: 1.6},
    {nominalSize: 'M2.5', d: 2.5, af: 5,   k: 2},
    {nominalSize: 'M3',   d: 3,   af: 5.5, k: 2.4},
    {nominalSize: 'M4',   d: 4,   af: 7,   k: 3.2},
    {nominalSize: 'M5',   d: 5,   af: 8,   k: 4.7},
    {nominalSize: 'M6',   d: 6,   af: 10,  k: 5.2},
    {nominalSize: 'M10',  d: 10,  af: 16,  k: 8.4},
    {nominalSize: 'M12',  d: 12,  af: 18,  k: 10.8},
    {nominalSize: 'M14',  d: 14,  af: 21,  k: 12.8},
    {nominalSize: 'M16',  d: 16,  af: 24,  k: 14.8},
    {nominalSize: 'M20',  d: 20,  af: 30,  k: 18},
    {nominalSize: 'M24',  d: 24,  af: 36,  k: 21.5},
    {nominalSize: 'M30',  d: 30,  af: 46,  k: 25.6},
    {nominalSize: 'M36',  d: 36,  af: 55,  k: 31},
    {nominalSize: 'M42',  d: 42,  af: 65,  k: 34},
    {nominalSize: 'M48',  d: 48,  af: 75,  k: 38},
    {nominalSize: 'M56',  d: 56,  af: 85,  k: 45}
  ];
  for (var i = 0; i < metricHexNutMatrix.length; i++) {
    metricHexNutMatrix[i].d = metricHexNutMatrix[i].d / 10;
    metricHexNutMatrix[i].ac = (2 * metricHexNutMatrix[i].af) / Math.sqrt(3);
    metricHexNutMatrix[i].af = metricHexNutMatrix[i].af / 10;
    metricHexNutMatrix[i].ac = metricHexNutMatrix[i].ac / 10;
    metricHexNutMatrix[i].k = metricHexNutMatrix[i].k / 10;
  }
  var app = adsk.core.Application.get(), ui;
  if (app) {
    ui = app.userInterface;
  }
  var product = app.activeProduct;
  var design = adsk.fusion.Design(product);
  if (!design) {
    ui.messageBox('It is not supported in current workspace, please change to MODEL workspace and try again.');
    adsk.terminate();
    return;
  }
  var newComp;
  function createNewComponent() {
    var rootComp = design.rootComponent;
    var allOccs = rootComp.occurrences;
    var newOcc = allOccs.addNewComponent(adsk.core.Matrix3D.create());
    newComp = newOcc.component;
  };
  var createCommandDefinition = function() {
    var commandDefinitions = ui.commandDefinitions;
    var cmDef = commandDefinitions.itemById('MetricHexNut');
    if (!cmDef) {
      cmDef = commandDefinitions.addButtonDefinition('MetricHexNut', 'Create Metric Hex Nut', 'Create a Metric Hex Nut.', './resources');
    }
    return cmDef;
  };
  var onCommandCreated = function(args) {
    try {
      var command = args.command;
      command.isRepeatable = false;
      command.execute.add(onCommandExecuted);
      command.executePreview.add(onCommandExecuted);
      command.destroy.add(function() {
        adsk.terminate();
      });
      var inputs = command.commandInputs;
      inputs.addImageCommandInput("image", "", "resources/example.png");
      inputs.addStringValueInput('metricHexNutName', 'Hex Nut Name', 'Metric Hex Nut');
      var initNominalSize = inputs.addDropDownCommandInput('nominalSize', 'Nominal Size', adsk.core.DropDownStyles.TextListDropDownStyle);
      initNominalSize.listItems.add(metricHexNutMatrix[0].nominalSize, true, '');
      for (var i = 1; i < metricHexNutMatrix.length; i++) {
        initNominalSize.listItems.add(metricHexNutMatrix[i].nominalSize, false, '');
      }
      initNominalSize.listItems.add('Custom', false, '');
      var initD = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].d);
      inputs.addValueInput('D', '(d) Diameter', 'cm', initD);
      var initAf = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].af);
      inputs.addValueInput('Af', '(af) Width Across Flats', 'cm', initAf);
      var initWidthAc = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].ac);
      inputs.addValueInput('Ac', '(ac) Width Across Corners', 'cm', initWidthAc);
      var initK = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].k);
      inputs.addValueInput('K', '(k) Thickness', 'cm', initK);
    }
    catch (e) {
      ui.messageBox('Failed to create command : ' + (e.description ? e.description : e));
    }
  };
  var onCommandExecuted = function(args) {
    try {
      var unitsMgr = app.activeProduct.unitsManager;
      var command = adsk.core.Command(args.firingEvent.sender);
      var inputs = command.commandInputs;
      var metricHexNut = new MetricHexNut();
      var selectedItemObject = new Object();
      for (var i = 0; i < inputs.count; i++) {
        var input = inputs.item(i);
        if (input.id === 'nominalSize') {
          var selectedItem = input.selectedItem.name;
          for (var j = 0; j < metricHexNutMatrix.length; j++) {
            if (selectedItem == metricHexNutMatrix[j].nominalSize) {
              selectedItemObject = metricHexNutMatrix[j];
              break;
            }
          }
          break;
        }
      }
      for (var i = 0; i < inputs.count; i++) {
        var input = inputs.item(i);
        if (input.id === 'metricHexNutName') {
          metricHexNut.metricHexNutName = input.value;
        } else if (input.id === 'D') {
          input.value = selectedItemObject.d;
          metricHexNut.d = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'Af') {
          input.value = selectedItemObject.af;
          metricHexNut.af = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'Ac') {
          input.value = selectedItemObject.ac;
          metricHexNut.ac = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'K') {
          input.value = selectedItemObject.k;
          metricHexNut.k = unitsMgr.evaluateExpression(input.expression, "cm");
        }
      }
      metricHexNut.buildMetricHexNut();
      args.isValidResult = true;
    }
    catch (e) {
      ui.messageBox('Failed to create Metric Hex Nut: ' + (e.description ? e.description : e));
    }
  };
  var MetricHexNut = function() {
    this.metricHexNutName = 'Metric Hex Nut';
    this.d = metricHexNutMatrix[0].d;
    this.af = metricHexNutMatrix[0].af;
    this.ac = metricHexNutMatrix[0].ac;
    this.k = metricHexNutMatrix[0].k;
    this.thread;
    this.buildMetricHexNut = function() {
      createNewComponent();
      if (!newComp) {
        ui.messageBox('New component failed to create', 'New Component Failed');
        adsk.terminate();
        return;
      }
      var sketches = newComp.sketches;
      var sketch = sketches.add(newComp.xYConstructionPlane);
      var center = adsk.core.Point3D.create(0, 0, 0);
      var vertices = [];
      for (var i = 0; i < 6; i++) {
        var vertex = adsk.core.Point3D.create((this.af / Math.sqrt(3)) * Math.cos(Math.PI * i / 3 + (30 * (Math.PI / 180))), (this.af / Math.sqrt(3)) * Math.sin(Math.PI * i / 3 + (30 * (Math.PI / 180))), 0);
        vertices.push(vertex);
      }
      for (i = 0; i < 6; i++) {
        sketch.sketchCurves.sketchLines.addByTwoPoints(vertices[(i + 1) % 6], vertices[i]);
      }
      var prof = sketch.profiles.item(0);
      var extrudes = newComp.features.extrudeFeatures;
      var extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation);
      var distance = adsk.core.ValueInput.createByReal(this.k);
      extInput.setDistanceExtent(false, distance);
      var ext = extrudes.add(extInput);
      var body = ext.bodies.item(0);
      body.name = this.metricHexNutName;
      var endFaces = ext.endFaces;
      var endFace = endFaces.item(0);
      var planes = newComp.constructionPlanes;
      var planeInput = planes.createInput();
      var offsetVal = adsk.core.ValueInput.createByString('0 cm');
      planeInput.setByOffset(endFace, offsetVal);
      var offsetPlane = planes.add(planeInput);
      var offsetSketch = sketches.add(offsetPlane);
      var offsetSketchPoints = offsetSketch.sketchPoints;
      var centerHole = offsetSketchPoints.add(center);
      var ptColl = adsk.core.ObjectCollection.create();
      ptColl.add(centerHole);
      var holes = newComp.features.holeFeatures;
      var holeInput = holes.createSimpleInput(adsk.core.ValueInput.createByReal(this.d));
      holeInput.setPositionBySketchPoints(ptColl);
      holeInput.setDistanceExtent(distance);
      holes.add(holeInput);
      var revolveSketch = sketches.add(newComp.xZConstructionPlane);
      var radius = this.ac / 2;
      var point1 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius * Math.cos(Math.PI / 6), 0, 0));
      var point2 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius, 0, 0));
      var point3 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(point2.x, 0, (point2.x - point1.x) * Math.tan(30 * (Math.PI / 180))));
      var point4 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius * Math.cos(Math.PI / 6), 0, this.k - center.y));
      var point5 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius, 0, this.k - center.y));
      var point6 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(point2.x, 0, this.k - center.y - (point5.x - point4.x) * Math.tan(30 * (Math.PI / 180))));
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point1, point2);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point2, point3);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point3, point1);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point4, point5);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point5, point6);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point6, point4);
      var revolves = newComp.features.revolveFeatures;
      var revInputOne = revolves.createInput(revolveSketch.profiles.item(0), newComp.zConstructionAxis, adsk.fusion.FeatureOperations.CutFeatureOperation);
      var revInputTwo = revolves.createInput(revolveSketch.profiles.item(1), newComp.zConstructionAxis, adsk.fusion.FeatureOperations.CutFeatureOperation);
      var angle = adsk.core.ValueInput.createByReal(Math.PI * 2);
      revInputOne.setAngleExtent(false, angle);
      revInputTwo.setAngleExtent(false, angle);
      revolves.add(revInputOne);
      revolves.add(revInputTwo);
      var threadFeatures = newComp.features.threadFeatures;
      var threadDataQuery = threadFeatures.threadDataQuery;
      var defaultThreadType = threadDataQuery.defaultMetricThreadType;
      var designate = new Object();
      var threadClass = new Object();
      var isOk = threadDataQuery.recommendThreadData(this.d, true, defaultThreadType, designate, threadClass);
      if (isOk) {
        this.thread = designate.value;
        var hole = holes.item(0);
        var sideFace = hole.sideFaces.item(0);
        var threadInfo = threadFeatures.createThreadInfo(true, defaultThreadType, designate.value, threadClass.value);
        var threadInput = threadFeatures.createInput(sideFace, threadInfo);
        threadInput.threadLength = adsk.core.ValueInput.createByReal(this.k);
        threadFeatures.add(threadInput);
      }
    };
  };
  try {
    if (adsk.debug === true) {
      debugger;
    }
    var command = createCommandDefinition();
    var commandCreatedEvent = command.commandCreated;
    commandCreatedEvent.add(onCommandCreated);
    command.execute();
  }
  catch (e) {
    if (ui) {
      ui.messageBox('Failed : ' + (e.description ? e.description : e));
    }
    adsk.terminate();
  }
}
<commit_msg>added fix that prevented some threads to occur<commit_after>//Author-Albert Sanchez
//Description-Create a Metric Hex Nut
function run(context) {
  "use strict";
  var metricHexNutMatrix = [
    {nominalSize: 'M1.6', d: 1.6, af: 3.2, k: 1.3},
    {nominalSize: 'M2',   d: 2,   af: 4,   k: 1.6},
    {nominalSize: 'M2.5', d: 2.5, af: 5,   k: 2},
    {nominalSize: 'M3',   d: 3,   af: 5.5, k: 2.4},
    {nominalSize: 'M4',   d: 4,   af: 7,   k: 3.2},
    {nominalSize: 'M5',   d: 5,   af: 8,   k: 4.7},
    {nominalSize: 'M6',   d: 6,   af: 10,  k: 5.2},
    {nominalSize: 'M10',  d: 10,  af: 16,  k: 8.4},
    {nominalSize: 'M12',  d: 12,  af: 18,  k: 10.8},
    {nominalSize: 'M14',  d: 14,  af: 21,  k: 12.8},
    {nominalSize: 'M16',  d: 16,  af: 24,  k: 14.8},
    {nominalSize: 'M20',  d: 20,  af: 30,  k: 18},
    {nominalSize: 'M24',  d: 24,  af: 36,  k: 21.5},
    {nominalSize: 'M30',  d: 30,  af: 46,  k: 25.6},
    {nominalSize: 'M36',  d: 36,  af: 55,  k: 31},
    {nominalSize: 'M42',  d: 42,  af: 65,  k: 34},
    {nominalSize: 'M48',  d: 48,  af: 75,  k: 38},
    {nominalSize: 'M56',  d: 56,  af: 85,  k: 45}
  ];
  for (var i = 0; i < metricHexNutMatrix.length; i++) {
    metricHexNutMatrix[i].d = metricHexNutMatrix[i].d / 10;
    metricHexNutMatrix[i].ac = (2 * metricHexNutMatrix[i].af) / Math.sqrt(3);
    metricHexNutMatrix[i].af = metricHexNutMatrix[i].af / 10;
    metricHexNutMatrix[i].ac = metricHexNutMatrix[i].ac / 10;
    metricHexNutMatrix[i].k = metricHexNutMatrix[i].k / 10;
  }
  var app = adsk.core.Application.get(), ui;
  if (app) {
    ui = app.userInterface;
  }
  var product = app.activeProduct;
  var design = adsk.fusion.Design(product);
  if (!design) {
    ui.messageBox('It is not supported in current workspace, please change to MODEL workspace and try again.');
    adsk.terminate();
    return;
  }
  var newComp;
  function createNewComponent() {
    var rootComp = design.rootComponent;
    var allOccs = rootComp.occurrences;
    var newOcc = allOccs.addNewComponent(adsk.core.Matrix3D.create());
    newComp = newOcc.component;
  };
  var createCommandDefinition = function() {
    var commandDefinitions = ui.commandDefinitions;
    var cmDef = commandDefinitions.itemById('MetricHexNut');
    if (!cmDef) {
      cmDef = commandDefinitions.addButtonDefinition('MetricHexNut', 'Create Metric Hex Nut', 'Create a Metric Hex Nut.', './resources');
    }
    return cmDef;
  };
  var onCommandCreated = function(args) {
    try {
      var command = args.command;
      command.isRepeatable = false;
      command.execute.add(onCommandExecuted);
      command.executePreview.add(onCommandExecuted);
      command.destroy.add(function() {
        adsk.terminate();
      });
      var inputs = command.commandInputs;
      inputs.addImageCommandInput("image", "", "resources/example.png");
      inputs.addStringValueInput('metricHexNutName', 'Hex Nut Name', 'Metric Hex Nut');
      var initNominalSize = inputs.addDropDownCommandInput('nominalSize', 'Nominal Size', adsk.core.DropDownStyles.TextListDropDownStyle);
      initNominalSize.listItems.add(metricHexNutMatrix[0].nominalSize, true, '');
      for (var i = 1; i < metricHexNutMatrix.length; i++) {
        initNominalSize.listItems.add(metricHexNutMatrix[i].nominalSize, false, '');
      }
      initNominalSize.listItems.add('Custom', false, '');
      var initD = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].d);
      inputs.addValueInput('D', '(d) Diameter', 'cm', initD);
      var initAf = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].af);
      inputs.addValueInput('Af', '(af) Width Across Flats', 'cm', initAf);
      var initWidthAc = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].ac);
      inputs.addValueInput('Ac', '(ac) Width Across Corners', 'cm', initWidthAc);
      var initK = adsk.core.ValueInput.createByReal(metricHexNutMatrix[0].k);
      inputs.addValueInput('K', '(k) Thickness', 'cm', initK);
    }
    catch (e) {
      ui.messageBox('Failed to create command : ' + (e.description ? e.description : e));
    }
  };
  var onCommandExecuted = function(args) {
    try {
      var unitsMgr = app.activeProduct.unitsManager;
      var command = adsk.core.Command(args.firingEvent.sender);
      var inputs = command.commandInputs;
      var metricHexNut = new MetricHexNut();
      var selectedItemObject = new Object();
      for (var i = 0; i < inputs.count; i++) {
        var input = inputs.item(i);
        if (input.id === 'nominalSize') {
          var selectedItem = input.selectedItem.name;
          for (var j = 0; j < metricHexNutMatrix.length; j++) {
            if (selectedItem == metricHexNutMatrix[j].nominalSize) {
              selectedItemObject = metricHexNutMatrix[j];
              break;
            }
          }
          break;
        }
      }
      for (var i = 0; i < inputs.count; i++) {
        var input = inputs.item(i);
        if (input.id === 'metricHexNutName') {
          metricHexNut.metricHexNutName = input.value;
        } else if (input.id === 'D') {
          input.value = selectedItemObject.d;
          metricHexNut.d = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'Af') {
          input.value = selectedItemObject.af;
          metricHexNut.af = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'Ac') {
          input.value = selectedItemObject.ac;
          metricHexNut.ac = unitsMgr.evaluateExpression(input.expression, "cm");
        } else if (input.id === 'K') {
          input.value = selectedItemObject.k;
          metricHexNut.k = unitsMgr.evaluateExpression(input.expression, "cm");
        }
      }
      metricHexNut.buildMetricHexNut();
      args.isValidResult = true;
    }
    catch (e) {
      ui.messageBox('Failed to create Metric Hex Nut: ' + (e.description ? e.description : e));
    }
  };
  var MetricHexNut = function() {
    this.metricHexNutName = 'Metric Hex Nut';
    this.d = metricHexNutMatrix[0].d;
    this.af = metricHexNutMatrix[0].af;
    this.ac = metricHexNutMatrix[0].ac;
    this.k = metricHexNutMatrix[0].k;
    this.thread;
    this.buildMetricHexNut = function() {
      createNewComponent();
      if (!newComp) {
        ui.messageBox('New component failed to create', 'New Component Failed');
        adsk.terminate();
        return;
      }
      var sketches = newComp.sketches;
      var sketch = sketches.add(newComp.xYConstructionPlane);
      var center = adsk.core.Point3D.create(0, 0, 0);
      var vertices = [];
      for (var i = 0; i < 6; i++) {
        var vertex = adsk.core.Point3D.create((this.af / Math.sqrt(3)) * Math.cos(Math.PI * i / 3 + (30 * (Math.PI / 180))), (this.af / Math.sqrt(3)) * Math.sin(Math.PI * i / 3 + (30 * (Math.PI / 180))), 0);
        vertices.push(vertex);
      }
      for (i = 0; i < 6; i++) {
        sketch.sketchCurves.sketchLines.addByTwoPoints(vertices[(i + 1) % 6], vertices[i]);
      }
      var prof = sketch.profiles.item(0);
      var extrudes = newComp.features.extrudeFeatures;
      var extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation);
      var distance = adsk.core.ValueInput.createByReal(this.k);
      extInput.setDistanceExtent(false, distance);
      var ext = extrudes.add(extInput);
      var body = ext.bodies.item(0);
      body.name = this.metricHexNutName;
      var endFaces = ext.endFaces;
      var endFace = endFaces.item(0);
      var planes = newComp.constructionPlanes;
      var planeInput = planes.createInput();
      var offsetVal = adsk.core.ValueInput.createByString('0 cm');
      planeInput.setByOffset(endFace, offsetVal);
      var offsetPlane = planes.add(planeInput);
      var offsetSketch = sketches.add(offsetPlane);
      var offsetSketchPoints = offsetSketch.sketchPoints;
      var centerHole = offsetSketchPoints.add(center);
      var ptColl = adsk.core.ObjectCollection.create();
      ptColl.add(centerHole);
      var holes = newComp.features.holeFeatures;
      var holeInput = holes.createSimpleInput(adsk.core.ValueInput.createByReal(this.d));
      holeInput.setPositionBySketchPoints(ptColl);
      holeInput.setDistanceExtent(distance);
      holes.add(holeInput);
      var revolveSketch = sketches.add(newComp.xZConstructionPlane);
      var radius = this.ac / 2;
      var point1 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius * Math.cos(Math.PI / 6), 0, 0));
      var point2 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius, 0, 0));
      var point3 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(point2.x, 0, (point2.x - point1.x) * Math.tan(30 * (Math.PI / 180))));
      var point4 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius * Math.cos(Math.PI / 6), 0, this.k - center.y));
      var point5 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(radius, 0, this.k - center.y));
      var point6 = revolveSketch.modelToSketchSpace(adsk.core.Point3D.create(point2.x, 0, this.k - center.y - (point5.x - point4.x) * Math.tan(30 * (Math.PI / 180))));
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point1, point2);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point2, point3);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point3, point1);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point4, point5);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point5, point6);
      revolveSketch.sketchCurves.sketchLines.addByTwoPoints(point6, point4);
      var revolves = newComp.features.revolveFeatures;
      var revInputOne = revolves.createInput(revolveSketch.profiles.item(0), newComp.zConstructionAxis, adsk.fusion.FeatureOperations.CutFeatureOperation);
      var revInputTwo = revolves.createInput(revolveSketch.profiles.item(1), newComp.zConstructionAxis, adsk.fusion.FeatureOperations.CutFeatureOperation);
      var angle = adsk.core.ValueInput.createByReal(Math.PI * 2);
      revInputOne.setAngleExtent(false, angle);
      revInputTwo.setAngleExtent(false, angle);
      revolves.add(revInputOne);
      revolves.add(revInputTwo);
      var threadFeatures = newComp.features.threadFeatures;
      var threadDataQuery = threadFeatures.threadDataQuery;
      var defaultThreadType = threadDataQuery.defaultMetricThreadType;
      var designate = new Object();
      var threadClass = new Object();
      var isOk = threadDataQuery.recommendThreadData(this.d, true, defaultThreadType, designate, threadClass);
      if (isOk) {
        this.thread = designate.value;
        var hole = holes.item(0);
        var sideFace = hole.sideFaces.item(0);
        var threadInfo = threadFeatures.createThreadInfo(true, defaultThreadType, designate.value, threadClass.value);
        var threadInput = threadFeatures.createInput(sideFace, threadInfo);
        threadInput.isFullLength = false;
        threadInput.threadLength = adsk.core.ValueInput.createByReal(this.k - (this.k * 0.01));
        threadFeatures.add(threadInput);
      }
    };
  };
  try {
    if (adsk.debug === true) {
      debugger;
    }
    var command = createCommandDefinition();
    var commandCreatedEvent = command.commandCreated;
    commandCreatedEvent.add(onCommandCreated);
    command.execute();
  }
  catch (e) {
    if (ui) {
      ui.messageBox('Failed : ' + (e.description ? e.description : e));
    }
    adsk.terminate();
  }
}
<|endoftext|>


<commit_before>export function enclosureDbAttachmentRemoval(encDb) {
  return encDb.allDocs({include_docs: true}).then(docs => {
    return Promise.all(docs.rows.map(doc => {
      if (doc.doc._attachments) {
        return Promise.all(Object.keys(doc.doc._attachments).map(att => {
          encDb.removeAttachment(doc.doc._id, att, doc.doc._rev);
        }));
      }
    }));
  }).then(() => {
    return encDb.compact();
  }).catch(err => {
    console.log(err);
  });
}
<commit_msg>improved migration function<commit_after>export function enclosureDbAttachmentRemoval(encDb) {
  return encDb.allDocs({include_docs: true}).then(docs => {
    return Promise.all(docs.rows.map(doc => {
      if (doc.doc._attachments) {
        return Promise.all(Object.keys(doc.doc._attachments).map(att => {
          return encDb.removeAttachment(doc.doc._id, att, doc.doc._rev);
        }));
      } else {
        return Promise.resolve();
      }
    }));
  }).then(() => {
    return encDb.compact();
  }).catch(err => {
    console.log(err);
  });
}
<|endoftext|>


<commit_before>/**
 *
 * @typedef {object} StructureType
 * @property {string} name
 * @property {string} asset
 * @property {set.<string>} harvestingWeeks
 * @property {boolean} continuousProduction
 * @property {number} turnipYield
 */

/**
 * Harvesting week format 'month.week'.
 */
const structureTypes = {
  wheat_farm: {
    name: 'wheat farm',
    asset: 'wheat_farm',
    harvestingWeeks: new Set(['8.1']),
    continuousProduction: false,
    turnipYield: 200,
    cost: 10000
  },
  dairy_farm: {
    name: 'dairy farm',
    asset: 'dairy_farm',
    harvestingWeeks: new Set(),
    continuousProduction: true,
    turnipYield: 12,
    cost: 15000
  },
  berry_farm: {
    name: 'berry farm',
    asset: 'berry_farm',
    harvestingWeeks: new Set(),
    continuousProduction: true,
    turnipYield: 10,
    cost: 10000
  }
}

/**
 * @typedef {object} TileType
 * @property {string} name
 * @property {string} asset
 * @property {StructureType[]} allowedStructures
 */

const tileTypes = {
  forest: {
    name: 'forest',
    asset: 'forest',
    flowers: 6,
    allowedStructures: []
  },
  grass: {
    name: 'grass',
    asset: 'grass',
    flowers: 4,
    allowedStructures: []
  },
  water: {
    name: 'water',
    asset: 'water',
    flowers: 5,
    allowedStructures: []
  }
}

const addAllowedStructures = (structureTypes, tileTypes) => {
  tileTypes.grass.allowedStructures = [structureTypes.wheat_farm, structureTypes.dairy_farm, structureTypes.berry_farm]
  tileTypes.water.allowedStructures = []
  return ({ structureTypes: structureTypes, tileTypes: tileTypes })
}

const StaticTypes = addAllowedStructures(structureTypes, tileTypes)

export default StaticTypes
<commit_msg>rakennusten tuotantoarvoja muokattu<commit_after>/**
 *
 * @typedef {object} StructureType
 * @property {string} name
 * @property {string} asset
 * @property {set.<string>} harvestingWeeks
 * @property {boolean} continuousProduction
 * @property {number} turnipYield
 */

/**
 * Harvesting week format 'month.week'.
 */
const structureTypes = {
  wheat_farm: {
    name: 'wheat farm',
    asset: 'wheat_farm',
    harvestingWeeks: new Set(['8.1']),
    continuousProduction: false,
    turnipYield: 12,
    cost: 10000
  },
  dairy_farm: {
    name: 'dairy farm',
    asset: 'dairy_farm',
    harvestingWeeks: new Set(),
    continuousProduction: true,
    turnipYield: 16,
    cost: 15000
  },
  berry_farm: {
    name: 'berry farm',
    asset: 'berry_farm',
    harvestingWeeks: new Set(),
    continuousProduction: true,
    turnipYield: 10,
    cost: 10000
  }
}

/**
 * @typedef {object} TileType
 * @property {string} name
 * @property {string} asset
 * @property {StructureType[]} allowedStructures
 */

const tileTypes = {
  forest: {
    name: 'forest',
    asset: 'forest',
    flowers: 6,
    allowedStructures: []
  },
  grass: {
    name: 'grass',
    asset: 'grass',
    flowers: 4,
    allowedStructures: []
  },
  water: {
    name: 'water',
    asset: 'water',
    flowers: 5,
    allowedStructures: []
  }
}

const addAllowedStructures = (structureTypes, tileTypes) => {
  tileTypes.grass.allowedStructures = [structureTypes.wheat_farm, structureTypes.dairy_farm, structureTypes.berry_farm]
  tileTypes.water.allowedStructures = []
  return ({ structureTypes: structureTypes, tileTypes: tileTypes })
}

const StaticTypes = addAllowedStructures(structureTypes, tileTypes)

export default StaticTypes
<|endoftext|>


<commit_before>import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { currentURL, click, settled } from '@ember/test-helpers';
import { create } from 'ember-cli-page-object';
import { typeInSearch, selectChoose, clickTrigger } from 'ember-power-select/test-support/helpers';

import authPage from 'vault/tests/pages/auth';
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
import transformationsPage from 'vault/tests/pages/secrets/backend/transform/transformations';
import rolesPage from 'vault/tests/pages/secrets/backend/transform/roles';
import templatesPage from 'vault/tests/pages/secrets/backend/transform/templates';
import alphabetsPage from 'vault/tests/pages/secrets/backend/transform/alphabets';
import searchSelect from 'vault/tests/pages/components/search-select';

const searchSelectComponent = create(searchSelect);

const mount = async () => {
  let path = `transform-${Date.now()}`;
  await mountSecrets.enable('transform', path);
  await settled();
  return path;
};

const newTransformation = async (backend, name, submit = false) => {
  const transformationName = name || 'foo';
  await transformationsPage.visitCreate({ backend });
  await settled();
  await transformationsPage.name(transformationName);
  await settled();
  await clickTrigger('#template');
  await selectChoose('#template', '.ember-power-select-option', 0);
  await settled();
  // Don't automatically choose role because we might be testing that
  if (submit) {
    await transformationsPage.submit();
    await settled();
  }
  return transformationName;
};

const newRole = async (backend, name) => {
  const roleName = name || 'bar';
  await rolesPage.visitCreate({ backend });
  await settled();
  await rolesPage.name(roleName);
  await settled();
  await clickTrigger('#transformations');
  await settled();
  await selectChoose('#transformations', '.ember-power-select-option', 0);
  await settled();
  await rolesPage.submit();
  await settled();
  return roleName;
};

module('Acceptance | Enterprise | Transform secrets', function (hooks) {
  setupApplicationTest(hooks);

  hooks.beforeEach(function () {
    return authPage.login();
  });

  test('it enables Transform secrets engine and shows tabs', async function (assert) {
    let backend = `transform-${Date.now()}`;
    await mountSecrets.enable('transform', backend);
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/list`,
      'mounts and redirects to the transformations list page'
    );
    assert.ok(transformationsPage.isEmpty, 'renders empty state');
    assert
      .dom('.active[data-test-secret-list-tab="Transformations"]')
      .exists('Has Transformations tab which is active');
    assert.dom('[data-test-secret-list-tab="Roles"]').exists('Has Roles tab');
    assert.dom('[data-test-secret-list-tab="Templates"]').exists('Has Templates tab');
    assert.dom('[data-test-secret-list-tab="Alphabets"]').exists('Has Alphabets tab');
  });

  test('it can create a transformation and add itself to the role attached', async function (assert) {
    let backend = await mount();
    const transformationName = 'foo';
    const roleName = 'foo-role';
    await settled();
    await transformationsPage.createLink({ backend });
    await settled();
    assert.equal(currentURL(), `/vault/secrets/${backend}/create`, 'redirects to create transformation page');
    await transformationsPage.name(transformationName);
    await settled();
    assert.dom('[data-test-input="type"]').hasValue('fpe', 'Has type FPE by default');
    assert.dom('[data-test-input="tweak_source"]').exists('Shows tweak source when FPE');
    await transformationsPage.type('masking');
    await settled();
    assert
      .dom('[data-test-input="masking_character"]')
      .exists('Shows masking character input when changed to masking type');
    assert.dom('[data-test-input="tweak_source"]').doesNotExist('Does not show tweak source when masking');
    await clickTrigger('#template');
    await settled();
    assert.equal(searchSelectComponent.options.length, 2, 'list shows two builtin options by default');
    await selectChoose('#template', '.ember-power-select-option', 0);
    await settled();

    await clickTrigger('#allowed_roles');
    await settled();
    await typeInSearch(roleName);
    await settled();
    await selectChoose('#allowed_roles', '.ember-power-select-option', 0);
    await settled();
    await transformationsPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/${transformationName}`,
      'redirects to show transformation page after submit'
    );
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(currentURL(), `/vault/secrets/${backend}/list`, 'Links back to list view from breadcrumb');
  });

  test('it can create a role and add itself to the transformation attached', async function (assert) {
    const roleName = 'my-role';
    let backend = await mount();
    // create transformation without role
    await newTransformation(backend, 'a-transformation', true);
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(currentURL(), `/vault/secrets/${backend}/list`, 'Links back to list view from breadcrumb');
    await click('[data-test-secret-list-tab="Roles"]');
    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=role`, 'links to role list page');
    // create role with transformation attached
    await rolesPage.createLink();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=role`,
      'redirects to create role page'
    );
    await rolesPage.name(roleName);
    await clickTrigger('#transformations');
    assert.equal(searchSelectComponent.options.length, 1, 'lists the transformation');
    await selectChoose('#transformations', '.ember-power-select-option', 0);
    await rolesPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/role/${roleName}`,
      'redirects to show role page after submit'
    );
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/list?tab=role`,
      'Links back to role list view from breadcrumb'
    );
  });

  test('it adds a role to a transformation when added to a role', async function (assert) {
    const roleName = 'role-test';
    let backend = await mount();
    let transformation = await newTransformation(backend, 'b-transformation', true);
    await newRole(backend, roleName);
    await transformationsPage.visitShow({ backend, id: transformation });
    assert.dom('[data-test-row-value="Allowed roles"]').hasText(roleName);
  });

  test('it shows a message if an update fails after save', async function (assert) {
    const roleName = 'role-remove';
    let backend = await mount();
    // Create transformation
    let transformation = await newTransformation(backend, 'c-transformation', true);
    // create role
    await newRole(backend, roleName);
    await transformationsPage.visitShow({ backend, id: transformation });
    assert.dom('[data-test-row-value="Allowed roles"]').hasText(roleName);
    // Edit transformation
    await click('[data-test-edit-link]');
    assert.dom('.modal.is-active').exists('Confirmation modal appears');
    await rolesPage.modalConfirm();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/${transformation}`,
      'Correctly links to edit page for secret'
    );
    // remove role
    await settled();
    await click('#allowed_roles [data-test-selected-list-button="delete"]');

    await transformationsPage.save();
    await settled();
    assert.dom('.flash-message.is-info').exists('Shows info message since role could not be updated');
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/${transformation}`,
      'Correctly links to show page for secret'
    );
    assert
      .dom('[data-test-row-value="Allowed roles"]')
      .doesNotExist('Allowed roles are no longer on the transformation');
  });

  test('it allows creation and edit of a template', async function (assert) {
    const templateName = 'my-template';
    let backend = await mount();
    await click('[data-test-secret-list-tab="Templates"]');

    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=template`, 'links to template list page');
    await settled();
    await templatesPage.createLink();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=template`,
      'redirects to create template page'
    );
    await templatesPage.name(templateName);
    await templatesPage.pattern(`(\\d{4})`);
    await clickTrigger('#alphabet');
    await settled();
    assert.ok(searchSelectComponent.options.length > 0, 'lists built-in alphabets');
    await selectChoose('#alphabet', '.ember-power-select-option', 0);
    assert.dom('#alphabet .ember-power-select-trigger').doesNotExist('Alphabet input no longer searchable');
    await templatesPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/template/${templateName}`,
      'redirects to show template page after submit'
    );
    await templatesPage.editLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/template/${templateName}`,
      'Links to template edit page'
    );
    await settled();
    assert.dom('[data-test-input="name"]').hasAttribute('readonly');
  });

  test('it allows creation and edit of an alphabet', async function (assert) {
    const alphabetName = 'vowels-only';
    let backend = await mount();
    await click('[data-test-secret-list-tab="Alphabets"]');

    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=alphabet`, 'links to alphabet list page');
    await alphabetsPage.createLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=alphabet`,
      'redirects to create alphabet page'
    );
    await alphabetsPage.name(alphabetName);
    await alphabetsPage.alphabet('aeiou');
    await alphabetsPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/alphabet/${alphabetName}`,
      'redirects to show alphabet page after submit'
    );
    assert.dom('[data-test-row-value="Name"]').hasText(alphabetName);
    assert.dom('[data-test-row-value="Alphabet"]').hasText('aeiou');
    await alphabetsPage.editLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/alphabet/${alphabetName}`,
      'Links to alphabet edit page'
    );
    assert.dom('[data-test-input="name"]').hasAttribute('readonly');
  });
});
<commit_msg>the fix (#17476)<commit_after>import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { currentURL, click, settled } from '@ember/test-helpers';
import { create } from 'ember-cli-page-object';
import { typeInSearch, selectChoose, clickTrigger } from 'ember-power-select/test-support/helpers';

import authPage from 'vault/tests/pages/auth';
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
import transformationsPage from 'vault/tests/pages/secrets/backend/transform/transformations';
import rolesPage from 'vault/tests/pages/secrets/backend/transform/roles';
import templatesPage from 'vault/tests/pages/secrets/backend/transform/templates';
import alphabetsPage from 'vault/tests/pages/secrets/backend/transform/alphabets';
import searchSelect from 'vault/tests/pages/components/search-select';

const searchSelectComponent = create(searchSelect);

const mount = async () => {
  let path = `transform-${Date.now()}`;
  await mountSecrets.enable('transform', path);
  await settled();
  return path;
};

const newTransformation = async (backend, name, submit = false) => {
  const transformationName = name || 'foo';
  await transformationsPage.visitCreate({ backend });
  await settled();
  await transformationsPage.name(transformationName);
  await settled();
  await clickTrigger('#template');
  await selectChoose('#template', '.ember-power-select-option', 0);
  await settled();
  // Don't automatically choose role because we might be testing that
  if (submit) {
    await transformationsPage.submit();
    await settled();
  }
  return transformationName;
};

const newRole = async (backend, name) => {
  const roleName = name || 'bar';
  await rolesPage.visitCreate({ backend });
  await settled();
  await rolesPage.name(roleName);
  await settled();
  await clickTrigger('#transformations');
  await settled();
  await selectChoose('#transformations', '.ember-power-select-option', 0);
  await settled();
  await rolesPage.submit();
  await settled();
  return roleName;
};

module('Acceptance | Enterprise | Transform secrets', function (hooks) {
  setupApplicationTest(hooks);

  hooks.beforeEach(function () {
    return authPage.login();
  });

  test('it enables Transform secrets engine and shows tabs', async function (assert) {
    let backend = `transform-${Date.now()}`;
    await mountSecrets.enable('transform', backend);
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/list`,
      'mounts and redirects to the transformations list page'
    );
    assert.ok(transformationsPage.isEmpty, 'renders empty state');
    assert
      .dom('.active[data-test-secret-list-tab="Transformations"]')
      .exists('Has Transformations tab which is active');
    assert.dom('[data-test-secret-list-tab="Roles"]').exists('Has Roles tab');
    assert.dom('[data-test-secret-list-tab="Templates"]').exists('Has Templates tab');
    assert.dom('[data-test-secret-list-tab="Alphabets"]').exists('Has Alphabets tab');
  });

  test('it can create a transformation and add itself to the role attached', async function (assert) {
    let backend = await mount();
    const transformationName = 'foo';
    const roleName = 'foo-role';
    await settled();
    await transformationsPage.createLink({ backend });
    await settled();
    assert.equal(currentURL(), `/vault/secrets/${backend}/create`, 'redirects to create transformation page');
    await transformationsPage.name(transformationName);
    await settled();
    assert.dom('[data-test-input="type"]').hasValue('fpe', 'Has type FPE by default');
    assert.dom('[data-test-input="tweak_source"]').exists('Shows tweak source when FPE');
    await transformationsPage.type('masking');
    await settled();
    assert
      .dom('[data-test-input="masking_character"]')
      .exists('Shows masking character input when changed to masking type');
    assert.dom('[data-test-input="tweak_source"]').doesNotExist('Does not show tweak source when masking');
    await clickTrigger('#template');
    await settled();
    assert.equal(searchSelectComponent.options.length, 2, 'list shows two builtin options by default');
    await selectChoose('#template', '.ember-power-select-option', 0);
    await settled();

    await clickTrigger('#allowed_roles');
    await settled();
    await typeInSearch(roleName);
    await settled();
    await selectChoose('#allowed_roles', '.ember-power-select-option', 0);
    await settled();
    await transformationsPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/${transformationName}`,
      'redirects to show transformation page after submit'
    );
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(currentURL(), `/vault/secrets/${backend}/list`, 'Links back to list view from breadcrumb');
  });

  test('it can create a role and add itself to the transformation attached', async function (assert) {
    const roleName = 'my-role';
    let backend = await mount();
    // create transformation without role
    await newTransformation(backend, 'a-transformation', true);
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(currentURL(), `/vault/secrets/${backend}/list`, 'Links back to list view from breadcrumb');
    await click('[data-test-secret-list-tab="Roles"]');
    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=role`, 'links to role list page');
    // create role with transformation attached
    await rolesPage.createLink();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=role`,
      'redirects to create role page'
    );
    await rolesPage.name(roleName);
    await clickTrigger('#transformations');
    assert.equal(searchSelectComponent.options.length, 1, 'lists the transformation');
    await selectChoose('#transformations', '.ember-power-select-option', 0);
    await rolesPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/role/${roleName}`,
      'redirects to show role page after submit'
    );
    await click(`[data-test-secret-breadcrumb="${backend}"]`);
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/list?tab=role`,
      'Links back to role list view from breadcrumb'
    );
  });

  test('it adds a role to a transformation when added to a role', async function (assert) {
    const roleName = 'role-test';
    let backend = await mount();
    let transformation = await newTransformation(backend, 'b-transformation', true);
    await newRole(backend, roleName);
    await transformationsPage.visitShow({ backend, id: transformation });
    await settled();
    assert.dom('[data-test-row-value="Allowed roles"]').hasText(roleName);
  });

  test('it shows a message if an update fails after save', async function (assert) {
    const roleName = 'role-remove';
    let backend = await mount();
    // Create transformation
    let transformation = await newTransformation(backend, 'c-transformation', true);
    // create role
    await newRole(backend, roleName);
    await settled();
    await transformationsPage.visitShow({ backend, id: transformation });
    assert.dom('[data-test-row-value="Allowed roles"]').hasText(roleName);
    // Edit transformation
    await click('[data-test-edit-link]');
    assert.dom('.modal.is-active').exists('Confirmation modal appears');
    await rolesPage.modalConfirm();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/${transformation}`,
      'Correctly links to edit page for secret'
    );
    // remove role
    await settled();
    await click('#allowed_roles [data-test-selected-list-button="delete"]');

    await transformationsPage.save();
    await settled();
    assert.dom('.flash-message.is-info').exists('Shows info message since role could not be updated');
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/${transformation}`,
      'Correctly links to show page for secret'
    );
    assert
      .dom('[data-test-row-value="Allowed roles"]')
      .doesNotExist('Allowed roles are no longer on the transformation');
  });

  test('it allows creation and edit of a template', async function (assert) {
    const templateName = 'my-template';
    let backend = await mount();
    await click('[data-test-secret-list-tab="Templates"]');

    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=template`, 'links to template list page');
    await settled();
    await templatesPage.createLink();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=template`,
      'redirects to create template page'
    );
    await templatesPage.name(templateName);
    await templatesPage.pattern(`(\\d{4})`);
    await clickTrigger('#alphabet');
    await settled();
    assert.ok(searchSelectComponent.options.length > 0, 'lists built-in alphabets');
    await selectChoose('#alphabet', '.ember-power-select-option', 0);
    assert.dom('#alphabet .ember-power-select-trigger').doesNotExist('Alphabet input no longer searchable');
    await templatesPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/template/${templateName}`,
      'redirects to show template page after submit'
    );
    await templatesPage.editLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/template/${templateName}`,
      'Links to template edit page'
    );
    await settled();
    assert.dom('[data-test-input="name"]').hasAttribute('readonly');
  });

  test('it allows creation and edit of an alphabet', async function (assert) {
    const alphabetName = 'vowels-only';
    let backend = await mount();
    await click('[data-test-secret-list-tab="Alphabets"]');

    assert.equal(currentURL(), `/vault/secrets/${backend}/list?tab=alphabet`, 'links to alphabet list page');
    await alphabetsPage.createLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/create?itemType=alphabet`,
      'redirects to create alphabet page'
    );
    await alphabetsPage.name(alphabetName);
    await alphabetsPage.alphabet('aeiou');
    await alphabetsPage.submit();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/show/alphabet/${alphabetName}`,
      'redirects to show alphabet page after submit'
    );
    assert.dom('[data-test-row-value="Name"]').hasText(alphabetName);
    assert.dom('[data-test-row-value="Alphabet"]').hasText('aeiou');
    await alphabetsPage.editLink();
    await settled();
    assert.equal(
      currentURL(),
      `/vault/secrets/${backend}/edit/alphabet/${alphabetName}`,
      'Links to alphabet edit page'
    );
    assert.dom('[data-test-input="name"]').hasAttribute('readonly');
  });
});
<|endoftext|>


<commit_before>var dot = require('dot')
var marked = require('marked');

// Templates
function Templates() {
  this._length = 0;
  this._templates = {};
}

Templates.prototype.addTemplate = function(name, html) {
  this._length++;
  this._templates[name] = {html: html};
};

Templates.prototype.length = function() {
  return this._length;
};

Templates.prototype.process = function(templateName, data) {
  var template = this._templates[templateName];
  if (template) {
    if (!template.compiledFunc) {
      template.compiledFunc = dot.template(template.html);
    }
    return template.compiledFunc(data);
  }
};
// Markdowns
function Markdowns() {
  this._length = 0;
  this._mds = {};
}

Markdowns.prototype.addMd = function(name, md) {
  this._length++;
  this._mds[name] = {md: md};
};

Markdowns.prototype.length = function() {
  return this._length;
};

Markdowns.prototype.process = function(mdName) {
  var md = this._mds[mdName];
  if (md) {
    if (!md.compiledValue) {
      md.compiledValue = marked(md.md);
    }
    return md.compiledValue;
  }
};

function Session() {
  this.clear();
}

Session.prototype.clear = function() {
  this._entities = {};
  this._templates = {};
  this._mds = {};
};

Session.prototype.addEntity = function(kind, entity) {
  this._entities[kind] = this._entities[kind] || [];
  this._entities[kind].push(entity);
};

Session.prototype.addTemplate = function(kind, templateName, html) {
  this._templates[kind] = this._templates[kind] || new Templates();
  this._templates[kind].addTemplate(templateName, html);
};

Session.prototype.addMd = function(kind, mdName, md) {
  this._mds[kind] = this._mds[kind] || new Markdowns();
  this._mds[kind].addMd(mdName, md);
};

Session.prototype.getTemplates = function(kind) {
  return this._templates[kind];
};

Session.prototype.getMds = function(kind) {
  return this._mds[kind];
};

Session.prototype.getEntities = function(kind) {
  return this._entities[kind];
}

module.exports = new Session();<commit_msg>Utility function to recover an html from a template<commit_after>var dot = require('dot')
var marked = require('marked');

// Templates
function Templates() {
  this._length = 0;
  this._templates = {};
}

Templates.prototype.addTemplate = function(name, html) {
  this._length++;
  this._templates[name] = {html: html};
};

Templates.prototype.getHtml = function(templateName) {
  var template = this._templates[templateName];
  var html = null;
  if (template) {
    html = template.html;
  }
  return html;
};

Templates.prototype.length = function() {
  return this._length;
};

Templates.prototype.process = function(templateName, data) {
  var template = this._templates[templateName];
  if (template) {
    if (!template.compiledFunc) {
      template.compiledFunc = dot.template(template.html);
    }
    return template.compiledFunc(data);
  }
};
// Markdowns
function Markdowns() {
  this._length = 0;
  this._mds = {};
}

Markdowns.prototype.addMd = function(name, md) {
  this._length++;
  this._mds[name] = {md: md};
};

Markdowns.prototype.length = function() {
  return this._length;
};

Markdowns.prototype.process = function(mdName) {
  var md = this._mds[mdName];
  if (md) {
    if (!md.compiledValue) {
      md.compiledValue = marked(md.md);
    }
    return md.compiledValue;
  }
};

function Session() {
  this.clear();
}

Session.prototype.clear = function() {
  this._entities = {};
  this._templates = {};
  this._mds = {};
};

Session.prototype.addEntity = function(kind, entity) {
  this._entities[kind] = this._entities[kind] || [];
  this._entities[kind].push(entity);
};

Session.prototype.addTemplate = function(kind, templateName, html) {
  this._templates[kind] = this._templates[kind] || new Templates();
  this._templates[kind].addTemplate(templateName, html);
};

Session.prototype.addMd = function(kind, mdName, md) {
  this._mds[kind] = this._mds[kind] || new Markdowns();
  this._mds[kind].addMd(mdName, md);
};

Session.prototype.getTemplates = function(kind) {
  return this._templates[kind];
};

Session.prototype.getMds = function(kind) {
  return this._mds[kind];
};

Session.prototype.getEntities = function(kind) {
  return this._entities[kind];
}

module.exports = new Session();<|endoftext|>


<commit_before>/**
 * Get the parent elements as a Grindstone object
 * @param {string} filterBy - only get the parent if it matches the selector, optional
 * @returns {object} parents instance of Grindstone
 */

	$.fn.parent = function(selector) {
        return $.fn.map.call(this, function() {
            var element = this.parentNode;
            if (element && (!selector || $(element).is(selector))) {
                return element;
            }
        });
	};

/**
 * Get the next element as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} instance of Grindstone
 */

	$.fn.next = function(selector) {
        return $.fn.map.call(this, function() {
            var element = this.nextSibling;
            if (element && (!selector || $(element).is(selector))) {
                return element;
            }
        });
	};

/**
 * Get the previous element as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} instance of Grindstone
 */

	$.fn.prev = function(selector) {
        return $.fn.map.call(this, function() {
            var element = this.previousSibling;
            if (element && (!selector || $(element).is(selector))) {
                return element;
            }
        });
	};

/**
 * Get the children elements as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} children instance of Grindstone
 */

	$.fn.children = function(selector) {
        var newSet = [];
        for (var i = 0; i < this.length; i++) {
            for (var child = this[i].firstChild; child; child = child.nextSibling) {
                if (!selector || $(child).is(selector)) {
                    newSet.push(child);
                }
            }
        }
        return $(newSet);
	};

<commit_msg>Traversing to exclude non-elements<commit_after>/**
 * Get the parent elements as a Grindstone object
 * @param {string} filterBy - only get the parent if it matches the selector, optional
 * @returns {object} parents instance of Grindstone
 */

	$.fn.parent = function(selector) {
        return $.fn.map.call(this, function() {
            var element = this.parentNode;
            if (element && (!selector || $(element).is(selector))) {
                return element;
            }
        });
	};

/**
 * Get the next element as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} instance of Grindstone
 */

	$.fn.next = function(selector) {
        return $.fn.map.call(this, function() {
            while (true) {
                var element = this.nextSibling;
                if (!element) {
                    break;
                }
                if (element.nodeType != 1) {
                    continue;
                }
                if (!selector || $(element).is(selector)) {
                    return element;
                }
                break;
            }
        });
	};

/**
 * Get the previous element as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} instance of Grindstone
 */

	$.fn.prev = function(selector) {
        return $.fn.map.call(this, function() {
            while (true) {
                var element = this.previousSibling;
                if (!element) {
                    break;
                }
                if (element.nodeType != 1) {
                    continue;
                }
                if (!selector || $(element).is(selector)) {
                    return element;
                }
                break;
            }
        });
	};

/**
 * Get the children elements as a Grindstone object
 * @param {string} selector - only get the element if it matches the selector, optional
 * @returns {object} children instance of Grindstone
 */

	$.fn.children = function(selector) {
        var newSet = [];
        for (var i = 0; i < this.length; i++) {
            for (var child = this[i].firstChild; child; child = child.nextSibling) {
                if (child.nodeType == 1) {
                    if (!selector || $(child).is(selector)) {
                        newSet.push(child);
                    }
                }
            }
        }
        return $(newSet);
	};

<|endoftext|>


<commit_before>var http = require('sys');
var http = require('http');
var https = require('https');
var RSS = require('rss');
var cache = require('./cache');

var reId = new RegExp(/\/(\d+)$/);

http.createServer(function(req, serverResponse) {
  try {
    //Get the ID off the url
    var m = reId.exec(req.url);
    if (m != null) { //Url has the id so send back rss feed
      serverResponse.writeHead(200, { 'content-type': 'application/rss+xml' });
      var googleId = m[1];
      var cacheFeed = cache.get(googleId);
      if (cacheFeed != null) {
        console.log("Read from cache: " + googleId);
        serverResponse.end(cacheFeed);
      } else {

        var options = {
          host: 'plus.google.com',
          port: 443,
          path: '/_/stream/getactivities/?&sp=[1,2,"'+googleId+'",null,null,40,null,"social.google.com",[]]',
          method: 'GET',
          headers: {
            'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110613 Firefox/6.0a2',
            'Connection':'keep-alive'
          }
        };

        var googleReq = https.request(options, function(googleResponse) {
          var data = [];
          googleResponse.on('data', function(d) {
            data.push(d);
          });
          googleResponse.on('end', function() {
            var results = data.join('');

            results = results.replace(")]}'","");
            results = results.replace(/\[,/g, '["",');
            results = results.replace(/,,/g, ',"",');
            results = results.replace(/,,/g, ',"",');

            var p = JSON.parse(results);
            var post = p[1][0];
            var authorName = post[0][3].toString();

            var feed = new RSS({
              title: authorName + "'s Google+ Public Feed",
              feed_url: 'http://plus.google.com/' + googleId + '/posts',
              site_url: 'http://plus.google.com',
              author: authorName
            });

            for (i=0;i<post.length;i++)
            {
              feed.item({
                  title:  post[i][4].toString(),
                  url: 'https://plus.google.com/' + post[i][21].toString(), // link to the item
                  date: new Date(post[i][5]) // any format that js Date can parse.
              });
            }
            var feed = feed.xml();
            cache.put(googleId, feed, 300000) //Cache for 5 minutes
            console.log("Pulled from google: " + googleId);
            serverResponse.end(feed);
          });
        });
        googleReq.end();
      }
    } else { //Else output directions
      serverResponse.writeHead(200, { 'content-type': 'text/html' });
      serverResponse.write("<title>Google+ to RSS Feed</title>");
      serverResponse.write("
Google+ to RSS Feed
");
      serverResponse.write("
Just add your google user id to this url like this http://googleplus2rss.nodester.com/12345678901234567890 and it will give you your feed.
");
      serverResponse.end("");
    }
  } catch (ex) {
    serverResponse.writeHead(200, { 'content-type': 'text/html' });
      serverResponse.write("<title>Google+ to RSS Feed</title>");
      serverResponse.write("
Google+ to RSS Feed Error
");
      serverResponse.write("
We got the following error getting the feed: " + ex.toString() + "
");
      serverResponse.end("");
  }
}).listen(11908);
<commit_msg>Update the HTML to give user more direction<commit_after>var http = require('sys');
var http = require('http');
var https = require('https');
var RSS = require('rss');
var cache = require('./cache');

var reId = new RegExp(/\/(\d+)$/);

http.createServer(function(req, serverResponse) {
  try {
    //Get the ID off the url
    var m = reId.exec(req.url);
    if (m != null) { //Url has the id so send back rss feed
      serverResponse.writeHead(200, { 'content-type': 'application/rss+xml' });
      var googleId = m[1];
      var cacheFeed = cache.get(googleId);
      if (cacheFeed != null) {
        console.log("Read from cache: " + googleId);
        serverResponse.end(cacheFeed);
      } else {

        var options = {
          host: 'plus.google.com',
          port: 443,
          path: '/_/stream/getactivities/?&sp=[1,2,"'+googleId+'",null,null,40,null,"social.google.com",[]]',
          method: 'GET',
          headers: {
            'user-agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110613 Firefox/6.0a2',
            'Connection':'keep-alive'
          }
        };

        var googleReq = https.request(options, function(googleResponse) {
          var data = [];
          googleResponse.on('data', function(d) {
            data.push(d);
          });
          googleResponse.on('end', function() {
            var results = data.join('');

            results = results.replace(")]}'","");
            results = results.replace(/\[,/g, '["",');
            results = results.replace(/,,/g, ',"",');
            results = results.replace(/,,/g, ',"",');

            var p = JSON.parse(results);
            var post = p[1][0];
            var authorName = post[0][3].toString();

            var feed = new RSS({
              title: authorName + "'s Google+ Public Feed",
              feed_url: 'http://plus.google.com/' + googleId + '/posts',
              site_url: 'http://plus.google.com',
              author: authorName
            });

            for (i=0;i<post.length;i++)
            {
              feed.item({
                  title:  post[i][4].toString(),
                  url: 'https://plus.google.com/' + post[i][21].toString(), // link to the item
                  date: new Date(post[i][5]) // any format that js Date can parse.
              });
            }
            var feed = feed.xml();
            cache.put(googleId, feed, 300000) //Cache for 5 minutes
            console.log("Pulled from google: " + googleId);
            serverResponse.end(feed);
          });
        });
        googleReq.end();
      }
    } else { //Else output directions
      serverResponse.writeHead(200, { 'content-type': 'text/html' });
      serverResponse.write("<title>Google+ to RSS Feed</title>");
      serverResponse.write("
Google+ to RSS Feed
");
      serverResponse.write("
Just add your google user id to this url like this http://googleplus2rss.nodester.com/118004117219257714873 and it will give you your raw rss feed. You can get your user id by logging into your Google+ account and clicking on your name. The url in the browse will have a long number which is your user id.
");
      serverResponse.write("
This still is a beta site so it might go down from time to time. If you have any issues please submit a issue to Github Issues
");
      serverResponse.write("
Current Working On:
");
      serverResponse.write("
");
      serverResponse.write("
Fixing links and videos with no text with them.
");
      serverResponse.write("
Adding a full express site with more detailed help.
");
      serverResponse.write("
Limits so we don't get kicked from nodester.com, it's free thanks guys!
");
      serverResponse.write("
A job doing Node.js so I can stop wasting my time with M$ programming. :)
");
      serverResponse.write("
");
      serverResponse.write("
Thanks for taking a look! JTWebMan
");
      serverResponse.end("");
    }
  } catch (ex) {
    serverResponse.writeHead(200, { 'content-type': 'text/html' });
      serverResponse.write("<title>Google+ to RSS Feed</title>");
      serverResponse.write("
Google+ to RSS Feed Error
");
      serverResponse.write("
We got the following error getting the feed: " + ex.toString() + "
");
      serverResponse.write("
This still is a beta site so it might go down from time to time. If you have any issues please submit a issue to Github Issues
");
      serverResponse.end("");
  }
}).listen(11908);
<|endoftext|>


<commit_before>import Watcher from '../watcher'
import Parser, { linkParser } from '../parser'
import { addEvent, removeEvent } from '../../dom'
import { isNormal, addScope } from '../expression'
import { removeSpace, each, def, extend, clearObject, warn, isFunc, error, noop } from '../../util'

const regKeyCode = /^(\d)*$/
const regBigBrackets = /^\{.*\}$/
const regSmallBrackets = /(\(.*\))/
const regQuotes = /(^'*)|('*$)|(^"*)|("*$)/g
const regJsonFormat = /[^,]+:[^:]+((?=,[^:]+:)|$)/g

/**
 * 生成匿名事件函数
 * @param   {String}  expression
 * @return  {Function}
 */
function createAnonymous (expression) {
    try {
        return new Function('scope', addScope(expression))
    } catch (e) {
        error('Invalid generated expression: [' + expression + ']')
        return noop
    }
}

/**
 * 分解字符串函数参数
 * @param   {String}  funcString
 * @return  {Object}
 */
function stringToParams (funcString) {
    let args, func
    let exp = removeSpace(funcString)
    let matches = exp.match(regSmallBrackets)
    let result = matches && matches[0]

    // 有函数名和参数
    if (result) {
        func = exp.substr(0, exp.indexOf(result))
        args = '[' + result.substr(1, result.length - 2) + ']'
    } else {
        func = exp
    }

    return { func, args }
}

/**
 * 字符串 json 转为键值对象
 * @param   {String}  jsonString
 * @return  {Object}
 */
function convertJson (jsonString) {
    let json = {}, string = jsonString.trim()

    if (regBigBrackets.test(string)) {
        let leng = string.length
        string = string.substr(1, leng - 2).replace(/\s/g, '')
        let props = string.match(regJsonFormat)

        each(props, function (prop) {
            let vals = prop.split(':')
            let name = vals[0], value = vals[1]
            if (name && value) {
                name = name.replace(regQuotes, '')
                json[name] = value
            }
        })
    }

    return json
}

/**
 * 格式化事件信息
 * @param   {String}  arg
 * @param   {String}  expression
 * @return  {Object}
 */
function formatEvent (arg, expression) {
    let pos = arg.indexOf('.')

    let type, dress = ''
    if (pos > -1) {
        type = arg.substr(0, pos)
        dress = arg.substr(pos + 1,  arg.length)
    } else {
        type = arg
    }

    let info = stringToParams(expression)
    let func = info.func, args = info.args

    // 尝试匿名函数表达式
    if (!isNormal(func)) {
        func = createAnonymous(expression)
    }

    return { type, dress, func, args }
}

/**
 * 收集绑定的事件
 * @param   {Object}  desc
 * @return  {Array}
 */
function collectEvents (desc) {
    let binds = []
    let args = desc.args
    let expression = desc.expression

    if (args) {
        binds = [formatEvent(args, expression)]
    } else {
        let json = convertJson(expression)
        each(json, function (value, key) {
            binds.push(formatEvent(key, value))
        })
    }

    return binds
}

/**
 * 获取事件修饰符对象
 * 支持 6 种事件修饰符
 * .self .stop .prevent .capture .keyCode .one
 * @param  {String}  type
 * @param  {String}  dress
 */
function getDress (type, dress) {
    let dresses = dress.split('.')

    let self = dresses.indexOf('self') > -1
    let stop = dresses.indexOf('stop') > -1
    let one = dresses.indexOf('one') > -1
    let prevent = dresses.indexOf('prevent') > -1
    let capture = dresses.indexOf('capture') > -1

    let keyCode
    if (type.indexOf('key') === 0) {
        each(dresses, function (value) {
            if (regKeyCode.test(value)) {
                keyCode = +value
                return false
            }
        })
    }

    return { self, stop, prevent, capture, keyCode, one }
}

/**
 * 事件 id 唯一计数
 * @type  {Number}
 */
let vonGuid = 2000
let identifier = '__vonid__'

/**
 * v-on 指令解析模块
 * 不需要实例化 Directive
 */
export function VOn () {
    this.cache = {}
    this.funcWatchers = []
    this.argsWatchers = []
    Parser.apply(this, arguments)
}

let von = linkParser(VOn)

/**
 * 解析 v-on 指令
 */
von.parse = function () {
    each(collectEvents(this.desc), function (bind) {
        this.parseEvent(bind)
    }, this)
}

/**
 * 获取事件/参数的监测信息
 * @param   {String}  expression
 * @return  {Object}
 */
von.getExpDesc = function (expression) {
    return extend({}, this.desc, {
        expression: expression
    })
}

/**
 * 解析事件处理函数
 * @param  {Object}  bind
 */
von.parseEvent = function (bind) {
    let func = bind.func
    let args = bind.args
    let type = bind.type
    let dress = bind.dress
    let capture = dress.indexOf('capture') > -1

    if (isFunc(func)) {
        return this.bindAnonymousEvent(type, dress, func)
    }

    if (func === '$remove') {
        return this.bindRemoveEvent(type, dress)
    }

    let desc = this.getExpDesc(func)
    let funcWatcher = new Watcher(this.vm, desc, function (newFunc, oldFunc) {
        this.off(type, oldFunc, capture)
        this.bindEvent(type, dress, newFunc, args)
    }, this)

    let listener = funcWatcher.value

    if (!isFunc(listener)) {
        funcWatcher.destroy()
        return warn('Directive ['+ this.desc.attr +'] must be a type of Function')
    }

    this.bindEvent(type, dress, listener, args)

    if (desc.once) {
        funcWatcher.destroy()
    } else {
        this.funcWatchers.push(funcWatcher)
    }
}

/**
 * 绑定匿名事件
 * @param  {String}    type   [事件类型]
 * @param  {String}    dress  [事件修饰符]
 * @param  {Function}  func   [匿名事件函数]
 */
von.bindAnonymousEvent = function (type, dress, func) {
    let scope = this.scope || this.vm.$data
    this.bindEvent(type, dress, func.bind(scope, scope))
}

/**
 * 匿名绑定删除($remove) vfor 选项事件
 * @param  {String}  type   [事件类型]
 * @param  {String}  dress  [事件修饰符]
 */
von.bindRemoveEvent = function (type, dress) {
    let scope = this.scope

    if (!scope) {
        return warn('The specify event $remove must be used in v-for scope')
    }

    let alias = scope.__alias__
    this.bindEvent(type, dress, function $remove () {
        scope.__viterator__.$remove(scope[alias])
    }, '['+ alias +']')
}

/**
 * 添加一个事件绑定，同时处理参数的变更
 * @param  {String}    type       [事件类型]
 * @param  {String}    dress      [事件修饰符]
 * @param  {Function}  func       [回调函数]
 * @param  {String}    argString  [参数字符串]
 */
von.bindEvent = function (type, dress, func, argString) {
    let { self, stop, prevent, capture, keyCode, one } = getDress(type, dress)

    // 挂载 $event
    def((this.scope || this.vm.$data), '$event', '__e__')

    // 处理回调参数以及依赖监测
    let args = []
    if (argString) {
        let desc = this.getExpDesc(argString)
        let argsWatcher = new Watcher(this.vm, desc, function (newArgs) {
            args = newArgs
        }, this)

        args = argsWatcher.value

        if (desc.once) {
            argsWatcher.destroy()
        } else {
            this.argsWatchers.push(argsWatcher)
        }
    }

    // 事件代理函数
    let el = this.el
    let listenerAgent = function _listenerAgent (e) {
        if (
            (self && e.target !== el) || // 是否限定只能在当前节点触发事件
            (keyCode && keyCode !== e.keyCode) // 键盘事件时是否指定键码触发
        ) {
            return
        }

        // 未指定参数，则原生事件对象作为唯一参数
        if (!args.length) {
            args.push(e)
        } else {
            // 更新/替换事件对象
            each(args, function (param, index) {
                if (param === '__e__' || param instanceof Event) {
                    args[index] = e
                }
            })
        }

        // 是否阻止默认事件
        if (prevent) {
            e.preventDefault()
        }

        // 是否阻止冒泡
        if (stop) {
            e.stopPropagation()
        }

        func.apply(this, args)
    }

    let listener
    let guid = vonGuid++

    // 回调函数是否只需触发一次
    let that = this
    if (one) {
        listener = function _oneListener (e) {
            listenerAgent(e)
            that.off(type, listener, capture)
        }

        listener[identifier] = guid
    } else {
        func[identifier] = guid
        listener = listenerAgent
    }

    // 缓存事件
    this.cache[guid] = listener

    // 添加绑定
    this.on(type, listener, capture)
}

/**
 * 绑定一个事件
 * @param  {String}    type
 * @param  {Function}  callback
 * @param  {Boolean}   capture
 */
von.on = function (type, callback, capture) {
    addEvent(this.el, type, callback, capture)
}

/**
 * 解绑一个事件
 * @param  {String}    type
 * @param  {Function}  callback
 * @param  {Boolean}   capture
 */
von.off = function (type, callback, capture) {
    let cache = this.cache
    let guid = callback[identifier]
    let listenerAgent = cache[guid]

    if (listenerAgent) {
        removeEvent(this.el, type, listenerAgent, capture)
        delete cache[guid]
    }
}

/**
 * von 指令特定的销毁函数
 */
von._destroy = function () {
    clearObject(this.cache)

    each(this.funcWatchers, function (watcher) {
        watcher.destroy()
    })

    each(this.argsWatchers, function (watcher) {
        watcher.destroy()
    })
}
<commit_msg>Rollup bundle miss index.<commit_after>import Watcher from '../watcher'
import Parser, { linkParser } from '../parser'
import { addEvent, removeEvent } from '../../dom'
import { isNormal, addScope } from '../expression/index'
import { removeSpace, each, def, extend, clearObject, warn, isFunc, error, noop } from '../../util'

const regKeyCode = /^(\d)*$/
const regBigBrackets = /^\{.*\}$/
const regSmallBrackets = /(\(.*\))/
const regQuotes = /(^'*)|('*$)|(^"*)|("*$)/g
const regJsonFormat = /[^,]+:[^:]+((?=,[^:]+:)|$)/g

/**
 * 生成匿名事件函数
 * @param   {String}  expression
 * @return  {Function}
 */
function createAnonymous (expression) {
    try {
        return new Function('scope', addScope(expression))
    } catch (e) {
        error('Invalid generated expression: [' + expression + ']')
        return noop
    }
}

/**
 * 分解字符串函数参数
 * @param   {String}  funcString
 * @return  {Object}
 */
function stringToParams (funcString) {
    let args, func
    let exp = removeSpace(funcString)
    let matches = exp.match(regSmallBrackets)
    let result = matches && matches[0]

    // 有函数名和参数
    if (result) {
        func = exp.substr(0, exp.indexOf(result))
        args = '[' + result.substr(1, result.length - 2) + ']'
    } else {
        func = exp
    }

    return { func, args }
}

/**
 * 字符串 json 转为键值对象
 * @param   {String}  jsonString
 * @return  {Object}
 */
function convertJson (jsonString) {
    let json = {}, string = jsonString.trim()

    if (regBigBrackets.test(string)) {
        let leng = string.length
        string = string.substr(1, leng - 2).replace(/\s/g, '')
        let props = string.match(regJsonFormat)

        each(props, function (prop) {
            let vals = prop.split(':')
            let name = vals[0], value = vals[1]
            if (name && value) {
                name = name.replace(regQuotes, '')
                json[name] = value
            }
        })
    }

    return json
}

/**
 * 格式化事件信息
 * @param   {String}  arg
 * @param   {String}  expression
 * @return  {Object}
 */
function formatEvent (arg, expression) {
    let pos = arg.indexOf('.')

    let type, dress = ''
    if (pos > -1) {
        type = arg.substr(0, pos)
        dress = arg.substr(pos + 1,  arg.length)
    } else {
        type = arg
    }

    let info = stringToParams(expression)
    let func = info.func, args = info.args

    // 尝试匿名函数表达式
    if (!isNormal(func)) {
        func = createAnonymous(expression)
    }

    return { type, dress, func, args }
}

/**
 * 收集绑定的事件
 * @param   {Object}  desc
 * @return  {Array}
 */
function collectEvents (desc) {
    let binds = []
    let args = desc.args
    let expression = desc.expression

    if (args) {
        binds = [formatEvent(args, expression)]
    } else {
        let json = convertJson(expression)
        each(json, function (value, key) {
            binds.push(formatEvent(key, value))
        })
    }

    return binds
}

/**
 * 获取事件修饰符对象
 * 支持 6 种事件修饰符
 * .self .stop .prevent .capture .keyCode .one
 * @param  {String}  type
 * @param  {String}  dress
 */
function getDress (type, dress) {
    let dresses = dress.split('.')

    let self = dresses.indexOf('self') > -1
    let stop = dresses.indexOf('stop') > -1
    let one = dresses.indexOf('one') > -1
    let prevent = dresses.indexOf('prevent') > -1
    let capture = dresses.indexOf('capture') > -1

    let keyCode
    if (type.indexOf('key') === 0) {
        each(dresses, function (value) {
            if (regKeyCode.test(value)) {
                keyCode = +value
                return false
            }
        })
    }

    return { self, stop, prevent, capture, keyCode, one }
}

/**
 * 事件 id 唯一计数
 * @type  {Number}
 */
let vonGuid = 2000
let identifier = '__vonid__'

/**
 * v-on 指令解析模块
 * 不需要实例化 Directive
 */
export function VOn () {
    this.cache = {}
    this.funcWatchers = []
    this.argsWatchers = []
    Parser.apply(this, arguments)
}

let von = linkParser(VOn)

/**
 * 解析 v-on 指令
 */
von.parse = function () {
    each(collectEvents(this.desc), function (bind) {
        this.parseEvent(bind)
    }, this)
}

/**
 * 获取事件/参数的监测信息
 * @param   {String}  expression
 * @return  {Object}
 */
von.getExpDesc = function (expression) {
    return extend({}, this.desc, {
        expression: expression
    })
}

/**
 * 解析事件处理函数
 * @param  {Object}  bind
 */
von.parseEvent = function (bind) {
    let func = bind.func
    let args = bind.args
    let type = bind.type
    let dress = bind.dress
    let capture = dress.indexOf('capture') > -1

    if (isFunc(func)) {
        return this.bindAnonymousEvent(type, dress, func)
    }

    if (func === '$remove') {
        return this.bindRemoveEvent(type, dress)
    }

    let desc = this.getExpDesc(func)
    let funcWatcher = new Watcher(this.vm, desc, function (newFunc, oldFunc) {
        this.off(type, oldFunc, capture)
        this.bindEvent(type, dress, newFunc, args)
    }, this)

    let listener = funcWatcher.value

    if (!isFunc(listener)) {
        funcWatcher.destroy()
        return warn('Directive ['+ this.desc.attr +'] must be a type of Function')
    }

    this.bindEvent(type, dress, listener, args)

    if (desc.once) {
        funcWatcher.destroy()
    } else {
        this.funcWatchers.push(funcWatcher)
    }
}

/**
 * 绑定匿名事件
 * @param  {String}    type   [事件类型]
 * @param  {String}    dress  [事件修饰符]
 * @param  {Function}  func   [匿名事件函数]
 */
von.bindAnonymousEvent = function (type, dress, func) {
    let scope = this.scope || this.vm.$data
    this.bindEvent(type, dress, func.bind(scope, scope))
}

/**
 * 匿名绑定删除($remove) vfor 选项事件
 * @param  {String}  type   [事件类型]
 * @param  {String}  dress  [事件修饰符]
 */
von.bindRemoveEvent = function (type, dress) {
    let scope = this.scope

    if (!scope) {
        return warn('The specify event $remove must be used in v-for scope')
    }

    let alias = scope.__alias__
    this.bindEvent(type, dress, function $remove () {
        scope.__viterator__.$remove(scope[alias])
    }, '['+ alias +']')
}

/**
 * 添加一个事件绑定，同时处理参数的变更
 * @param  {String}    type       [事件类型]
 * @param  {String}    dress      [事件修饰符]
 * @param  {Function}  func       [回调函数]
 * @param  {String}    argString  [参数字符串]
 */
von.bindEvent = function (type, dress, func, argString) {
    let { self, stop, prevent, capture, keyCode, one } = getDress(type, dress)

    // 挂载 $event
    def((this.scope || this.vm.$data), '$event', '__e__')

    // 处理回调参数以及依赖监测
    let args = []
    if (argString) {
        let desc = this.getExpDesc(argString)
        let argsWatcher = new Watcher(this.vm, desc, function (newArgs) {
            args = newArgs
        }, this)

        args = argsWatcher.value

        if (desc.once) {
            argsWatcher.destroy()
        } else {
            this.argsWatchers.push(argsWatcher)
        }
    }

    // 事件代理函数
    let el = this.el
    let listenerAgent = function _listenerAgent (e) {
        if (
            (self && e.target !== el) || // 是否限定只能在当前节点触发事件
            (keyCode && keyCode !== e.keyCode) // 键盘事件时是否指定键码触发
        ) {
            return
        }

        // 未指定参数，则原生事件对象作为唯一参数
        if (!args.length) {
            args.push(e)
        } else {
            // 更新/替换事件对象
            each(args, function (param, index) {
                if (param === '__e__' || param instanceof Event) {
                    args[index] = e
                }
            })
        }

        // 是否阻止默认事件
        if (prevent) {
            e.preventDefault()
        }

        // 是否阻止冒泡
        if (stop) {
            e.stopPropagation()
        }

        func.apply(this, args)
    }

    let listener
    let guid = vonGuid++

    // 回调函数是否只需触发一次
    let that = this
    if (one) {
        listener = function _oneListener (e) {
            listenerAgent(e)
            that.off(type, listener, capture)
        }

        listener[identifier] = guid
    } else {
        func[identifier] = guid
        listener = listenerAgent
    }

    // 缓存事件
    this.cache[guid] = listener

    // 添加绑定
    this.on(type, listener, capture)
}

/**
 * 绑定一个事件
 * @param  {String}    type
 * @param  {Function}  callback
 * @param  {Boolean}   capture
 */
von.on = function (type, callback, capture) {
    addEvent(this.el, type, callback, capture)
}

/**
 * 解绑一个事件
 * @param  {String}    type
 * @param  {Function}  callback
 * @param  {Boolean}   capture
 */
von.off = function (type, callback, capture) {
    let cache = this.cache
    let guid = callback[identifier]
    let listenerAgent = cache[guid]

    if (listenerAgent) {
        removeEvent(this.el, type, listenerAgent, capture)
        delete cache[guid]
    }
}

/**
 * von 指令特定的销毁函数
 */
von._destroy = function () {
    clearObject(this.cache)

    each(this.funcWatchers, function (watcher) {
        watcher.destroy()
    })

    each(this.argsWatchers, function (watcher) {
        watcher.destroy()
    })
}
<|endoftext|>


<commit_before>/**
Message for highlighting the failure, possible failure, or success of
an action. Particularly useful for forms.
*/
Ext.define('devilry_extjsextras.AlertMessage', {
    extend: 'Ext.Component',
    alias: 'widget.alertmessage',

    tpl: [
        '
',
            '<tpl if="closable">',
                '<button type="button" class="close" data-dismiss="alert">×</button>',
            '</tpl>',
            '<tpl if="title">',
                '{title}: ',
            '</tpl>',
            '{message}',
        '
'
    ],

    /**
     * @cfg {String} [boxMargin]
     * Override the margin style of the alert DIV.
     */
    boxMargin: undefined,

    /**
     * @cfg
     * Type of message. Valid values: 'error', 'warning', 'info' or 'success'.
     * Defaults to 'warning'.
     */
    type: 'warning',

    /**
     * @cfg
     * The one line of message to display. This is not required, as it may be
     * useful to initialize an hidden alert message and use update() to change
     * the message when there is a message to show.
     */
    message: '',

    /**
     * @cfg {bool} [closable=false]
     * Show close button. The ``closed`` event is fired when the button is
     * clicked.
     */
    closable: false,

    /**
     * @cfg {int} [autoclose]
     * Fire the ``close`` event ``autoclose`` seconds after creating the message.
     */

    /**
     * @cfg
     * An optional title for the message.
     */
    title: null,

    initComponent: function() {
        var cls = 'bootstrap devilry_extjsextras_alertmessage';
        if(this.cls) {
            cls += Ext.String.format('{0} {1}', cls, this.cls);
        }
        this.cls = cls;
        this.callParent(arguments);
        this.update(this.message, this.type);

        this.addListener({
            scope: this,
            element: 'el',
            delegate: '.close',
            click: function(e) {
                e.preventDefault();
                this.fireEvent('closed', this);
            }
        });

        if(!Ext.isEmpty(this.autoclose)) {
            Ext.defer(function() {
                this.fireEvent('closed', this);
            }, 1000*this.autoclose, this);
        }
    },

    /**
     * Update the message and optionally the type. If the type is not
     * specified, the type will not be changed.
     * */
    update: function(message, type) {
        if(type) {
            this.type = type;
        }
        this.message = message;
        var style = '';
        if(!Ext.isEmpty(this.boxMargin)) {
            style = Ext.String.format('margin: {0};', this.boxMargin);
        }
        this.callParent([{
            type: this.type,
            message: this.message,
            title: this.title,
            style: style,
            closable: this.closable
        }]);
    }
});
<commit_msg>devilry_extjsextras...AlertMessage: Autocalculate autoclose time.<commit_after>/**
Message for highlighting the failure, possible failure, or success of
an action. Particularly useful for forms.
*/
Ext.define('devilry_extjsextras.AlertMessage', {
    extend: 'Ext.Component',
    alias: 'widget.alertmessage',

    requires: [
        'Ext.fx.Animator'
    ],

    tpl: [
        '
',
            '<tpl if="closable">',
                '<button type="button" class="close" data-dismiss="alert">×</button>',
            '</tpl>',
            '<tpl if="title">',
                '{title}: ',
            '</tpl>',
            '{message}',
        '
'
    ],

    /**
     * @cfg {String} [boxMargin]
     * Override the margin style of the alert DIV.
     */
    boxMargin: undefined,

    /**
     * @cfg
     * Type of message. Valid values: 'error', 'warning', 'info' or 'success'.
     * Defaults to 'warning'.
     */
    type: 'warning',

    /**
     * @cfg
     * The one line of message to display. This is not required, as it may be
     * useful to initialize an hidden alert message and use update() to change
     * the message when there is a message to show.
     */
    message: '',

    /**
     * @cfg {bool} [closable=false]
     * Show close button. The ``closed`` event is fired when the button is
     * clicked.
     */
    closable: false,

    /**
     * @cfg {int} [autoclose]
     * Fire the ``close`` event ``autoclose`` seconds after creating the message.
     * If ``true``, we calculate the autoclose time automatically based on the
     * number of words.
     */

    /**
     * @cfg
     * An optional title for the message.
     */
    title: null,

    initComponent: function() {
        var cls = 'bootstrap devilry_extjsextras_alertmessage';
        if(this.cls) {
            cls += Ext.String.format('{0} {1}', cls, this.cls);
        }
        this.cls = cls;
        this.callParent(arguments);
        this.update(this.message, this.type);

        this.addListener({
            scope: this,
            element: 'el',
            delegate: '.close',
            click: function(e) {
                e.preventDefault();
                this.fireEvent('closed', this);
            }
        });

        if(!Ext.isEmpty(this.autoclose)) {
            if(this.autoclose === true) {
                this.autoclose = this._calculateAutocloseTime();
            }
            Ext.defer(function() {
                this.fadeOutAndClose();
            }, 1000*this.autoclose, this);
        }
    },

    _calculateAutocloseTime: function() {
        var alltext = this.message;
        if(!Ext.isEmpty(this.title)) {
            alltext += this.title;
        }
        var words = Ext.String.splitWords(alltext);
        var sec = words.length * 1; // One second per work
        sec = sec > 7? sec: 7; // ... but never less than 5 sec
        return 2 + sec; // 2 seconds to focus on the message + time to read it
    },

    fadeOutAndClose: function() {
        Ext.create('Ext.fx.Animator', {
            target: this.getEl(),
            duration: 3000,
            keyframes: {
                0: {
                    opacity: 1,
                },
                100: {
                    opacity: 0,
                }
            },
            listeners: {
                scope: this,
                afteranimate: function() {
                    this.fireEvent('closed', this);
                }
            }
        });
    },

    /**
     * Update the message and optionally the type. If the type is not
     * specified, the type will not be changed.
     * */
    update: function(message, type) {
        if(type) {
            this.type = type;
        }
        this.message = message;
        var style = '';
        if(!Ext.isEmpty(this.boxMargin)) {
            style = Ext.String.format('margin: {0};', this.boxMargin);
        }
        this.callParent([{
            type: this.type,
            message: this.message,
            title: this.title,
            style: style,
            closable: this.closable
        }]);
    }
});
<|endoftext|>