<commit_before>/**
@module ember
@submodule ember-handlebars-compiler
*/

// Eliminate dependency on any Ember to simplify precompilation workflow
var objectCreate = Object.create || function(parent) {
  function F() {}
  F.prototype = parent;
  return new F();
};

var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars);
if (!Handlebars && typeof require === 'function') {
  Handlebars = require('handlebars');
}

Ember.assert("Ember Handlebars requires Handlebars version 1.0.0. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars);
Ember.assert("Ember Handlebars requires Handlebars version 1.0.0, COMPILER_REVISION expected: 4, got: " +  Handlebars.COMPILER_REVISION + " - Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 4);

/**
  Prepares the Handlebars templating library for use inside Ember's view
  system.

  The `Ember.Handlebars` object is the standard Handlebars library, extended to
  use Ember's `get()` method instead of direct property access, which allows
  computed properties to be used inside templates.

  To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`.
  This will return a function that can be used by `Ember.View` for rendering.

  @class Handlebars
  @namespace Ember
*/
Ember.Handlebars = objectCreate(Handlebars);

function makeBindings(options) {
  var hash = options.hash,
      hashType = options.hashTypes;

  for (var prop in hash) {
    if (hashType[prop] === 'ID') {
      hash[prop + 'Binding'] = hash[prop];
      hashType[prop + 'Binding'] = 'STRING';
      delete hash[prop];
      delete hashType[prop];
    }
  }
}

/**
  Register a bound helper or custom view helper.

  ## Simple bound helper example

  ```javascript
  Ember.Handlebars.helper('capitalize', function(value) {
    return value.toUpperCase();
  });
  ```

  The above bound helper can be used inside of templates as follows:

  ```handlebars
  {{capitalize name}}
  ```

  In this case, when the `name` property of the template's context changes,
  the rendered value of the helper will update to reflect this change.

  For more examples of bound helpers, see documentation for
  `Ember.Handlebars.registerBoundHelper`.

  ## Custom view helper example

  Assuming a view subclass named `App.CalenderView` were defined, a helper
  for rendering instances of this view could be registered as follows:

  ```javascript
  Ember.Handlebars.helper('calendar', App.CalendarView):
  ```

  The above bound helper can be used inside of templates as follows:

  ```handlebars
  {{calendar}}
  ```

  Which is functionally equivalent to:

  ```handlebars
  {{view App.CalendarView}}
  ```

  Options in the helper will be passed to the view in exactly the same
  manner as with the `view` helper.

  @method helper
  @for Ember.Handlebars
  @param {String} name
  @param {Function|Ember.View} function or view class constructor
  @param {String} dependentKeys*
*/
Ember.Handlebars.helper = function(name, value) {
  if (Ember.Component.detect(value)) {
    Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", name.match(/-/));

    var proto = value.proto();
    if (!proto.layoutName && !proto.templateName) {
      value.reopen({
        layoutName: 'components/' + name
      });
    }
  }

  if (Ember.View.detect(value)) {
    Ember.Handlebars.registerHelper(name, function(options) {
      Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View", arguments.length < 2);
      makeBindings(options);
      return Ember.Handlebars.helpers.view.call(this, value, options);
    });
  } else {
    Ember.Handlebars.registerBoundHelper.apply(null, arguments);
  }
};

/**
@class helpers
@namespace Ember.Handlebars
*/
Ember.Handlebars.helpers = objectCreate(Handlebars.helpers);

/**
  Override the the opcode compiler and JavaScript compiler for Handlebars.

  @class Compiler
  @namespace Ember.Handlebars
  @private
  @constructor
*/
Ember.Handlebars.Compiler = function() {};

// Handlebars.Compiler doesn't exist in runtime-only
if (Handlebars.Compiler) {
  Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
}

Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler;

/**
  @class JavaScriptCompiler
  @namespace Ember.Handlebars
  @private
  @constructor
*/
Ember.Handlebars.JavaScriptCompiler = function() {};

// Handlebars.JavaScriptCompiler doesn't exist in runtime-only
if (Handlebars.JavaScriptCompiler) {
  Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
  Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler;
}

Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars";

Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() {
  return "''";
};

/**
  @private

  Override the default buffer for Ember Handlebars. By default, Handlebars
  creates an empty String at the beginning of each invocation and appends to
  it. Ember's Handlebars overrides this to append to a single shared buffer.

  @method appendToBuffer
  @param string {String}
*/
Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
  return "data.buffer.push("+string+");";
};

var prefix = "ember" + (+new Date()), incr = 1;

/**
  @private

  Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that
  all simple mustaches in Ember's Handlebars will also set up an observer to
  keep the DOM up to date when the underlying property changes.

  @method mustache
  @for Ember.Handlebars.Compiler
  @param mustache
*/
Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
  if (mustache.isHelper && mustache.id.string === 'control') {
    mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
    mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
  } else if (mustache.params.length || mustache.hash) {
    // no changes required
  } else {
    var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);

    // Update the mustache node to include a hash value indicating whether the original node
    // was escaped. This will allow us to properly escape values when the underlying value
    // changes and we need to re-render the value.
    if (!mustache.escaped) {
      mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
      mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
    }
    mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
  }

  return Handlebars.Compiler.prototype.mustache.call(this, mustache);
};

/**
  Used for precompilation of Ember Handlebars templates. This will not be used
  during normal app execution.

  @method precompile
  @for Ember.Handlebars
  @static
  @param {String} string The template to precompile
*/
Ember.Handlebars.precompile = function(string) {
  var ast = Handlebars.parse(string);

  var options = {
    knownHelpers: {
      action: true,
      unbound: true,
      bindAttr: true,
      template: true,
      view: true,
      _triageMustache: true
    },
    data: true,
    stringParams: true
  };

  var environment = new Ember.Handlebars.Compiler().compile(ast, options);
  return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
};

// We don't support this for Handlebars runtime-only
if (Handlebars.compile) {
  /**
    The entry point for Ember Handlebars. This replaces the default
    `Handlebars.compile` and turns on template-local data and String
    parameters.

    @method compile
    @for Ember.Handlebars
    @static
    @param {String} string The template to compile
    @return {Function}
  */
  Ember.Handlebars.compile = function(string) {
    var ast = Handlebars.parse(string);
    var options = { data: true, stringParams: true };
    var environment = new Ember.Handlebars.Compiler().compile(ast, options);
    var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);

    return Ember.Handlebars.template(templateSpec);
  };
}

<commit_msg>Fixed minor type of  to be , as both correctly spelt and referenced elsewhere in the surrounding comments<commit_after>/**
@module ember
@submodule ember-handlebars-compiler
*/

// Eliminate dependency on any Ember to simplify precompilation workflow
var objectCreate = Object.create || function(parent) {
  function F() {}
  F.prototype = parent;
  return new F();
};

var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars);
if (!Handlebars && typeof require === 'function') {
  Handlebars = require('handlebars');
}

Ember.assert("Ember Handlebars requires Handlebars version 1.0.0. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars);
Ember.assert("Ember Handlebars requires Handlebars version 1.0.0, COMPILER_REVISION expected: 4, got: " +  Handlebars.COMPILER_REVISION + " - Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 4);

/**
  Prepares the Handlebars templating library for use inside Ember's view
  system.

  The `Ember.Handlebars` object is the standard Handlebars library, extended to
  use Ember's `get()` method instead of direct property access, which allows
  computed properties to be used inside templates.

  To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`.
  This will return a function that can be used by `Ember.View` for rendering.

  @class Handlebars
  @namespace Ember
*/
Ember.Handlebars = objectCreate(Handlebars);

function makeBindings(options) {
  var hash = options.hash,
      hashType = options.hashTypes;

  for (var prop in hash) {
    if (hashType[prop] === 'ID') {
      hash[prop + 'Binding'] = hash[prop];
      hashType[prop + 'Binding'] = 'STRING';
      delete hash[prop];
      delete hashType[prop];
    }
  }
}

/**
  Register a bound helper or custom view helper.

  ## Simple bound helper example

  ```javascript
  Ember.Handlebars.helper('capitalize', function(value) {
    return value.toUpperCase();
  });
  ```

  The above bound helper can be used inside of templates as follows:

  ```handlebars
  {{capitalize name}}
  ```

  In this case, when the `name` property of the template's context changes,
  the rendered value of the helper will update to reflect this change.

  For more examples of bound helpers, see documentation for
  `Ember.Handlebars.registerBoundHelper`.

  ## Custom view helper example

  Assuming a view subclass named `App.CalendarView` were defined, a helper
  for rendering instances of this view could be registered as follows:

  ```javascript
  Ember.Handlebars.helper('calendar', App.CalendarView):
  ```

  The above bound helper can be used inside of templates as follows:

  ```handlebars
  {{calendar}}
  ```

  Which is functionally equivalent to:

  ```handlebars
  {{view App.CalendarView}}
  ```

  Options in the helper will be passed to the view in exactly the same
  manner as with the `view` helper.

  @method helper
  @for Ember.Handlebars
  @param {String} name
  @param {Function|Ember.View} function or view class constructor
  @param {String} dependentKeys*
*/
Ember.Handlebars.helper = function(name, value) {
  if (Ember.Component.detect(value)) {
    Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", name.match(/-/));

    var proto = value.proto();
    if (!proto.layoutName && !proto.templateName) {
      value.reopen({
        layoutName: 'components/' + name
      });
    }
  }

  if (Ember.View.detect(value)) {
    Ember.Handlebars.registerHelper(name, function(options) {
      Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View", arguments.length < 2);
      makeBindings(options);
      return Ember.Handlebars.helpers.view.call(this, value, options);
    });
  } else {
    Ember.Handlebars.registerBoundHelper.apply(null, arguments);
  }
};

/**
@class helpers
@namespace Ember.Handlebars
*/
Ember.Handlebars.helpers = objectCreate(Handlebars.helpers);

/**
  Override the the opcode compiler and JavaScript compiler for Handlebars.

  @class Compiler
  @namespace Ember.Handlebars
  @private
  @constructor
*/
Ember.Handlebars.Compiler = function() {};

// Handlebars.Compiler doesn't exist in runtime-only
if (Handlebars.Compiler) {
  Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
}

Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler;

/**
  @class JavaScriptCompiler
  @namespace Ember.Handlebars
  @private
  @constructor
*/
Ember.Handlebars.JavaScriptCompiler = function() {};

// Handlebars.JavaScriptCompiler doesn't exist in runtime-only
if (Handlebars.JavaScriptCompiler) {
  Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
  Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler;
}

Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars";

Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() {
  return "''";
};

/**
  @private

  Override the default buffer for Ember Handlebars. By default, Handlebars
  creates an empty String at the beginning of each invocation and appends to
  it. Ember's Handlebars overrides this to append to a single shared buffer.

  @method appendToBuffer
  @param string {String}
*/
Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
  return "data.buffer.push("+string+");";
};

var prefix = "ember" + (+new Date()), incr = 1;

/**
  @private

  Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that
  all simple mustaches in Ember's Handlebars will also set up an observer to
  keep the DOM up to date when the underlying property changes.

  @method mustache
  @for Ember.Handlebars.Compiler
  @param mustache
*/
Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
  if (mustache.isHelper && mustache.id.string === 'control') {
    mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
    mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
  } else if (mustache.params.length || mustache.hash) {
    // no changes required
  } else {
    var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);

    // Update the mustache node to include a hash value indicating whether the original node
    // was escaped. This will allow us to properly escape values when the underlying value
    // changes and we need to re-render the value.
    if (!mustache.escaped) {
      mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
      mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
    }
    mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
  }

  return Handlebars.Compiler.prototype.mustache.call(this, mustache);
};

/**
  Used for precompilation of Ember Handlebars templates. This will not be used
  during normal app execution.

  @method precompile
  @for Ember.Handlebars
  @static
  @param {String} string The template to precompile
*/
Ember.Handlebars.precompile = function(string) {
  var ast = Handlebars.parse(string);

  var options = {
    knownHelpers: {
      action: true,
      unbound: true,
      bindAttr: true,
      template: true,
      view: true,
      _triageMustache: true
    },
    data: true,
    stringParams: true
  };

  var environment = new Ember.Handlebars.Compiler().compile(ast, options);
  return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
};

// We don't support this for Handlebars runtime-only
if (Handlebars.compile) {
  /**
    The entry point for Ember Handlebars. This replaces the default
    `Handlebars.compile` and turns on template-local data and String
    parameters.

    @method compile
    @for Ember.Handlebars
    @static
    @param {String} string The template to compile
    @return {Function}
  */
  Ember.Handlebars.compile = function(string) {
    var ast = Handlebars.parse(string);
    var options = { data: true, stringParams: true };
    var environment = new Ember.Handlebars.Compiler().compile(ast, options);
    var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);

    return Ember.Handlebars.template(templateSpec);
  };
}

<|endoftext|>


<commit_before>/*!
 * VIZABI TIMESLIDER
 * Reusable timeslider component
 */

(function () {

  "use strict";

  var root = this;
  var Vizabi = root.Vizabi;
  var Promise = Vizabi.Promise;
  var utils = Vizabi.utils;
  var precision = 3;

  //warn client if d3 is not defined
  if (!Vizabi._require('d3')) {
    return;
  }

  //constants
  var class_playing = "vzb-playing";
  var class_hide_play = "vzb-ts-hide-play-button";
  var class_dragging = "vzb-ts-dragging";
  var class_axis_aligned = "vzb-ts-axis-aligned";
  var class_show_value = "vzb-ts-show-value";
  var class_show_value_when_drag_play = "vzb-ts-show-value-when-drag-play";

  var time_formats = {
    "year": "%Y",
    "month": "%b",
    "week": "week %U",
    "day": "%d/%m/%Y",
    "hour": "%d/%m/%Y %H",
    "minute": "%d/%m/%Y %H:%M",
    "second": "%d/%m/%Y %H:%M:%S"
  };

  //margins for slider
  var profiles = {
    small: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 8,
      label_spacing: 10
    },
    medium: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 10,
      label_spacing: 12
    },
    large: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 11,
      label_spacing: 14
    }
  };

  Vizabi.Component.extend("gapminder-timeslider", {
    /**
     * Initializes the timeslider.
     * Executed once before any template is rendered.
     * @param config The options passed to the component
     * @param context The component's parent
     */
    init: function (config, context) {
      this.template = this.template || "src/components/_gapminder/timeslider/timeslider.html";

      //define expected models/hooks for this component
      this.model_expects = [{
        name: "time",
        type: "time"
      }];

      var _this = this;

      //binds methods to this model
      this.model_binds = {
        'change:time': function (evt, original) {
          if ((['change:time:start', 'change:time:end']).indexOf(evt) !== -1) {
            _this.changeLimits();
          }
          _this._optionClasses();

          //only set handle position if change is external
          if (!_this._dragging) {
            _this._setHandle(_this.model.time.playing);
          }
        }
      };

      this.ui = utils.extend({
        show_limits: false,
        show_value: false,
        show_value_when_drag_play: true,
        show_button: true,
        class_axis_aligned: false
      }, config.ui, this.ui);

      // Same constructor as the superclass
      this._super(config, context);

      this._dragging = false;
      //defaults
      this.width = 0;
      this.height = 0;
    },

    //template is ready
    readyOnce: function () {
      var _this = this;

      //DOM to d3
      this.element = d3.select(this.element);

      //html elements
      this.slider_outer = this.element.select(".vzb-ts-slider");
      this.slider = this.slider_outer.select("g");
      this.axis = this.element.select(".vzb-ts-slider-axis");
      this.slide = this.element.select(".vzb-ts-slider-slide");
      this.handle = this.slide.select(".vzb-ts-slider-handle");
      this.valueText = this.slide.select('.vzb-ts-slider-value');

      //Scale
      this.xScale = d3.time.scale()
        .clamp(true);
      //Axis
      this.xAxis = d3.svg.axis()
        .orient("bottom")
        .tickSize(0);

      //Value
      this.valueText.attr("text-anchor", "middle").attr("dy", "-1em");

      var brushed = _this._getBrushed(),
        brushedEnd = _this._getBrushedEnd();

      //Brush for dragging
      this.brush = d3.svg.brush()
        .x(this.xScale)
        .extent([0, 0])
        .on("brush", function () {
          utils.throttle(brushed.bind(this), 30);
        })
        .on("brushend", function () {
          utils.throttle(brushedEnd.bind(this), 30);
        });

      //Slide
      this.slide.call(this.brush);
      this.slide.selectAll(".extent,.resize")
        .remove();

      this.parent.on('myEvent', function (evt, arg) {

        // set the right margin that depends on longest label width
        _this.element.select(".vzb-ts-slider-wrapper")
          .style("right", arg.mRight + "px");

        profiles[_this.getLayoutProfile()].margin =
        {bottom: 0, left: 0, right: 0, top: 0};

        _this.xScale.range([0, arg.rangeMax]);
        _this.resize();
      });

      var _this = this;

    },

    //template and model are ready
    ready: function () {

      var play = this.element.select(".vzb-ts-btn-play");
      var pause = this.element.select(".vzb-ts-btn-pause");
      var _this = this;
      var time = this.model.time;

      play.on('click', function () {
        _this._dragging = false;
        _this.model.time.play();
      });

      pause.on('click', function () {
        _this._dragging = false;
        _this.model.time.pause();
      });//format

      var fmt = time.formatOutput || time_formats[time.unit];
      this.format = d3.time.format(fmt);

      this.changeLimits();
      this.changeTime();
      this.resize();
      this._setHandle(this.model.time.playing);
    },

    changeLimits: function () {
      var minValue = this.model.time.start;
      var maxValue = this.model.time.end;
      //scale
      this.xScale.domain([minValue, maxValue]);
      //axis
      this.xAxis.tickValues([minValue, maxValue])
        .tickFormat(this.format);
    },

    changeTime: function () {
      this.ui.format = this.model.time.unit;
      //time slider should always receive a time model
      var time = this.model.time.value;
      //special classes
      this._optionClasses();
    },

    /**
     * Executes everytime the container or vizabi is resized
     * Ideally,it contains only operations related to size
     */
    resize: function () {

      this.model.time.pause();

      this.profile = profiles[this.getLayoutProfile()];

      var slider_w = parseInt(this.slider_outer.style("width"), 10);
      var slider_h = parseInt(this.slider_outer.style("height"), 10);
      this.width = slider_w - this.profile.margin.left - this.profile.margin.right;
      this.height = slider_h - this.profile.margin.bottom - this.profile.margin.top;
      var _this = this;

      //translate according to margins
      this.slider.attr("transform", "translate(" + this.profile.margin.left + "," + this.profile.margin.top + ")");

      //adjust scale width if it was not set manually before
      if (this.xScale.range()[1] = 1) this.xScale.range([0, this.width]);

      //adjust axis with scale
      this.xAxis = this.xAxis.scale(this.xScale)
        .tickPadding(this.profile.label_spacing);

      this.axis.attr("transform", "translate(0," + this.height / 2 + ")")
        .call(this.xAxis);

      this.slide.select(".background")
        .attr("height", this.height);

      //size of handle
      this.handle.attr("transform", "translate(0," + this.height / 2 + ")")
        .attr("r", this.profile.radius);

      this._setHandle();

    },

    /**
     * Gets brushed function to be executed when dragging
     * @returns {Function} brushed function
     */
    _getBrushed: function () {
      var _this = this;
      return function () {
        _this.model.time.pause();

        if (!_this._blockUpdate) {
          _this._optionClasses();
          _this._blockUpdate = true;
          _this.element.classed(class_dragging, true);
        }

        var value = _this.brush.extent()[0];

        //set brushed properties
        if (d3.event.sourceEvent) {
          _this._dragging = true;
          var posX = utils.roundStep(Math.round(d3.mouse(this)[0]), _this.width / (_this.model.time.end.getYear() - _this.model.time.start.getYear())) + 1;
          value = _this.xScale.invert(posX);

          var textWidth = _this.valueText.node().getBoundingClientRect().width;
          var sliderWidth = _this.slider.node().getBoundingClientRect().width - textWidth / 2;
          if (posX > sliderWidth)
            posX = sliderWidth;
          else if (posX < 0)
            posX = 0;

          //set handle position
          _this.handle.attr("cx", posX);
          _this.valueText.attr("transform", "translate(" + posX + "," + (_this.height / 2) + ")");
          _this.valueText.text(_this.format(value));
        }

        //set time according to dragged position
        if (value - _this.model.time.value !== 0) {
          _this._setTime(value);
        }
      };
    },

    /**
     * Gets brushedEnd function to be executed when dragging ends
     * @returns {Function} brushedEnd function
     */
    _getBrushedEnd: function () {
      var _this = this;
      return function () {
        _this._dragging = false;
        _this._blockUpdate = false;
        _this.element.classed(class_dragging, false);
        _this.model.time.pause();
        _this.model.time.snap();
      };
    },

    /**
     * Sets the handle to the correct position
     * @param {Boolean} transition whether to use transition or not
     */
    _setHandle: function (transition) {
      var value = this.model.time.value;
      this.slide.call(this.brush.extent([value, value]));

      this.valueText.text(this.format(value));

      var old_pos = this.handle.attr("cx");
      var new_pos = this.xScale(value);

      if(old_pos==null) old_pos = new_pos;
      var speed = new_pos > old_pos ? this.model.time.speed : 0;

      if (transition) {
        this.handle.attr("cx", old_pos)
          .transition()
          .duration(speed)
          .ease("linear")
          .attr("cx", new_pos);
      }
      else {
        this.handle.attr("cx", new_pos);
      }

      this.valueText.attr("transform", "translate(" + old_pos + "," + (this.height / 2) + ")")
        .transition()
        .duration(speed)
        .ease("linear")
        .attr("transform", "translate(" + new_pos + "," + (this.height / 2) + ")");
    },

    /**
     * Sets the current time model to time
     * @param {number} time The time
     */
    _setTime: function (time) {
      //update state
      var _this = this,
        frameRate = 50;

      //avoid updating more than once in "frameRate"
      var now = new Date();
      if (this._updTime != null && now - this._updTime < frameRate) return;
      this._updTime = now;

      _this.model.time.value = time;
    },

    /**
     * Applies some classes to the element according to options
     */
    _optionClasses: function () {
      //show/hide classes

      var show_limits = this.ui.show_limits;
      var show_value = this.ui.show_value;
      var show_value_when_drag_play = this.ui.show_value_when_drag_play;
      var axis_aligned = this.ui.axis_aligned;
      var show_play = (this.ui.show_button) && (this.model.time.playable);

      if (!show_limits) {
        this.xAxis.tickValues([]).ticks(0);
      }

      this.element.classed(class_hide_play, !show_play);
      this.element.classed(class_playing, this.model.time.playing);
      this.element.classed(class_show_value, show_value);
      this.element.classed(class_show_value_when_drag_play, show_value_when_drag_play);
      this.element.classed(class_axis_aligned, axis_aligned);
    }
  });

}).call(this);
<commit_msg>Fix update of text value in time slider<commit_after>/*!
 * VIZABI TIMESLIDER
 * Reusable timeslider component
 */

(function () {

  "use strict";

  var root = this;
  var Vizabi = root.Vizabi;
  var Promise = Vizabi.Promise;
  var utils = Vizabi.utils;
  var precision = 3;

  //warn client if d3 is not defined
  if (!Vizabi._require('d3')) {
    return;
  }

  //constants
  var class_playing = "vzb-playing";
  var class_hide_play = "vzb-ts-hide-play-button";
  var class_dragging = "vzb-ts-dragging";
  var class_axis_aligned = "vzb-ts-axis-aligned";
  var class_show_value = "vzb-ts-show-value";
  var class_show_value_when_drag_play = "vzb-ts-show-value-when-drag-play";

  var time_formats = {
    "year": "%Y",
    "month": "%b",
    "week": "week %U",
    "day": "%d/%m/%Y",
    "hour": "%d/%m/%Y %H",
    "minute": "%d/%m/%Y %H:%M",
    "second": "%d/%m/%Y %H:%M:%S"
  };

  //margins for slider
  var profiles = {
    small: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 8,
      label_spacing: 10
    },
    medium: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 10,
      label_spacing: 12
    },
    large: {
      margin: {top: 9, right: 15, bottom: 10, left: 15},
      radius: 11,
      label_spacing: 14
    }
  };

  Vizabi.Component.extend("gapminder-timeslider", {
    /**
     * Initializes the timeslider.
     * Executed once before any template is rendered.
     * @param config The options passed to the component
     * @param context The component's parent
     */
    init: function (config, context) {
      this.template = this.template || "src/components/_gapminder/timeslider/timeslider.html";

      //define expected models/hooks for this component
      this.model_expects = [{
        name: "time",
        type: "time"
      }];

      var _this = this;

      //binds methods to this model
      this.model_binds = {
        'change:time': function (evt, original) {
          if ((['change:time:start', 'change:time:end']).indexOf(evt) !== -1) {
            _this.changeLimits();
          }
          _this._optionClasses();

          //only set handle position if change is external
          if (!_this._dragging) {
            _this._setHandle(_this.model.time.playing);
          }
        }
      };

      this.ui = utils.extend({
        show_limits: false,
        show_value: false,
        show_value_when_drag_play: true,
        show_button: true,
        class_axis_aligned: false
      }, config.ui, this.ui);

      // Same constructor as the superclass
      this._super(config, context);

      this._dragging = false;
      //defaults
      this.width = 0;
      this.height = 0;
    },

    //template is ready
    readyOnce: function () {
      var _this = this;

      //DOM to d3
      this.element = d3.select(this.element);

      //html elements
      this.slider_outer = this.element.select(".vzb-ts-slider");
      this.slider = this.slider_outer.select("g");
      this.axis = this.element.select(".vzb-ts-slider-axis");
      this.slide = this.element.select(".vzb-ts-slider-slide");
      this.handle = this.slide.select(".vzb-ts-slider-handle");
      this.valueText = this.slide.select('.vzb-ts-slider-value');

      //Scale
      this.xScale = d3.time.scale()
        .clamp(true);
      //Axis
      this.xAxis = d3.svg.axis()
        .orient("bottom")
        .tickSize(0);

      //Value
      this.valueText.attr("text-anchor", "middle").attr("dy", "-1em");

      var brushed = _this._getBrushed(),
        brushedEnd = _this._getBrushedEnd();

      //Brush for dragging
      this.brush = d3.svg.brush()
        .x(this.xScale)
        .extent([0, 0])
        .on("brush", function () {
          brushed.call(this);
        })
        .on("brushend", function () {
          brushedEnd.call(this);
        });

      //Slide
      this.slide.call(this.brush);
      this.slide.selectAll(".extent,.resize")
        .remove();

      this.parent.on('myEvent', function (evt, arg) {

        // set the right margin that depends on longest label width
        _this.element.select(".vzb-ts-slider-wrapper")
          .style("right", arg.mRight + "px");

        profiles[_this.getLayoutProfile()].margin =
        {bottom: 0, left: 0, right: 0, top: 0};

        _this.xScale.range([0, arg.rangeMax]);
        _this.resize();
      });

      var _this = this;

    },

    //template and model are ready
    ready: function () {

      var play = this.element.select(".vzb-ts-btn-play");
      var pause = this.element.select(".vzb-ts-btn-pause");
      var _this = this;
      var time = this.model.time;

      play.on('click', function () {
        _this._dragging = false;
        _this.model.time.play();
      });

      pause.on('click', function () {
        _this._dragging = false;
        _this.model.time.pause();
      });//format

      var fmt = time.formatOutput || time_formats[time.unit];
      this.format = d3.time.format(fmt);

      this.changeLimits();
      this.changeTime();
      this.resize();
      this._setHandle(this.model.time.playing);
    },

    changeLimits: function () {
      var minValue = this.model.time.start;
      var maxValue = this.model.time.end;
      //scale
      this.xScale.domain([minValue, maxValue]);
      //axis
      this.xAxis.tickValues([minValue, maxValue])
        .tickFormat(this.format);
    },

    changeTime: function () {
      this.ui.format = this.model.time.unit;
      //time slider should always receive a time model
      var time = this.model.time.value;
      //special classes
      this._optionClasses();
    },

    /**
     * Executes everytime the container or vizabi is resized
     * Ideally,it contains only operations related to size
     */
    resize: function () {

      this.model.time.pause();

      this.profile = profiles[this.getLayoutProfile()];

      var slider_w = parseInt(this.slider_outer.style("width"), 10);
      var slider_h = parseInt(this.slider_outer.style("height"), 10);
      this.width = slider_w - this.profile.margin.left - this.profile.margin.right;
      this.height = slider_h - this.profile.margin.bottom - this.profile.margin.top;
      var _this = this;

      //translate according to margins
      this.slider.attr("transform", "translate(" + this.profile.margin.left + "," + this.profile.margin.top + ")");

      //adjust scale width if it was not set manually before
      if (this.xScale.range()[1] = 1) this.xScale.range([0, this.width]);

      //adjust axis with scale
      this.xAxis = this.xAxis.scale(this.xScale)
        .tickPadding(this.profile.label_spacing);

      this.axis.attr("transform", "translate(0," + this.height / 2 + ")")
        .call(this.xAxis);

      this.slide.select(".background")
        .attr("height", this.height);

      //size of handle
      this.handle.attr("transform", "translate(0," + this.height / 2 + ")")
        .attr("r", this.profile.radius);

      this._setHandle();

    },

    /**
     * Gets brushed function to be executed when dragging
     * @returns {Function} brushed function
     */
    _getBrushed: function () {
      var _this = this;
      return function () {
        _this.model.time.pause();

        if (!_this._blockUpdate) {
          _this._optionClasses();
          _this._blockUpdate = true;
          _this.element.classed(class_dragging, true);
        }

        var value = _this.brush.extent()[0];

        //set brushed properties
        if (d3.event.sourceEvent) {
          _this._dragging = true;
          var posX = utils.roundStep(Math.round(d3.mouse(this)[0]), _this.width / (_this.model.time.end.getYear() - _this.model.time.start.getYear())) + 1;
          value = _this.xScale.invert(posX);

          var textWidth = _this.valueText.node().getBoundingClientRect().width;
          var sliderWidth = _this.slider.node().getBoundingClientRect().width - textWidth / 2;
          if (posX > sliderWidth)
            posX = sliderWidth;
          else if (posX < 0)
            posX = 0;

          //set handle position
          _this.handle.attr("cx", posX);
          _this.valueText.attr("transform", "translate(" + posX + "," + (_this.height / 2) + ")");
          _this.valueText.text(_this.format(value));
        }

        //set time according to dragged position
        if (value - _this.model.time.value !== 0) {
          _this._setTime(value);
        }
      };
    },

    /**
     * Gets brushedEnd function to be executed when dragging ends
     * @returns {Function} brushedEnd function
     */
    _getBrushedEnd: function () {
      var _this = this;
      return function () {
        _this._dragging = false;
        _this._blockUpdate = false;
        _this.element.classed(class_dragging, false);
        _this.model.time.pause();
        _this.model.time.snap();
      };
    },

    /**
     * Sets the handle to the correct position
     * @param {Boolean} transition whether to use transition or not
     */
    _setHandle: function (transition) {
      var value = this.model.time.value;
      this.slide.call(this.brush.extent([value, value]));

      this.valueText.text(this.format(value));

      var old_pos = this.handle.attr("cx");
      var new_pos = this.xScale(value);

      if(old_pos==null) old_pos = new_pos;
      var speed = new_pos > old_pos ? this.model.time.speed : 0;

      if (transition) {
        this.handle.attr("cx", old_pos)
          .transition()
          .duration(speed)
          .ease("linear")
          .attr("cx", new_pos);
      }
      else {
        this.handle.attr("cx", new_pos);
      }

      this.valueText.attr("transform", "translate(" + old_pos + "," + (this.height / 2) + ")")
        .transition()
        .duration(speed)
        .ease("linear")
        .attr("transform", "translate(" + new_pos + "," + (this.height / 2) + ")");
    },

    /**
     * Sets the current time model to time
     * @param {number} time The time
     */
    _setTime: function (time) {
      //update state
      var _this = this,
        frameRate = 50;

      //avoid updating more than once in "frameRate"
      var now = new Date();
      if (this._updTime != null && now - this._updTime < frameRate) return;
      this._updTime = now;

      _this.model.time.value = time;
    },

    /**
     * Applies some classes to the element according to options
     */
    _optionClasses: function () {
      //show/hide classes

      var show_limits = this.ui.show_limits;
      var show_value = this.ui.show_value;
      var show_value_when_drag_play = this.ui.show_value_when_drag_play;
      var axis_aligned = this.ui.axis_aligned;
      var show_play = (this.ui.show_button) && (this.model.time.playable);

      if (!show_limits) {
        this.xAxis.tickValues([]).ticks(0);
      }

      this.element.classed(class_hide_play, !show_play);
      this.element.classed(class_playing, this.model.time.playing);
      this.element.classed(class_show_value, show_value);
      this.element.classed(class_show_value_when_drag_play, show_value_when_drag_play);
      this.element.classed(class_axis_aligned, axis_aligned);
    }
  });

}).call(this);
<|endoftext|>


<commit_before>/* Copyright 2015 Bloomberg Finance L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define(["./d3", "./MarkModel"], function(d3, MarkModelModule) {
    "use strict";

    var OHLCModel = MarkModelModule.MarkModel.extend({
        initialize: function() {
            OHLCModel.__super__.initialize.apply(this);
            this.on_some_change(["x", "y"], this.update_data, this);
            this.on_some_change(["preserve_domain"], this.update_domains, this);
            this.on("change:format", this.update_format, this);
            this.px = { open: -1, high: -1, low: -1, close: -1 };
            this.mark_data = [];
        },
        update_format: function() {
            this.update_data();
            this.trigger("format_updated");
        },
        update_data: function() {
            var x_data = this.get_typed_field("x");
            var y_data = this.get_typed_field("y");
            var format = this.get("format");
            this.px = { open: -1, high: -1, low: -1, close: -1 };
            var charmap = { o: -1, h: -1, l: -1, c: -1 };

            // Local private function to report errors in format
            function print_bad_format(format) {
                if(console) {
                    console.error("Invalid OHLC format: '" + format + "'");
                }
            }

            this.charmap = format.toLowerCase().split("")
                .reduce(function(dict, key, val)
                {
                    if(dict[key] !== -1) {
                        print_bad_format(format);
                        x_data = [];
                        y_data = [];
                    }
                    dict[key] = val;
                    return dict;
                },
                charmap);
            // Copy to human-readable dictionary
            this.px.open = charmap.o;
            this.px.high = charmap.h;
            this.px.low = charmap.l;
            this.px.close = charmap.c;

            // We cannot have high without low and vice versa
            if((this.px.high !== -1 && this.px.low === -1)
            || (this.px.high === -1 && this.px.low !== -1)) {
                print_bad_format(format);
                x_data = [];
                y_data = [];
            } else {
                // Verify that OHLC data is valid
                var that = this;
                if(this.px.high !== -1
                && !y_data.every(function(d) {
                    return (d[that.px.high] === d3.max(d) &&
                            d[that.px.low] === d3.min(d) &&
                            d.length === format.length); }))
                {
                    x_data = [];
                    y_data = [];
                    if(console) console.error("Invalid OHLC data");
                }
            }

            if(x_data.length > y_data.length) {
                x_data = x_data.slice(0, y_data.length);
            } else if(x_data.length < y_data.length) {
                y_data = y_data.slice(0, x_data.length);
            }

            this.mark_data = _.zip(x_data, y_data);
            this.update_domains();
            this.trigger("data_updated");
        },
        update_domains: function() {
            if(!this.mark_data) {
                return;
            }
            var that = this;
            var scales = this.get("scales");
            var x_scale = scales["x"], y_scale = scales["y"];
            var min_x_dist = Number.POSITIVE_INFINITY;
            var max_y_height = 0, dist = 0, height = 0;

            /*
             * Compute the minimum x distance between the data points. We will
             * use this to pad either side of the x domain.
             * Also compute the maximum height of all of the marks (i.e. maximum
             * distance from high to low) and use that to pad the y domain.
             */
            for(var i = 0; i < this.mark_data.length; i++) {
                if(i > 0) {
                    dist = this.mark_data[i][0] - this.mark_data[i-1][0];
                    if(dist < min_x_dist) min_x_dist = dist;
                }
                height = this.mark_data[i][this.px.high] -
                            this.mark_data[i][this.px.low];
                if(height > max_y_height) max_y_height = height;
            }
            if(this.mark_data.length < 2) {
                min_x_dist = 0;
            }

            if((!this.get("preserve_domain")["x"]) && this.mark_data.length !== 0) {
                var min = d3.min(this.mark_data.map(function(d) {
                    return d[0];
                }));
                var max = d3.max(this.mark_data.map(function(d) {
                    return d[0];
                }));
                if(max instanceof Date) max = max.getTime();
                x_scale.set_domain([min - min_x_dist/2, max + min_x_dist/2], this.id);
            } else {
                x_scale.del_domain([], this.id);
            }

            if((!this.get("preserve_domain")["y"]) && this.mark_data.length !== 0) {
                // Remember that elem contains OHLC data here so we cannot use
                // compute_and_set_domain
                var top = this.px.high;
                var bottom = this.px.low;
                if(top === -1 || bottom === -1) {
                    top = this.px.open;
                    bottom = this.px.close;
                }
                var min = d3.min(this.mark_data.map(function(d) {
                    return (d[1][bottom] < d[1][top]) ? d[1][bottom] : d[1][top];
                }));
                var max = d3.max(this.mark_data.map(function(d) {
                    return (d[1][top] > d[1][bottom]) ? d[1][top] : d[1][bottom];
                }));
                if(max instanceof  Date) max = max.getTime();
                y_scale.set_domain([min - max_y_height, max + max_y_height], this.id);
            } else {
                y_scale.del_domain([], this.id);
            }
        },
    });

    return {
        OHLCModel: OHLCModel,
    };
});

<commit_msg>Fixed a bug in the verification process<commit_after>/* Copyright 2015 Bloomberg Finance L.P.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

define(["./d3", "./MarkModel"], function(d3, MarkModelModule) {
    "use strict";

    var OHLCModel = MarkModelModule.MarkModel.extend({
        initialize: function() {
            OHLCModel.__super__.initialize.apply(this);
            this.on_some_change(["x", "y"], this.update_data, this);
            this.on_some_change(["preserve_domain"], this.update_domains, this);
            this.on("change:format", this.update_format, this);
            this.px = { open: -1, high: -1, low: -1, close: -1 };
            this.mark_data = [];
        },
        update_format: function() {
            this.update_data();
            this.trigger("format_updated");
        },
        update_data: function() {
            var x_data = this.get_typed_field("x");
            var y_data = this.get_typed_field("y");
            var format = this.get("format");
            this.px = { open: -1, high: -1, low: -1, close: -1 };
            var charmap = { o: -1, h: -1, l: -1, c: -1 };

            // Local private function to report errors in format
            function print_bad_format(format) {
                if(console) {
                    console.error("Invalid OHLC format: '" + format + "'");
                }
            }

            this.charmap = format.toLowerCase().split("")
                .reduce(function(dict, key, val)
                {
                    if(dict[key] !== -1) {
                        print_bad_format(format);
                        x_data = [];
                        y_data = [];
                    }
                    dict[key] = val;
                    return dict;
                },
                charmap);
            // Copy to human-readable dictionary
            this.px.open = charmap.o;
            this.px.high = charmap.h;
            this.px.low = charmap.l;
            this.px.close = charmap.c;

            // We cannot have high without low and vice versa
            if((this.px.high !== -1 && this.px.low === -1)
            || (this.px.high === -1 && this.px.low !== -1)
            || format.length < 2 || format.length > 4) {
                print_bad_format(format);
                x_data = [];
                y_data = [];
            } else {
                // Verify that OHLC data is valid
                var that = this;
                if((this.px.high !== -1
                && !y_data.every(function(d) {
                    return (d[that.px.high] === d3.max(d) &&
                            d[that.px.low] === d3.min(d)); }))
                || !y_data.every(function(d) {
                    return d.length === format.length; }))
                {
                    x_data = [];
                    y_data = [];
                    if(console) console.error("Invalid OHLC data");
                }
            }

            // Make x and y data the same length
            if(x_data.length > y_data.length) {
                x_data = x_data.slice(0, y_data.length);
            } else if(x_data.length < y_data.length) {
                y_data = y_data.slice(0, x_data.length);
            }

            this.mark_data = _.zip(x_data, y_data);
            this.update_domains();
            this.trigger("data_updated");
        },
        update_domains: function() {
            if(!this.mark_data) {
                return;
            }
            var that = this;
            var scales = this.get("scales");
            var x_scale = scales["x"], y_scale = scales["y"];
            var min_x_dist = Number.POSITIVE_INFINITY;
            var max_y_height = 0, dist = 0, height = 0;

            /*
             * Compute the minimum x distance between the data points. We will
             * use this to pad either side of the x domain.
             * Also compute the maximum height of all of the marks (i.e. maximum
             * distance from high to low) and use that to pad the y domain.
             */
            for(var i = 0; i < this.mark_data.length; i++) {
                if(i > 0) {
                    dist = this.mark_data[i][0] - this.mark_data[i-1][0];
                    if(dist < min_x_dist) min_x_dist = dist;
                }
                height = this.mark_data[i][this.px.high] -
                            this.mark_data[i][this.px.low];
                if(height > max_y_height) max_y_height = height;
            }
            if(this.mark_data.length < 2) {
                min_x_dist = 0;
            }

            if((!this.get("preserve_domain")["x"]) && this.mark_data.length !== 0) {
                var min = d3.min(this.mark_data.map(function(d) {
                    return d[0];
                }));
                var max = d3.max(this.mark_data.map(function(d) {
                    return d[0];
                }));
                if(max instanceof Date) max = max.getTime();
                x_scale.set_domain([min - min_x_dist/2, max + min_x_dist/2], this.id);
            } else {
                x_scale.del_domain([], this.id);
            }

            if((!this.get("preserve_domain")["y"]) && this.mark_data.length !== 0) {
                // Remember that elem contains OHLC data here so we cannot use
                // compute_and_set_domain
                var top = this.px.high;
                var bottom = this.px.low;
                if(top === -1 || bottom === -1) {
                    top = this.px.open;
                    bottom = this.px.close;
                }
                var min = d3.min(this.mark_data.map(function(d) {
                    return (d[1][bottom] < d[1][top]) ? d[1][bottom] : d[1][top];
                }));
                var max = d3.max(this.mark_data.map(function(d) {
                    return (d[1][top] > d[1][bottom]) ? d[1][top] : d[1][bottom];
                }));
                if(max instanceof  Date) max = max.getTime();
                y_scale.set_domain([min - max_y_height, max + max_y_height], this.id);
            } else {
                y_scale.del_domain([], this.id);
            }
        },
    });

    return {
        OHLCModel: OHLCModel,
    };
});

<|endoftext|>


<commit_before>/*jshint debug:true*/

require('ember-handlebars/ext');

/**
@module ember
@submodule ember-handlebars
*/

var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath;

/**
  `log` allows you to output the value of a value in the current rendering
  context.

  ```handlebars
  {{log myVariable}}
  ```

  @method log
  @for Ember.Handlebars.helpers
  @param {String} property
*/
Ember.Handlebars.registerHelper('log', function(property, options) {
  var context = (options.contexts && options.contexts[0]) || this,
      normalized = normalizePath(context, property, options.data),
      pathRoot = normalized.root,
      path = normalized.path,
      value = (path === 'this') ? pathRoot : handlebarsGet(pathRoot, path, options);
  Ember.Logger.log(value);
});

/**
  Execute the `debugger` statement in the current context.

  ```handlebars
  {{debugger}}
  ```

  @method debugger
  @for Ember.Handlebars.helpers
  @param {String} property
*/
Ember.Handlebars.registerHelper('debugger', function() {
  debugger;
});
<commit_msg>Expose `options` arg in `debugger` HB helper<commit_after>/*jshint debug:true*/

require('ember-handlebars/ext');

/**
@module ember
@submodule ember-handlebars
*/

var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath;

/**
  `log` allows you to output the value of a value in the current rendering
  context.

  ```handlebars
  {{log myVariable}}
  ```

  @method log
  @for Ember.Handlebars.helpers
  @param {String} property
*/
Ember.Handlebars.registerHelper('log', function(property, options) {
  var context = (options.contexts && options.contexts[0]) || this,
      normalized = normalizePath(context, property, options.data),
      pathRoot = normalized.root,
      path = normalized.path,
      value = (path === 'this') ? pathRoot : handlebarsGet(pathRoot, path, options);
  Ember.Logger.log(value);
});

/**
  Execute the `debugger` statement in the current context.

  ```handlebars
  {{debugger}}
  ```

  @method debugger
  @for Ember.Handlebars.helpers
  @param {String} property
*/
Ember.Handlebars.registerHelper('debugger', function(options) {
  debugger;
});
<|endoftext|>


<commit_before>import { eachComponent } from '../../../utils/utils';

export default [
  {
    type: 'select',
    input: true,
    weight: 0,
    tooltip: 'The source to use for the select data. Values lets you provide your own values and labels. JSON lets you provide raw JSON data. URL lets you provide a URL to retrieve the JSON data from.',
    key: 'dataSrc',
    defaultValue: 'values',
    label: 'Data Source Type',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Values', value: 'values' },
        { label: 'URL', value: 'url' },
        { label: 'Resource', value: 'resource' },
        { label: 'Custom', value: 'custom' },
        { label: 'Raw JSON', value: 'json' },
        { label: 'IndexedDB', value: 'indexeddb' },
      ],
    },
  },
  {
    type: 'textfield',
    weight: 10,
    input: true,
    key: 'indexeddb.database',
    label: 'Database name',
    tooltip: 'The name of the indexeddb database.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'indexeddb.table',
    label: 'Table name',
    weight: 16,
    tooltip: 'The name of table in the indexeddb database.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    }
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 18,
    input: true,
    key: 'indexeddb.filter',
    label: 'Row Filter',
    tooltip: 'Filter table items that match the object.',
    defaultValue: {},
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    },
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 10,
    input: true,
    key: 'data.json',
    label: 'Data Source Raw JSON',
    tooltip: 'A raw JSON array to use as a data source.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'json'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'data.url',
    weight: 10,
    label: 'Data Source URL',
    placeholder: 'Data Source URL',
    tooltip: 'A URL that returns a JSON array to use as the data source.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    label: 'Lazy Load Data',
    key: 'lazyLoad',
    tooltip: 'When set, this will not fire off the request to the URL until this control is within focus. This can improve performance if you have many Select dropdowns on your form where the API\'s will only fire when the control is activated.',
    weight: 11,
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'resource',
            'url',
          ],
        ],
      },
    },
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Request Headers',
    key: 'data.headers',
    tooltip: 'Set any headers that should be sent along with the request to the url. This is useful for authentication.',
    weight: 11,
    components: [
      {
        label: 'Key',
        key: 'key',
        input: true,
        type: 'textfield',
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
      },
    ],
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Data Source Values',
    key: 'data.values',
    tooltip: 'Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission.',
    weight: 10,
    reorder: true,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield',
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] },
      },
    ],
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'values'] },
    },
  },
  {
    type: 'select',
    input: true,
    dataSrc: 'url',
    data: {
      url: '/form?type=resource&limit=4294967295&select=_id,title',
    },
    authenticate: true,
    template: '{{ item.title }}',
    valueProperty: '_id',
    clearOnHide: false,
    label: 'Resource',
    key: 'data.resource',
    lazyLoad: false,
    weight: 10,
    tooltip: 'The resource to be used with this field.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Data Path',
    key: 'selectValues',
    weight: 12,
    description: 'The object path to the iterable items.',
    tooltip: 'The property within the source data, where iterable items reside. For example: results.items or results[0].items',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'select',
    input: true,
    label: 'Value Property',
    key: 'valueProperty',
    skipMerge: true,
    clearOnHide: false,
    tooltip: 'The field to use as the value.',
    weight: 11,
    refreshOn: 'data.resource',
    template: '{{ item.label }}',
    valueProperty: 'key',
    dataSrc: 'url',
    lazyLoad: false,
    onSetItems(component, form) {
      const newItems = [];

      eachComponent(form.components, (component, path) => {
        if (component.input) {
          newItems.push({
            label: component.label || component.key,
            key: `data.${path}`
          });
        }
      });
      return newItems;
    },
    onChange(context) {
      if (context && context.flags && context.flags.modified) {
        const valueProp = context.instance.data.valueProperty;
        const templateProp = valueProp ? valueProp : 'data';
        const template = `<span>{{ item.${templateProp} }}</span>`;
        const searchField = valueProp ? `${valueProp}__regex` : '';
        context.instance.root.getComponent('template').setValue(template);
        context.instance.root.getComponent('searchField').setValue(searchField);
      }
    },
    data: {
      url: '/form/{{ data.data.resource }}',
    },
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { var: 'data.data.resource' },
        ],
      },
    },
  },
  {
    type: 'select',
    input: true,
    label: 'Storage Type',
    key: 'dataType',
    clearOnHide: true,
    tooltip: 'The type to store the data. If you select something other than autotype, it will force it to that type.',
    weight: 12,
    template: '{{ item.label }}',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Autotype', value: 'auto' },
        { label: 'String', value: 'string' },
        { label: 'Number', value: 'number' },
        { label: 'Boolean', value: 'boolean' },
        { label: 'Object', value: 'object' },
      ],
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Value Property',
    key: 'valueProperty',
    skipMerge: true,
    clearOnHide: false,
    weight: 13,
    description: "The selected item's property to save.",
    tooltip: 'The property of each item in the data source to use as the select value. If not specified, the item itself will be used.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'json',
            'url',
            'custom'
          ],
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Select Fields',
    key: 'selectFields',
    tooltip: 'The properties on the resource to return as part of the options. Separate property names by commas. If left blank, all properties will be returned.',
    placeholder: 'Comma separated list of fields to select.',
    weight: 14,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { '===': [{ var: 'data.valueProperty' }, ''] },
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    key: 'disableLimit',
    label: 'Disable limiting response',
    tooltip: 'When enabled the request will not include the limit and skip options in the query string',
    weight: 15,
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'searchField',
    label: 'Search Query Name',
    weight: 16,
    description: 'Name of URL query parameter',
    tooltip: 'The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'number',
    input: true,
    key: 'minSearch',
    weight: 17,
    label: 'Minimum Search Length',
    tooltip: 'The minimum amount of characters they must type before a search is made.',
    defaultValue: 0,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'url'] },
          { '!=': [{ var: 'data.searchField' }, ''] },
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'filter',
    label: 'Filter Query',
    weight: 18,
    description: 'The filter query for results.',
    tooltip: 'Use this to provide additional filtering using query parameters.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'sort',
    label: 'Sort Query',
    weight: 18,
    description: 'The sort query for results',
    tooltip: 'Use this to provide additional sorting using query parameters',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'number',
    input: true,
    key: 'limit',
    label: 'Limit',
    weight: 18,
    defaultValue: 100,
    description: 'Maximum number of items to view per page of results.',
    tooltip: 'Use this to limit the number of items to request or view.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource'
          ],
        ],
      },
    },
  },
  {
    type: 'textarea',
    input: true,
    key: 'data.custom',
    label: 'Custom Values',
    editor: 'ace',
    rows: 10,
    weight: 14,
    placeholder: "values = data['mykey'];",
    tooltip: 'Write custom code to return the value options. The form data object is available.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'custom'] },
    },
  },
  {
    type: 'textarea',
    input: true,
    key: 'template',
    label: 'Item Template',
    editor: 'ace',
    as: 'html',
    rows: 3,
    weight: 18,
    tooltip: 'The HTML template for the result data items.',
    allowCalculateOverride: true,
    calculateValue:(context) => {
      if (!context.data.template) {
        if (context.instance && context.instance._currentForm.options.editComponent) {
          return context.instance._currentForm.options.editComponent.template;
        }
      }

      return context.data.template;
    }
  },
  {
    type: 'select',
    input: true,
    key: 'refreshOn',
    label: 'Refresh Options On',
    weight: 19,
    tooltip: 'Refresh data when another field changes.',
    dataSrc: 'custom',
    valueProperty: 'value',
    data: {
      custom(context) {
        var values = [];
        values.push({ label: 'Any Change', value: 'data' });
        context.utils.eachComponent(context.instance.options.editForm.components, function(component, path) {
          if (component.key !== context.data.key) {
            values.push({
              label: component.label || component.key,
              value: path
            });
          }
        });
        return values;
      }
    },
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 20,
    key: 'clearOnRefresh',
    label: 'Clear Value On Refresh Options',
    defaultValue: false,
    tooltip: 'When the Refresh On field is changed, clear this components value.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 21,
    key: 'searchEnabled',
    label: 'Enable Static Search',
    defaultValue: true,
    tooltip: 'When checked, the select dropdown will allow for searching within the static list of items provided.',
  },
  {
    label: 'Search Threshold',
    mask: false,
    tableView: true,
    alwaysEnabled: false,
    type: 'number',
    input: true,
    key: 'selectThreshold',
    validate: {
      min: 0,
      customMessage: '',
      json: '',
      max: 1,
    },
    delimiter: false,
    requireDecimal: false,
    encrypted: false,
    defaultValue: 0.3,
    weight: 22,
    tooltip: 'At what point does the match algorithm give up. A threshold of 0.0 requires a perfect match, a threshold of 1.0 would match anything.',
  },
  {
    type: 'checkbox',
    input: true,
    weight: 23,
    key: 'addResource',
    label: 'Add Resource',
    tooltip: 'Allows to create a new resource while entering a submission.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'textfield',
    label: 'Add Resource Label',
    key: 'addResourceLabel',
    tooltip: 'Set the text of the Add Resource button.',
    placeholder: 'Add Resource',
    weight: 24,
    input: true,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { '!!': { var: 'data.addResource' } },
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 25,
    key: 'reference',
    label: 'Save as reference',
    tooltip: 'Using this option will save this field as a reference and link its value to the value of the origin record.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 26,
    key: 'authenticate',
    label: 'Formio Authenticate',
    tooltip: 'Check this if you would like to use Formio Authentication with the request.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 27,
    key: 'readOnlyValue',
    label: 'Read Only Value',
    tooltip: 'Check this if you would like to show just the value when in Read Only mode.',
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 28,
    input: true,
    key: 'customOptions',
    label: 'Choices.js options',
    tooltip: 'A raw JSON object to use as options for the Select component (Choices JS).',
    defaultValue: {},
  },
];
<commit_msg>Feat(Select) : add options for values<commit_after>import { eachComponent } from '../../../utils/utils';

export default [
  {
    type: 'select',
    input: true,
    weight: 0,
    tooltip: 'The source to use for the select data. Values lets you provide your own values and labels. JSON lets you provide raw JSON data. URL lets you provide a URL to retrieve the JSON data from.',
    key: 'dataSrc',
    defaultValue: 'values',
    label: 'Data Source Type',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Values', value: 'values' },
        { label: 'URL', value: 'url' },
        { label: 'Resource', value: 'resource' },
        { label: 'Custom', value: 'custom' },
        { label: 'Raw JSON', value: 'json' },
        { label: 'IndexedDB', value: 'indexeddb' },
      ],
    },
  },
  {
    type: 'textfield',
    weight: 10,
    input: true,
    key: 'indexeddb.database',
    label: 'Database name',
    tooltip: 'The name of the indexeddb database.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'indexeddb.table',
    label: 'Table name',
    weight: 16,
    tooltip: 'The name of table in the indexeddb database.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    }
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 18,
    input: true,
    key: 'indexeddb.filter',
    label: 'Row Filter',
    tooltip: 'Filter table items that match the object.',
    defaultValue: {},
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'indexeddb'] },
    },
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 10,
    input: true,
    key: 'data.json',
    label: 'Data Source Raw JSON',
    tooltip: 'A raw JSON array to use as a data source.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'json'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'data.url',
    weight: 10,
    label: 'Data Source URL',
    placeholder: 'Data Source URL',
    tooltip: 'A URL that returns a JSON array to use as the data source.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    label: 'Lazy Load Data',
    key: 'lazyLoad',
    tooltip: 'When set, this will not fire off the request to the URL until this control is within focus. This can improve performance if you have many Select dropdowns on your form where the API\'s will only fire when the control is activated.',
    weight: 11,
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'resource',
            'url',
          ],
        ],
      },
    },
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Request Headers',
    key: 'data.headers',
    tooltip: 'Set any headers that should be sent along with the request to the url. This is useful for authentication.',
    weight: 11,
    components: [
      {
        label: 'Key',
        key: 'key',
        input: true,
        type: 'textfield',
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
      },
    ],
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'datagrid',
    input: true,
    label: 'Data Source Values',
    key: 'data.values',
    tooltip: 'Values to use as the data source. Labels are shown in the select field. Values are the corresponding values saved with the submission.',
    weight: 10,
    reorder: true,
    defaultValue: [{ label: '', value: '' }],
    components: [
      {
        label: 'Label',
        key: 'label',
        input: true,
        type: 'textfield',
      },
      {
        label: 'Value',
        key: 'value',
        input: true,
        type: 'textfield',
        allowCalculateOverride: true,
        calculateValue: { _camelCase: [{ var: 'row.label' }] },
      },
    ],
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'values'] },
    },
  },
  {
    type: 'select',
    input: true,
    dataSrc: 'url',
    data: {
      url: '/form?type=resource&limit=4294967295&select=_id,title',
    },
    authenticate: true,
    template: '{{ item.title }}',
    valueProperty: '_id',
    clearOnHide: false,
    label: 'Resource',
    key: 'data.resource',
    lazyLoad: false,
    weight: 10,
    tooltip: 'The resource to be used with this field.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Data Path',
    key: 'selectValues',
    weight: 12,
    description: 'The object path to the iterable items.',
    tooltip: 'The property within the source data, where iterable items reside. For example: results.items or results[0].items',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'select',
    input: true,
    label: 'Value Property',
    key: 'valueProperty',
    skipMerge: true,
    clearOnHide: false,
    tooltip: 'The field to use as the value.',
    weight: 11,
    refreshOn: 'data.resource',
    template: '{{ item.label }}',
    valueProperty: 'key',
    dataSrc: 'url',
    lazyLoad: false,
    onSetItems(component, form) {
      const newItems = [];

      eachComponent(form.components, (component, path) => {
        if (component.input) {
          newItems.push({
            label: component.label || component.key,
            key: `data.${path}`
          });
        }
      });
      return newItems;
    },
    onChange(context) {
      if (context && context.flags && context.flags.modified) {
        const valueProp = context.instance.data.valueProperty;
        const templateProp = valueProp ? valueProp : 'data';
        const template = `<span>{{ item.${templateProp} }}</span>`;
        const searchField = valueProp ? `${valueProp}__regex` : '';
        context.instance.root.getComponent('template').setValue(template);
        context.instance.root.getComponent('searchField').setValue(searchField);
      }
    },
    data: {
      url: '/form/{{ data.data.resource }}',
    },
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { var: 'data.data.resource' },
        ],
      },
    },
  },
  {
    type: 'select',
    input: true,
    label: 'Storage Type',
    key: 'dataType',
    clearOnHide: true,
    tooltip: 'The type to store the data. If you select something other than autotype, it will force it to that type.',
    weight: 12,
    template: '{{ item.label }}',
    dataSrc: 'values',
    data: {
      values: [
        { label: 'Autotype', value: 'auto' },
        { label: 'String', value: 'string' },
        { label: 'Number', value: 'number' },
        { label: 'Boolean', value: 'boolean' },
        { label: 'Object', value: 'object' },
      ],
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Value Property',
    key: 'valueProperty',
    skipMerge: true,
    clearOnHide: false,
    weight: 13,
    description: "The selected item's property to save.",
    tooltip: 'The property of each item in the data source to use as the select value. If not specified, the item itself will be used.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'json',
            'url',
            'custom'
          ],
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    label: 'Select Fields',
    key: 'selectFields',
    tooltip: 'The properties on the resource to return as part of the options. Separate property names by commas. If left blank, all properties will be returned.',
    placeholder: 'Comma separated list of fields to select.',
    weight: 14,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { '===': [{ var: 'data.valueProperty' }, ''] },
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    key: 'disableLimit',
    label: 'Disable limiting response',
    tooltip: 'When enabled the request will not include the limit and skip options in the query string',
    weight: 15,
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'searchField',
    label: 'Search Query Name',
    weight: 16,
    description: 'Name of URL query parameter',
    tooltip: 'The name of the search querystring parameter used when sending a request to filter results with. The server at the URL must handle this query parameter.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'number',
    input: true,
    key: 'minSearch',
    weight: 17,
    label: 'Minimum Search Length',
    tooltip: 'The minimum amount of characters they must type before a search is made.',
    defaultValue: 0,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'url'] },
          { '!=': [{ var: 'data.searchField' }, ''] },
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'filter',
    label: 'Filter Query',
    weight: 18,
    description: 'The filter query for results.',
    tooltip: 'Use this to provide additional filtering using query parameters.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'textfield',
    input: true,
    key: 'sort',
    label: 'Sort Query',
    weight: 18,
    description: 'The sort query for results',
    tooltip: 'Use this to provide additional sorting using query parameters',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
          ],
        ],
      },
    },
  },
  {
    type: 'number',
    input: true,
    key: 'limit',
    label: 'Limit',
    weight: 18,
    defaultValue: 100,
    description: 'Maximum number of items to view per page of results.',
    tooltip: 'Use this to limit the number of items to request or view.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource'
          ],
        ],
      },
    },
  },
  {
    type: 'textarea',
    input: true,
    key: 'data.custom',
    label: 'Custom Values',
    editor: 'ace',
    rows: 10,
    weight: 14,
    placeholder: "values = data['mykey'];",
    tooltip: 'Write custom code to return the value options. The form data object is available.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'custom'] },
    },
  },
  {
    type: 'textarea',
    input: true,
    key: 'template',
    label: 'Item Template',
    editor: 'ace',
    as: 'html',
    rows: 3,
    weight: 18,
    tooltip: 'The HTML template for the result data items.',
    allowCalculateOverride: true,
    calculateValue:(context) => {
      if (!context.data.template) {
        if (context.instance && context.instance._currentForm.options.editComponent) {
          return context.instance._currentForm.options.editComponent.template;
        }
      }

      return context.data.template;
    }
  },
  {
    type: 'select',
    input: true,
    key: 'refreshOn',
    label: 'Refresh Options On',
    weight: 19,
    tooltip: 'Refresh data when another field changes.',
    dataSrc: 'custom',
    valueProperty: 'value',
    data: {
      custom(context) {
        var values = [];
        values.push({ label: 'Any Change', value: 'data' });
        context.utils.eachComponent(context.instance.options.editForm.components, function(component, path) {
          if (component.key !== context.data.key) {
            values.push({
              label: component.label || component.key,
              value: path
            });
          }
        });
        return values;
      }
    },
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
            'values'
          ],
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 20,
    key: 'clearOnRefresh',
    label: 'Clear Value On Refresh Options',
    defaultValue: false,
    tooltip: 'When the Refresh On field is changed, clear this components value.',
    conditional: {
      json: {
        in: [
          { var: 'data.dataSrc' },
          [
            'url',
            'resource',
            'values'
          ],
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 21,
    key: 'searchEnabled',
    label: 'Enable Static Search',
    defaultValue: true,
    tooltip: 'When checked, the select dropdown will allow for searching within the static list of items provided.',
  },
  {
    label: 'Search Threshold',
    mask: false,
    tableView: true,
    alwaysEnabled: false,
    type: 'number',
    input: true,
    key: 'selectThreshold',
    validate: {
      min: 0,
      customMessage: '',
      json: '',
      max: 1,
    },
    delimiter: false,
    requireDecimal: false,
    encrypted: false,
    defaultValue: 0.3,
    weight: 22,
    tooltip: 'At what point does the match algorithm give up. A threshold of 0.0 requires a perfect match, a threshold of 1.0 would match anything.',
  },
  {
    type: 'checkbox',
    input: true,
    weight: 23,
    key: 'addResource',
    label: 'Add Resource',
    tooltip: 'Allows to create a new resource while entering a submission.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'textfield',
    label: 'Add Resource Label',
    key: 'addResourceLabel',
    tooltip: 'Set the text of the Add Resource button.',
    placeholder: 'Add Resource',
    weight: 24,
    input: true,
    conditional: {
      json: {
        and: [
          { '===': [{ var: 'data.dataSrc' }, 'resource'] },
          { '!!': { var: 'data.addResource' } },
        ],
      },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 25,
    key: 'reference',
    label: 'Save as reference',
    tooltip: 'Using this option will save this field as a reference and link its value to the value of the origin record.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'resource'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 26,
    key: 'authenticate',
    label: 'Formio Authenticate',
    tooltip: 'Check this if you would like to use Formio Authentication with the request.',
    conditional: {
      json: { '===': [{ var: 'data.dataSrc' }, 'url'] },
    },
  },
  {
    type: 'checkbox',
    input: true,
    weight: 27,
    key: 'readOnlyValue',
    label: 'Read Only Value',
    tooltip: 'Check this if you would like to show just the value when in Read Only mode.',
  },
  {
    type: 'textarea',
    as: 'json',
    editor: 'ace',
    weight: 28,
    input: true,
    key: 'customOptions',
    label: 'Choices.js options',
    tooltip: 'A raw JSON object to use as options for the Select component (Choices JS).',
    defaultValue: {},
  },
];
<|endoftext|>