<commit_before>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    // opera.postError("" + evt.keyCode + " " + evt.which );
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 97: // a key. ctrl-a == move to start of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this._textarea.selectionStart = 0;
          this._textarea.selectionEnd = 0;
        }
        break;
      }
      case 107: // k key. ctrl-k == kill to en of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var pos = this._textarea.selectionStart;
          this._textarea.value = this._textarea.value.slice(0, pos);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<commit_msg>Implemented ctrl-w. Kill word backwards<commit_after>window.cls = window.cls || (window.cls = {});

/**
 * @constructor
 * @extends ViewBase
 */

cls.ReplView = function(id, name, container_class, html, default_handler) {
  this._resolver = new cls.PropertyFinder();
  this._data = new cls.ReplData(this);
  this._service = new cls.ReplService(this, this._data);
  this._linelist = null;
  this._textarea = null;
  this._lastupdate = null;
  this._current_input = "";
  this._current_scroll = 0;
  this._container = null;
  this._backlog_index = -1;

  this.ondestroy = function()
  {
    this._lastupdate = 0;
    this._backlog_index = -1;
    this._current_scroll = this._container.scrollTop;
    this._current_input = this._textarea.value;
  };

  this.createView = function(container)
  {
    var switched_to_view = false;
    if (!this._lastupdate)
    {
      container.innerHTML = "";
      container.render(templates.repl_main());
      this._linelist = container.querySelector("ol");
      this._textarea = container.querySelector("textarea");
      this._textarea.value = this._current_input;
      this._container = container;
      switched_to_view = true;
      // note: events are bound to handlers at the bottom of this class
    }

    var scroll_at_bottom = this._container.scrollTop + this._container.offsetHeight >= this._container.scrollHeight;
    this._update();

    if (this._current_scroll)
    {
      this._container.scrollTop = this._current_scroll;
      this._current_scroll = null;
    }
    else if (scroll_at_bottom)
    {
      this._container.scrollTop = 9999999; this._container.scrollHeight;
      if (switched_to_view) {
        window.setTimeout(function() {this._textarea.focus();}.bind(this), 100);
      }
    }

    return;
  };

  this.clear = function()
  {
    this.ondestroy();
  };

  /**
   * Pulls all the available, non-rendered, events from the data
   * object and renders them
   */
  this._update = function()
  {
    var now = new Date().getTime();
    var entries = this._data.get_log(this._lastupdate);
    this._lastupdate = now;

    for (var n=0, e; e=entries[n]; n++)
    {
      switch(e.type) {
        case "input":
          this._render_input(e.data);
          break;
        case "string":
          this._render_string(e.data);
          break;
        case "exception":
          this._render_error(e.data);
          break;
        case "iobj":
          this._render_inspectable_object(e.data);
          break;
        case "iele":
          this._render_inspectable_element(e.data);
          break;
        case "pobj":
          this._render_pointer_to_object(e.data);
          break;
        case "valuelist":
          this._render_value_list(e.data);
          break;
        case "trace":
          this._render_trace(e.data);
          break;
        case "groupstart":
          this._render_groupstart(e.data);
          break;
        case "groupend":
          this._render_groupend();
          break;
      default:
          this._render_string("unknown");
      }
    }
  };

  this._render_groupstart = function(data)
  {
    this._add_line([["button", "", "class", "folder-key"+(data.collapsed ? "" : " open" ),
                                   "handler", "repl-toggle-group"
                    ],
                    data.name]);
    var ol = document.createElement("ol");
    ol.className="repl-lines";
    this._add_line(ol);
    if (data.collapsed) {
      ol.parentNode.style.display = "none";
    }

    this._linelist = ol;
  };

  this._render_groupend = function()
  {
    if (this._linelist.parentNode.parentNode.nodeName.toLowerCase() == "ol")
    {
      this._linelist = this._linelist.parentNode.parentNode;
    }
  };

  this._render_pointer_to_object = function(data)
  {
    this._add_line(templates.repl_output_pobj(data));
  };

  this._render_inspectable_element = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableDomNodeView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_element.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_inspectable_object = function(data)
  {
    if (!data.view) {
      var rt_id = data.rt_id, obj_id=data.obj_id, name=data.name;
      data.view = new cls.InspectableObjectView(rt_id, obj_id, name, false);
    }

    if (data.view && !data.view.expanded)
    {
      // re-enter once we have the data.
      data.view.expand(this._render_inspectable_object.bind(this, data));
      return;
    }

    this._add_line(data.view.render());
  };

  this._render_error = function(data)
  {
    this._render_string(data.message, data.stacktrace);
  };

  this._render_trace = function(data)
  {
    this._add_line(templates.repl_output_trace(data));
  };

  this._render_value_list = function(values) {
    var tpl = values.map(templates.repl_output_native_or_pobj);
    var separated = [];
    separated.push(tpl.shift());
    while (tpl.length)
    {
      separated.push(["span", ", "]);
      separated.push(tpl.shift());
    }
    this._add_line(separated);
  };

  /**
   * Render an arbitrary numver of string arguments
   */
  this._render_string = function()
  {
    for (var n=0; n<arguments.length; n++)
    {
      this._add_line(templates.repl_output_native(arguments[n]));
    }
  };

  this._render_input = function(str)
  {
    this._render_string(">>> " + str);
  };

  this.set_current_input = function(str)
  {
    this._textarea.textContent = str;
  };

  this._add_line = function(elem_or_template)
  {
    var line = document.createElement("li");

    if (elem_or_template.nodeType === undefined)
    {
      line.render(elem_or_template);
    }
    else
    {
      line.appendChild(elem_or_template);
    }
    this._linelist.appendChild(line);
  };

  this._handle_keypress_bound = function(evt)
  {
    // opera.postError("" + evt.keyCode + " " + evt.which );
    switch (evt.keyCode) {
      case 9: // tab
      {
        evt.preventDefault();
        this._resolver.find_props(this._handle_completer.bind(this),
                                  this._textarea.value,
                                  window.stop_at.getSelectedFrame());
        break;
      }
      case 13: // enter
      {
        // stop it from adding a newline just before processing. Looks strange
        evt.preventDefault();
        var input = this._textarea.value;
        input = input.trim();
        this._textarea.value = "";
        this._backlog_index = -1;
        this._current_input = "";

        if (input == "") {
          this._render_input("");
          return;
        }

        this._service.handle_input(input);
        break;
      }
      case 38: // up and down. maybe. See DSK-246193
      case 40:
      {
        // workaround as long as we don't have support for keyIdentifier
        // event.which is 0 in a keypress event for function keys
        if( !evt.which )
        {
          evt.preventDefault();
          this._handle_backlog(evt.keyCode == 38 ? 1 : -1);
        }
        break;
      }
      case 97: // a key. ctrl-a == move to start of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this._textarea.selectionStart = 0;
          this._textarea.selectionEnd = 0;
        }
        break;
      }
      case 107: // k key. ctrl-k == kill to en of line
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var pos = this._textarea.selectionStart;
          this._textarea.value = this._textarea.value.slice(0, pos);
        }
        break;
      }
      case 108: // l key
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          this.clear();
          this._data.clear();
        }
        break;
      }
      case 119: // w key. ctrl-w == kill word backwards
      {
        if (evt.ctrlKey) {
          evt.preventDefault();
          var s = this._textarea.value.slice(0, this._textarea.selectionStart-1);
          var pos = s.lastIndexOf(" ");
          pos++;

          this._textarea.value = this._textarea.value.slice(0, pos) + this._textarea.value.slice(this._textarea.selectionStart);
          this._textarea.selectionStart = pos;
          this._textarea.selectionEnd = pos;
        }
        break;
      }
    }
  }.bind(this);

  this._handle_backlog = function(delta)
  {
    this._set_input_from_backlog(this._backlog_index + delta);
  };

  this._set_input_from_backlog = function(index)
  {
    if (index <= -1)
    {
      this._backlog_index = -1;
      this._textarea.value = this._current_input;
      return;
    }

    if (this._backlog_index == -1)
    {
      this._current_input = this._textarea.value;
    }

    var log = this._data.get_typed_history();
    this._backlog_index = Math.min(index, log.length-1);
    var entry = log[this._backlog_index];

    if (entry != undefined)
    {
      this._textarea.value = entry;
    }
  };

  this._handle_completer = function(props)
  {
    var localpart = props.identifier;

    var matches = props.props.filter(function(e) {
      return e.indexOf(localpart) == 0;
    });

    if (! matches.length) {
      return;
    }

    var match = this._longest_common_prefix(matches.slice(0));
    if (match.length > localpart.length)
    {
      var pos = this._textarea.value.lastIndexOf(localpart);
      this._textarea.value = this._textarea.value.slice(0, pos) + match;
    }
    else
    {
      this._render_input(this._textarea.value);
      this._render_string(matches.sort().join(", "));
    }

  };

  /**
   * Return the longest common prefix of all the strings in the array
   * of strings. For example ["foobar", "foobaz", "foomatic"] -> "foo"
   */
  this._longest_common_prefix = function(strings)
  {
    if (strings.length == 0)
    {
      return "";
    }
    else if (strings.length == 1)
    {
      return strings[0];
    }
    else
    {
      var sorted = strings.slice(0).sort();
      var first = sorted.shift();
      var last = sorted.pop();

      for (var n=last.length; n; n--)
      {
        if (first.indexOf(last.slice(0,n)) == 0) { return last.slice(0, n); }
      }
    }
    return "";
  };

  this._handle_repl_toggle_group = function(event, target)
  {
    var li = target.parentNode;
    if (target.hasClass("open"))
    {
      target.removeClass("open");
      li.nextSibling.style.display = "none";
    }
    else
    {
      target.addClass("open");
      li.nextSibling.style.display = "";
    }
  };

  this._handle_option_change_bound = function(event, target)
  {
    settings.repl.set('max-typed-history-length', target.value);
    messages.post("setting-changed", {id: "repl", key: "max-typed-history-length"});
  }.bind(this);

  var eh = window.eventHandlers;
  eh.click["repl-toggle-group"] = this._handle_repl_toggle_group;
  eh.keypress['repl-textarea'] = this._handle_keypress_bound;
  eh.change['set-typed-history-length'] = this._handle_option_change_bound;

  this.init(id, name, container_class, html, default_handler);
};
cls.ReplView.prototype = ViewBase;

cls.ReplView.create_ui_widgets = function()
{

  new Settings(
    'repl',
    { // key/value
      'max-typed-history-length': 8,
      'typed-history': []
    },
    { // key/label
      'max-typed-history-length': "Max items in typed history to remember"
    },
    { // settings map
      customSettings:
      [
        'max-typed-history-length'
      ]
    },
    {  // custom templates
      'max-typed-history-length':
      function(setting)
      {
        return (
        [
          'setting-composite',
          ['label',
           setting.label_map['max-typed-history-length'] + ': ',
           ['input',
            'type', 'number',
            'handler', 'set-typed-history-length',
            'max', '1000',
            'min', '0',
            'value', setting.get('max-typed-history-length')
           ]
          ]
        ]);
      }
    }
  );
};
<|endoftext|>


<commit_before>import { normalize } from 'normalizr';
import HttpError from 'standard-http-error';

const hasBody = ['POST', 'PUT', 'PATCH'];

export function requestBuilder(url, requestOpts = {}, { handleResponse = false, normalizeSchema = false, handleError = false } = {}) {
  return (payload, meta = {}) => {
    const callUrl = typeof url === 'function' ? url(payload, meta) : url;
    const callOpts = {
      ...requestOpts,
    };

    let headers = {};
    let body = hasBody.includes(requestOpts.method) ? payload : undefined;

    if (meta.authToken) {
      headers['Authorization'] = `Bearer ${meta.authToken}`;
    }
    if (typeof callOpts.headers === 'function') {
      headers = callOpts.headers(headers, payload, meta);
    }
    if (headers.Authorization) {
      headers['Access-Control-Allow-Credentials'] = true;
      callOpts.credentials = 'include';
    }

    if (typeof callOpts.body === 'function') {
      body = callOpts.body(body, meta);
    }
    if (typeof body !== 'undefined' && typeof body !== 'string') {
      body = JSON.stringify(body);
      headers['Accept'] = 'application/json';
      headers['Content-Type'] = 'application/json';
    }

    callOpts.headers = headers;
    callOpts.body = body;

    return fetch(callUrl, callOpts)
      .then((response) => {
        switch (response.status) {
          case 200:
          case 201:
            return response;  // Keep going!
          default:
            throw new HttpError(response.status, { response });
        }
      })
      .then((response) => handleResponse ? handleResponse(response, payload, meta) : response.json())
      .then((data) => normalizeSchema ? normalize(data, normalizeSchema) : data)
      .catch(handleError ? (err) => handleError(err, payload, meta) : (err) => {
        throw err;
      });
  };
}
<commit_msg>requestBuilder: Replace `normalize` with `handleSuccess`<commit_after>import HttpError from 'standard-http-error';

const methodsWithBodies = ['POST', 'PUT', 'PATCH'];

export function requestBuilder(url, requestOpts = {}, { handleResponse = false, handleSuccess = false, handleError = false } = {}) {
  return (payload, meta = {}) => {
    // Build the URL
    const callUrl = typeof url === 'function' ? url(payload, meta) : url;

    // Options to pass to the `fetch` request
    const callOpts = {
      ...requestOpts,
    };

    // Headers
    let headers = {};
    if (meta.authToken) {
      headers['Authorization'] = `Bearer ${meta.authToken}`;
    }
    if (typeof callOpts.headers === 'function') {
      headers = callOpts.headers(headers, payload, meta);
    }
    if (headers.Authorization) {
      headers['Access-Control-Allow-Credentials'] = true;
      callOpts.credentials = 'include';
    }

    // Body
    let body = methodsWithBodies.includes(requestOpts.method) ? payload : undefined;
    if (typeof callOpts.body === 'function') {
      body = callOpts.body(body, meta);
    }
    if (typeof body !== 'undefined' && typeof body !== 'string') {
      body = JSON.stringify(body);
      headers['Accept'] = 'application/json';
      headers['Content-Type'] = 'application/json';
    }

    callOpts.headers = headers;
    callOpts.body = body;

    return fetch(callUrl, callOpts)
      .then((response) => {
        switch (response.status) {
          case 200:
          case 201:
            return response;  // Keep going!
          default:
            throw new HttpError(response.status, { response });
        }
      })
      .then((response) => handleResponse ? handleResponse(response, payload, meta) : response.json())
      .then((data) => handleSuccess ? handleSuccess(data, payload, meta) : data)
      .catch(handleError ? (err) => handleError(err, payload, meta) : (err) => { throw err; });
  };
}
<|endoftext|>


<commit_before>function ResourceSolver(res) {

  function Solver($resource, resourceSolver, $state) {
    var baseUrl = resourceSolver.getBaseUrl();
    var action = res.action || 'get';

    var stateParams = $state.nextParams? $state.nextParams: $state.params;
    var params = angular.extend({}, stateParams, res.params || {});

    var resource = $resource(baseUrl+res.url, params, {
      'update': {method: 'PUT'}
    });

    if(res.data) {
      return resource[action](res.data).$promise;
    } else {
      return resource[action]().$promise;
    }
  };

  var injectable = ['$resource', 'resourceSolver', '$state', Solver];

  injectable.fetch = function() {
    var $injector = ResourceSolver.prototype.$injector;
    if(!$injector) {
      console.error("Should not call rs.fetch before the configuration phase is over");
    } else {
      return $injector.invoke(injectable);
    }
  };

  return injectable;
}

angular.module('resourceSolver', ['ui.router'])

.constant('rs', ResourceSolver)

.provider('resourceSolver', function ResourceSolverProvider() {
  var baseUrl = '';
  var _headers = {};

  this.setBaseUrl = function(url) {
    baseUrl = url;
  };

  this.setHeaders = function(headers) {
    _headers = headers;
  };

  this.$get = function() {

    return {
      getBaseUrl: function() {
        return baseUrl;
      },
      getHeaders: function() {
        return _headers;
      },
      setHeaders: function(headers) {
        _headers = headers;
      }
    };
  };
})
.factory('authorizationInterceptor', ['resourceSolver', function (resourceSolver) {
  return {
    request: function (config) {
      angular.extend(config.headers, resourceSolver.getHeaders());
      return config;
    }
  };
}])
.config(['$provide', '$httpProvider', function($provide, $httpProvider) {

  $httpProvider.interceptors.push('authorizationInterceptor');

  $provide.decorator("$state", function($delegate, $rootScope) {
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
      $delegate.next = toState;
      $delegate.nextParams = toParams;
    });
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
      $delegate.next = null;
      $delegate.nextParams = null;
    });
    $rootScope.$on('$stateChangeError', function(event, toState, toParams) {
      $delegate.next = null;
      $delegate.nextParams = null;
    });

    function searchState(state, scope) {
      for(var i in state.locals) {
        if(state.locals.hasOwnProperty(i) && i.indexOf('@') > -1 && state.locals[i].$scope === scope) {
          console.log("Matched scope", i, state.self.name);
          return state;
        }
      }
      if(state.parent) {
        return searchState(state.parent, scope);
      } else {
        return null;
      }
    }

    $rootScope.$on('$viewContentLoaded', function(event) {

      var state = searchState($delegate.$current, event.targetScope);
      if(state) {
        var data = {};
        var foundData = false;
        for(var i in state.locals.globals) {
          if(state.resolve && state.resolve.hasOwnProperty(i) && i.indexOf('$') != 0) {
            foundData = true;
            data[i] = state.locals.globals[i];
          }
        }
        if(foundData) {
          //event.targetScope.$apply(function() {
          console.log("Autoinject into scope", data);
          angular.extend(event.targetScope, data);
          //});
        }
      }

    });

    return $delegate;
  });
}])
.run(['$injector', function($injector) {

  //Decorate rs constant for runtime auto-injection.
  ResourceSolver.prototype.$injector = $injector;

}])
.directive('rsUrl', ['$parse', 'rs', function($parse, rs) {
  return {
    restrict: 'A',
    require: 'rsUrl',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: ['$scope', function($scope) {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.submit = function() {
        var params, data;

        if(_attrs.rsParams) {
          params = $parse(_attrs.rsParams)($scope);
        } else {
          params = {};
        }

        if(_attrs.ngModel) {
          data = $parse(_attrs.ngModel)($scope);
        }
        return rs({
          url: _attrs.rsUrl,
          params: params,
          action: _attrs.rsAction || 'post',
          data: data
        }).fetch();
      };
    }]
  };
}])
.directive('rsNotifySuccess', ['$rootScope', '$timeout', function($rootScope, $timeout) {
  return {
    scope: {
      'rsNotifySuccess': '='
    },
    link: function(scope, elem, attrs, controllers) {
      elem.hide();
      var formSuccess = $rootScope.$on('formSuccess', function(event, form) {
        if(form === scope.rsNotifySuccess) {
          elem.slideDown();
          $timeout(function() {
            elem.slideUp();
          }, 5000);
        }
      });

      scope.$on('$destroy', formSuccess);
    }
  };
}])
.directive('rsOnSuccess', ['$parse', function($parse) {
  return {
    require: 'rsOnSuccess',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: function() {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.success = function(data) {
        $parse(_attrs.rsOnSuccess)(scope, {
          value: data
        });
      };
    }
  };
}])
.directive('rsOnError', ['$parse', function($parse) {
  return {
    require: 'rsOnError',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: function() {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.error = function(error) {
        $parse(_attrs.rsOnSuccess)(scope, {
          error: error
        });
      };
    }
  };
}])
.directive('rsThen', ['$compile', function($compile) {
  return {
    restrict: 'A',
    require: 'rsThen',
    link: function(scope, elem, attrs, controller) {
      var proxyElem = $compile('')(scope);
      proxyElem.insertAfter(elem);

      controller.setProxy(proxyElem);
    },
    controller: function() {
      var proxyElem;

      this.setProxy = function(elem) {
        proxyElem = elem;
      };

      this.success = function() {
        proxyElem.trigger('click');
      };
    }
  };
}])
.directive('rsSubmit', function() {
  return {
    require: ['^form', '^rsUrl', '?rsThen', '?rsOnSuccess', '?rsOnError'],
    link: function(scope, elem, attrs, controllers) {
      var form = controllers[0];
      var resource = controllers[1];
      var rsThen = controllers[2];
      var rsOnSuccess = controllers[3];
      var rsOnError = controllers[4];

      elem.on('click', function() {
        scope.$apply(function() {
          form.$setSubmitted(true);
        });
        if(form.$valid) {
          resource.submit().then(function(data) {
            scope.$emit('formSuccess', form);
            if(rsThen) {
              rsThen.success();
            }
            if(rsOnSuccess) {
              rsOnSuccess.success(data);
            }
          }, function(error) {
            //TODO: add errors to form?
            if(rsOnError) {
              rsOnError.error(error);
            }
            console.error("Could not submit form", error);
          });
        }
      });
    }
  };
});
<commit_msg>Add actions for arrays<commit_after>function ResourceSolver(res) {

  function Solver($resource, resourceSolver, $state) {
    var baseUrl = resourceSolver.getBaseUrl();
    var action = res.action || 'get';

    var stateParams = $state.nextParams? $state.nextParams: $state.params;
    var params = angular.extend({}, stateParams, res.params || {});

    var resource = $resource(baseUrl+res.url, params, {
      'update': {method: 'PUT'},
      'bulkUpdate': {method: 'PUT', isArray: true},
      'bulkSave': {method: 'POST', isArray: true}
    });

    if(res.data) {
      return resource[action](res.data).$promise;
    } else {
      return resource[action]().$promise;
    }
  };

  var injectable = ['$resource', 'resourceSolver', '$state', Solver];

  injectable.fetch = function() {
    var $injector = ResourceSolver.prototype.$injector;
    if(!$injector) {
      console.error("Should not call rs.fetch before the configuration phase is over");
    } else {
      return $injector.invoke(injectable);
    }
  };

  return injectable;
}

angular.module('resourceSolver', ['ui.router'])

.constant('rs', ResourceSolver)

.provider('resourceSolver', function ResourceSolverProvider() {
  var baseUrl = '';
  var _headers = {};

  this.setBaseUrl = function(url) {
    baseUrl = url;
  };

  this.setHeaders = function(headers) {
    _headers = headers;
  };

  this.$get = function() {

    return {
      getBaseUrl: function() {
        return baseUrl;
      },
      getHeaders: function() {
        return _headers;
      },
      setHeaders: function(headers) {
        _headers = headers;
      }
    };
  };
})
.factory('authorizationInterceptor', ['resourceSolver', function (resourceSolver) {
  return {
    request: function (config) {
      angular.extend(config.headers, resourceSolver.getHeaders());
      return config;
    }
  };
}])
.config(['$provide', '$httpProvider', function($provide, $httpProvider) {

  $httpProvider.interceptors.push('authorizationInterceptor');

  $provide.decorator("$state", function($delegate, $rootScope) {
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
      $delegate.next = toState;
      $delegate.nextParams = toParams;
    });
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
      $delegate.next = null;
      $delegate.nextParams = null;
    });
    $rootScope.$on('$stateChangeError', function(event, toState, toParams) {
      $delegate.next = null;
      $delegate.nextParams = null;
    });

    function searchState(state, scope) {
      for(var i in state.locals) {
        if(state.locals.hasOwnProperty(i) && i.indexOf('@') > -1 && state.locals[i].$scope === scope) {
          console.log("Matched scope", i, state.self.name);
          return state;
        }
      }
      if(state.parent) {
        return searchState(state.parent, scope);
      } else {
        return null;
      }
    }

    $rootScope.$on('$viewContentLoaded', function(event) {

      var state = searchState($delegate.$current, event.targetScope);
      if(state) {
        var data = {};
        var foundData = false;
        for(var i in state.locals.globals) {
          if(state.resolve && state.resolve.hasOwnProperty(i) && i.indexOf('$') != 0) {
            foundData = true;
            data[i] = state.locals.globals[i];
          }
        }
        if(foundData) {
          //event.targetScope.$apply(function() {
          console.log("Autoinject into scope", data);
          angular.extend(event.targetScope, data);
          //});
        }
      }

    });

    return $delegate;
  });
}])
.run(['$injector', function($injector) {

  //Decorate rs constant for runtime auto-injection.
  ResourceSolver.prototype.$injector = $injector;

}])
.directive('rsUrl', ['$parse', 'rs', function($parse, rs) {
  return {
    restrict: 'A',
    require: 'rsUrl',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: ['$scope', function($scope) {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.submit = function() {
        var params, data;

        if(_attrs.rsParams) {
          params = $parse(_attrs.rsParams)($scope);
        } else {
          params = {};
        }

        if(_attrs.ngModel) {
          data = $parse(_attrs.ngModel)($scope);
        }
        return rs({
          url: _attrs.rsUrl,
          params: params,
          action: _attrs.rsAction || 'post',
          data: data
        }).fetch();
      };
    }]
  };
}])
.directive('rsNotifySuccess', ['$rootScope', '$timeout', function($rootScope, $timeout) {
  return {
    scope: {
      'rsNotifySuccess': '='
    },
    link: function(scope, elem, attrs, controllers) {
      elem.hide();
      var formSuccess = $rootScope.$on('formSuccess', function(event, form) {
        if(form === scope.rsNotifySuccess) {
          elem.slideDown();
          $timeout(function() {
            elem.slideUp();
          }, 5000);
        }
      });

      scope.$on('$destroy', formSuccess);
    }
  };
}])
.directive('rsOnSuccess', ['$parse', function($parse) {
  return {
    require: 'rsOnSuccess',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: function() {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.success = function(data) {
        $parse(_attrs.rsOnSuccess)(scope, {
          value: data
        });
      };
    }
  };
}])
.directive('rsOnError', ['$parse', function($parse) {
  return {
    require: 'rsOnError',
    link: function(scope, elem, attrs, controller) {
      controller.setAttributes(attrs);
    },
    controller: function() {
      var _attrs;

      this.setAttributes = function(attrs) {
        _attrs = attrs;
      };

      this.error = function(error) {
        $parse(_attrs.rsOnSuccess)(scope, {
          error: error
        });
      };
    }
  };
}])
.directive('rsThen', ['$compile', function($compile) {
  return {
    restrict: 'A',
    require: 'rsThen',
    link: function(scope, elem, attrs, controller) {
      var proxyElem = $compile('')(scope);
      proxyElem.insertAfter(elem);

      controller.setProxy(proxyElem);
    },
    controller: function() {
      var proxyElem;

      this.setProxy = function(elem) {
        proxyElem = elem;
      };

      this.success = function() {
        proxyElem.trigger('click');
      };
    }
  };
}])
.directive('rsSubmit', function() {
  return {
    require: ['^form', '^rsUrl', '?rsThen', '?rsOnSuccess', '?rsOnError'],
    link: function(scope, elem, attrs, controllers) {
      var form = controllers[0];
      var resource = controllers[1];
      var rsThen = controllers[2];
      var rsOnSuccess = controllers[3];
      var rsOnError = controllers[4];

      elem.on('click', function() {
        scope.$apply(function() {
          form.$setSubmitted(true);
        });
        if(form.$valid) {
          resource.submit().then(function(data) {
            scope.$emit('formSuccess', form);
            if(rsThen) {
              rsThen.success();
            }
            if(rsOnSuccess) {
              rsOnSuccess.success(data);
            }
          }, function(error) {
            //TODO: add errors to form?
            if(rsOnError) {
              rsOnError.error(error);
            }
            console.error("Could not submit form", error);
          });
        }
      });
    }
  };
});
<|endoftext|>


<commit_before>//  Dependancies
const express = require('express');
const request = require('request');
const path = require('path');
const Conversation = require('watson-developer-cloud/conversation/v1'); //  watson sdk
const FacebookMessageAPI = require(path.join(__dirname, '..', 'lib', 'FacebookMessage', 'FacebookMessageAPI'));

const AnswerFactory = require('../lib/AnswerFactory/AnswerFactory');
const factory = new AnswerFactory();

//  Router
const router = express.Router();

//  Send request to Watson
const pingWatson = require(path.join(__dirname, '../', 'watson', 'request'));

//  Watson convo config
const { USER_NAME, USER_PASS, WORKSPACE_ID, ACCESS_TOKEN } = process.env;

let facebookApi = new FacebookMessageAPI(ACCESS_TOKEN);

//  Facebook AUTH
router.get('/', (req, res) => {
    if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'alexander') {
        console.log("Validating webhook");
        return res
            .status(200)
            .send(req.query['hub.challenge']);
    }
    else {
        console.error("Failed validation. Make sure the validation tokens match.");
        return res
            .sendStatus(403);
    }
});

//  Webhook
router.post('/', function (req, res) {

    //  Extract the request body
    var data = req.body;

    // Make sure this is a page subscription
    if (data.object === 'page') {

        // Iterate over each entry - there may be multiple if batched
        data.entry.forEach(function (entry) {
            var pageID = entry.id;
            var timeOfEvent = entry.time;

            // Iterate over each messaging event
            entry.messaging.forEach(function (event) {

                let senderId = event.sender.id;

                if (event.message) {
                    //  If without postback (not buttons clicked) - contact watson
                    if(typeof event.postback === 'undefined' || (event.postback && !event.postback.payload)) return contactWatson(event);

                    //  postback present - contact Telenor api
                    let payload;
                    try {
                        payload = JSON.parse(event.postback.payload);
                    }
                    catch (err) {
                        console.log(`
                            Error:
                            ${err}
                        `);
                        return;
                    }

                    //  Send the response back to facebook
                    if(payload) {
                        console.log('Ulazi u id');
                        factory.factory(senderId, payload.type, payload.entities, (messageData) => {
                            facebookApi.send(messageData);
                        });
                    }

                } else {
                    console.log("Webhook received unknown event: ", event);
                }
            });
        });

        //  Return the status code of 200
        return res.sendStatus(200);
    }
});

function contactWatson(event) {
    var senderId = event.sender.id;
    var message = event.message;

    console.log(`
        Recieved message from the page!
    `);

    var messageText = message.text;
    var messageAttachments = message.attachments;

    pingWatson(messageText)
        .then(({intent, entities}) => {

            factory.factory(senderId, intent, entities, (messageData) => {

                facebookApi.send(messageData);

            });

        })
        .catch((err) => {

        });
}

//  Export the module
module.exports = router;<commit_msg>message<commit_after>//  Dependancies
const express = require('express');
const request = require('request');
const path = require('path');
const Conversation = require('watson-developer-cloud/conversation/v1'); //  watson sdk
const FacebookMessageAPI = require(path.join(__dirname, '..', 'lib', 'FacebookMessage', 'FacebookMessageAPI'));

const AnswerFactory = require('../lib/AnswerFactory/AnswerFactory');
const factory = new AnswerFactory();

//  Router
const router = express.Router();

//  Send request to Watson
const pingWatson = require(path.join(__dirname, '../', 'watson', 'request'));

//  Watson convo config
const { USER_NAME, USER_PASS, WORKSPACE_ID, ACCESS_TOKEN } = process.env;

let facebookApi = new FacebookMessageAPI(ACCESS_TOKEN);

//  Facebook AUTH
router.get('/', (req, res) => {
    if (req.query['hub.mode'] === 'subscribe' && req.query['hub.verify_token'] === 'alexander') {
        console.log("Validating webhook");
        return res
            .status(200)
            .send(req.query['hub.challenge']);
    }
    else {
        console.error("Failed validation. Make sure the validation tokens match.");
        return res
            .sendStatus(403);
    }
});

//  Webhook
router.post('/', function (req, res) {

    //  Extract the request body
    var data = req.body;

    // Make sure this is a page subscription
    if (data.object === 'page') {

        // Iterate over each entry - there may be multiple if batched
        data.entry.forEach(function (entry) {
            var pageID = entry.id;
            var timeOfEvent = entry.time;

            // Iterate over each messaging event
            entry.messaging.forEach(function (event) {

                let senderId = event.sender.id;

                if (event.message) {
                    //  If without postback (not buttons clicked) - contact watson
                    if(typeof event.postback === 'undefined' || (event.postback && !event.postback.payload)) return contactWatson(event);

                    //  postback present - contact Telenor api
                    let payload;
                    try {
                        payload = JSON.parse(event.postback.payload);
                    }
                    catch (err) {
                        console.log(`
                            Error:
                            ${err}
                        `);
                        return;
                    }

                    //  Send the response back to facebook
                    factory.factory(senderId, payload.type, payload.entities, (messageData) => {
                        facebookApi.send(messageData);
                    });

                } else {
                    console.log("Webhook received unknown event: ", event);
                }
            });
        });

        //  Return the status code of 200
        return res.sendStatus(200);
    }
});

function contactWatson(event) {
    var senderId = event.sender.id;
    var message = event.message;

    console.log(`
        Recieved message from the page!
    `);

    var messageText = message.text;
    var messageAttachments = message.attachments;

    pingWatson(messageText)
        .then(({intent, entities}) => {

            factory.factory(senderId, intent, entities, (messageData) => {

                facebookApi.send(messageData);

            });

        })
        .catch((err) => {

        });
}

//  Export the module
module.exports = router;<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import { Link, Router } from 'routes';

// Utils
import { LAYERS_PLANET_PULSE } from 'utils/layers/pulse_layers';

// Components
import Legend from 'layout/pulse/legend';
import WidgetChart from 'components/charts/widget-chart';
import LoginRequired from 'components/ui/login-required';

// Modal
import Modal from 'components/modal/modal-component';
import SubscribeToDatasetModal from 'components/modal/SubscribeToDatasetModal';

class LayerCardComponent extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      showSubscribeToDatasetModal: false
    };
  }
  componentWillReceiveProps(nextProps) {
    if ((nextProps.layerMenuPulse.layerActive && nextProps.layerMenuPulse.layerActive.id) !==
      (this.props.layerMenuPulse.layerActive && this.props.layerMenuPulse.layerActive.id)) {
      this.loadWidgets(nextProps);
      this.props.loadDatasetData({ id: nextProps.layerMenuPulse.layerActive.attributes.dataset });
    }
  }

  loadWidgets(nextProps) {
    const { layerMenuPulse } = nextProps;
    const layerActive = layerMenuPulse.layerActive && layerMenuPulse.layerActive.id;

    if (layerActive) {
      let found = false;
      for (let i = 0; i < LAYERS_PLANET_PULSE.length && !found; i++) {
        found = LAYERS_PLANET_PULSE[i].layers.find(obj => obj.id === layerActive);
      }
      if (found) {
        const { widgets } = found;
        if (widgets && widgets.length > 0) {
          this.props.loadWidgetData(widgets[0]);
        } else {
          this.props.setWidget(null);
        }
      }
    }
  }

  handleToggleSubscribeToDatasetModal = (bool) => {
    this.setState({ showSubscribeToDatasetModal: bool });
  }

  render() {
    const { showSubscribeToDatasetModal } = this.state;
    const { layerMenuPulse, layerCardPulse } = this.props;
    const { layerActive, layerPoints } = layerMenuPulse;
    const { dataset, widget } = layerCardPulse;
    const subscribable = dataset && dataset.attributes && dataset.attributes.subscribable &&
      Object.keys(dataset.attributes.subscribable).length > 0;

    const source = layerActive && layerActive.metadata && layerActive.metadata[0].source;

    const className = classNames({
      'c-layer-card': true,
      '-hidden': layerActive === null
    });

    const datasetId = (layerActive !== null) ? layerActive.attributes.dataset : null;
    const contextLayers = layerActive && layerActive.contextLayers;

    return (

{layerActive && layerActive.label}

        {source &&

            source

        }
        {layerActive && layerActive.descriptionPulse}
        {layerPoints && layerPoints.length > 0 &&

            Number of objects: {layerPoints.length}

        }
        <Legend
          layerActive={layerActive}
          className={{ color: '-dark' }}
        />
        {contextLayers &&

            {
              contextLayers.map(ctLayer => ctLayer.active && (
                <Legend
                  layerActive={ctLayer}
                  className={{ color: '-dark' }}
                />
              ))
            }

        }
        {widget &&

Similar content

 Router.pushRoute('explore_detail', { id: widget.attributes.dataset })}
              role="button"
              tabIndex={-1}
            >

                {widget.attributes.name}

              <WidgetChart
                widget={widget.attributes}
                mode="thumbnail"
              />

        }

          { datasetId &&
            <Link
              route="explore_detail"
              params={{ id: datasetId }}
            >
              Explore the data
            </Link>
          }
          { subscribable &&
            <LoginRequired text="Log in or sign up to subscribe to alerts from this dataset">
              <button
                className="c-button -tertiary link_button"
                onClick={() => this.handleToggleSubscribeToDatasetModal(true)}
              >
                Subscribe to alerts
                <Modal
                  isOpen={showSubscribeToDatasetModal}
                  onRequestClose={() => this.handleToggleSubscribeToDatasetModal(false)}
                >
                  <SubscribeToDatasetModal
                    dataset={dataset}
                    showDatasetSelector={false}
                    onRequestClose={() => this.handleToggleSubscribeToDatasetModal(false)}
                  />
                </Modal>
              </button>
            </LoginRequired>
          }

    );
  }
}

LayerCardComponent.propTypes = {
  // PROPS
  layerMenuPulse: PropTypes.object.isRequired,
  layerCardPulse: PropTypes.object.isRequired,
  user: PropTypes.object.isRequired,

  // Actions
  loadDatasetData: PropTypes.func.isRequired,
  loadWidgetData: PropTypes.func.isRequired,
  setWidget: PropTypes.func.isRequired
};

export default LayerCardComponent;
<commit_msg>source fix<commit_after>import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import { Link, Router } from 'routes';

// Utils
import { LAYERS_PLANET_PULSE } from 'utils/layers/pulse_layers';

// Components
import Legend from 'layout/pulse/legend';
import WidgetChart from 'components/charts/widget-chart';
import LoginRequired from 'components/ui/login-required';

// Modal
import Modal from 'components/modal/modal-component';
import SubscribeToDatasetModal from 'components/modal/SubscribeToDatasetModal';

class LayerCardComponent extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      showSubscribeToDatasetModal: false
    };
  }
  componentWillReceiveProps(nextProps) {
    if ((nextProps.layerMenuPulse.layerActive && nextProps.layerMenuPulse.layerActive.id) !==
      (this.props.layerMenuPulse.layerActive && this.props.layerMenuPulse.layerActive.id)) {
      this.loadWidgets(nextProps);
      this.props.loadDatasetData({ id: nextProps.layerMenuPulse.layerActive.attributes.dataset });
    }
  }

  loadWidgets(nextProps) {
    const { layerMenuPulse } = nextProps;
    const layerActive = layerMenuPulse.layerActive && layerMenuPulse.layerActive.id;

    if (layerActive) {
      let found = false;
      for (let i = 0; i < LAYERS_PLANET_PULSE.length && !found; i++) {
        found = LAYERS_PLANET_PULSE[i].layers.find(obj => obj.id === layerActive);
      }
      if (found) {
        const { widgets } = found;
        if (widgets && widgets.length > 0) {
          this.props.loadWidgetData(widgets[0]);
        } else {
          this.props.setWidget(null);
        }
      }
    }
  }

  handleToggleSubscribeToDatasetModal = (bool) => {
    this.setState({ showSubscribeToDatasetModal: bool });
  }

  render() {
    const { showSubscribeToDatasetModal } = this.state;
    const { layerMenuPulse, layerCardPulse } = this.props;
    const { layerActive, layerPoints } = layerMenuPulse;
    const { dataset, widget } = layerCardPulse;
    const subscribable = dataset && dataset.attributes && dataset.attributes.subscribable &&
      Object.keys(dataset.attributes.subscribable).length > 0;

    const source = layerActive && layerActive.metadata && layerActive.metadata[0].attributes.source;

    const className = classNames({
      'c-layer-card': true,
      '-hidden': layerActive === null
    });

    const datasetId = (layerActive !== null) ? layerActive.attributes.dataset : null;
    const contextLayers = layerActive && layerActive.contextLayers;

    return (

{layerActive && layerActive.label}

        {source &&

            source

        }
        {layerActive && layerActive.descriptionPulse}
        {layerPoints && layerPoints.length > 0 &&

            Number of objects: {layerPoints.length}

        }
        <Legend
          layerActive={layerActive}
          className={{ color: '-dark' }}
        />
        {contextLayers &&

            {
              contextLayers.map(ctLayer => ctLayer.active && (
                <Legend
                  layerActive={ctLayer}
                  className={{ color: '-dark' }}
                />
              ))
            }

        }
        {widget &&

Similar content

 Router.pushRoute('explore_detail', { id: widget.attributes.dataset })}
              role="button"
              tabIndex={-1}
            >

                {widget.attributes.name}

              <WidgetChart
                widget={widget.attributes}
                mode="thumbnail"
              />

        }

          { datasetId &&
            <Link
              route="explore_detail"
              params={{ id: datasetId }}
            >
              Explore the data
            </Link>
          }
          { subscribable &&
            <LoginRequired text="Log in or sign up to subscribe to alerts from this dataset">
              <button
                className="c-button -tertiary link_button"
                onClick={() => this.handleToggleSubscribeToDatasetModal(true)}
              >
                Subscribe to alerts
                <Modal
                  isOpen={showSubscribeToDatasetModal}
                  onRequestClose={() => this.handleToggleSubscribeToDatasetModal(false)}
                >
                  <SubscribeToDatasetModal
                    dataset={dataset}
                    showDatasetSelector={false}
                    onRequestClose={() => this.handleToggleSubscribeToDatasetModal(false)}
                  />
                </Modal>
              </button>
            </LoginRequired>
          }

    );
  }
}

LayerCardComponent.propTypes = {
  // PROPS
  layerMenuPulse: PropTypes.object.isRequired,
  layerCardPulse: PropTypes.object.isRequired,
  user: PropTypes.object.isRequired,

  // Actions
  loadDatasetData: PropTypes.func.isRequired,
  loadWidgetData: PropTypes.func.isRequired,
  setWidget: PropTypes.func.isRequired
};

export default LayerCardComponent;
<|endoftext|>


<commit_before>var Leaderboard = require('./react.leaderboard.js');

module.exports = function (config) {
  window.fetch(config.countriesInfoUrl).then(function (response) {
    return response.json();
  }).then(function (data) {

    config.countries = {};
    data.map(function(country) {
      config.countries[country.iso2] = country;
    });

    React.render(
      <Leaderboard config={config} />,
      document.getElementById('leaderboard-container')
    );
  });
};
<commit_msg>Don't make multiple calls to country info api #137<commit_after>var Leaderboard = require('./react.leaderboard.js');

module.exports = function (config) {
  React.render(
    <Leaderboard config={config} />,
    document.getElementById('leaderboard-container')
  );
};
<|endoftext|>


<commit_before>/******************************************************************************
Copyright (c) 2014, Google Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the <ORGANIZATION> nor the names of its contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/

/******************************************************************************
*******************************************************************************
  Calculates the Speed Index for a page by:
  - Collecting a list of visible rectangles for elements that loaded
    external resources (images, background images, fonts)
  - Gets the time when the external resource for those elements loaded
    through Resource Timing
  - Calculates the likely time that the background painted
  - Runs the various paint rectangles through the SpeedIndex calculation:
    https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/metrics/speed-index

  TODO:
  - Improve the start render estimate
  - Handle overlapping rects (though maybe counting the area as multiple paints
    will work out well)
  - Detect elements with Custom fonts and the time that the respective font
    loaded
  - Better error handling for browsers that don't support resource timing
*******************************************************************************
******************************************************************************/

var RUMSpeedIndex = function(win) {
  win = win || window;
  var doc = win.document;

  /****************************************************************************
    Support Routines
  ****************************************************************************/
  // Get the rect for the visible portion of the provided DOM element
  var GetElementViewportRect = function(el) {
    var intersect = false;
    if (el.getBoundingClientRect) {
      var elRect = el.getBoundingClientRect();
      intersect = {'top': Math.max(elRect.top, 0),
                       'left': Math.max(elRect.left, 0),
                       'bottom': Math.min(elRect.bottom, (win.innerHeight || doc.documentElement.clientHeight)),
                       'right': Math.min(elRect.right, (win.innerWidth || doc.documentElement.clientWidth))};
      if (intersect.bottom <= intersect.top ||
          intersect.right <= intersect.left) {
        intersect = false;
      } else {
        intersect.area = (intersect.bottom - intersect.top) * (intersect.right - intersect.left);
      }
    }
    return intersect;
  };

  // Check a given element to see if it is visible
  var CheckElement = function(el, url) {
    if (url) {
      var rect = GetElementViewportRect(el);
      if (rect) {
        rects.push({'url': url,
                     'area': rect.area,
                     'rect': rect});
      }
    }
  };

  // Get the visible rectangles for elements that we care about
  var GetRects = function() {
    // Walk all of the elements in the DOM (try to only do this once)
    var elements = doc.getElementsByTagName('*');
    var re = /url\(.*(http.*)\)/ig;
    for (var i = 0; i < elements.length; i++) {
      var el = elements[i];
      var style = win.getComputedStyle(el);

      // check for Images
      if (el.tagName == 'IMG') {
        CheckElement(el, el.currentSrc || el.src);
      }
      // Check for background images
      if (style['background-image']) {
        re.lastIndex = 0;
        var matches = re.exec(style['background-image']);
        if (matches && matches.length > 1)
          CheckElement(el, matches[1].replace('"', ''));
      }
      // recursively walk any iFrames
      if (el.tagName == 'IFRAME') {
        try {
          var rect = GetElementViewportRect(el);
          if (rect) {
            var tm = RUMSpeedIndex(el.contentWindow);
            if (tm) {
              rects.push({'tm': tm,
                          'area': rect.area,
                          'rect': rect});
            }
        }
        } catch(e) {
        }
      }
    }
  };

  // Get the time at which each external resource loaded
  var GetRectTimings = function() {
    var timings = {};
    var requests = win.performance.getEntriesByType("resource");
    for (var i = 0; i < requests.length; i++)
      timings[requests[i].name] = requests[i].responseEnd;
    for (var j = 0; j < rects.length; j++) {
      if (!('tm' in rects[j]))
        rects[j].tm = timings[rects[j].url] !== undefined ? timings[rects[j].url] : 0;
    }
  };

  // Get the first paint time.
  var GetFirstPaint = function() {
    // Try the standardized paint timing api
    try {
      var entries = performance.getEntriesByType('paint');
      for (var i = 0; i < entries.length; i++) {
        if (entries[i]['name'] == 'first-paint') {
          navStart = performance.getEntriesByType("navigation")[0].startTime;
          firstPaint = entries[i].startTime - navStart;
          break;
        }
      }
    } catch(e) {
    }
    // If the browser supports a first paint event, just use what the browser reports
    if (firstPaint === undefined && 'msFirstPaint' in win.performance.timing)
      firstPaint = win.performance.timing.msFirstPaint - navStart;
    if (firstPaint === undefined && 'chrome' in win && 'loadTimes' in win.chrome) {
      var chromeTimes = win.chrome.loadTimes();
      if ('firstPaintTime' in chromeTimes && chromeTimes.firstPaintTime > 0) {
        var startTime = chromeTimes.startLoadTime;
        if ('requestTime' in chromeTimes)
          startTime = chromeTimes.requestTime;
        if (chromeTimes.firstPaintTime >= startTime)
          firstPaint = (chromeTimes.firstPaintTime - startTime) * 1000.0;
      }
    }
    // For browsers that don't support first-paint or where we get insane values,
    // use the time of the last non-async script or css from the head.
    if (firstPaint === undefined || firstPaint < 0 || firstPaint > 120000) {
      firstPaint = win.performance.timing.responseStart - navStart;
      var headURLs = {};
      var headElements = doc.getElementsByTagName('head')[0].children;
      for (var i = 0; i < headElements.length; i++) {
        var el = headElements[i];
        if (el.tagName == 'SCRIPT' && el.src && !el.async)
          headURLs[el.src] = true;
        if (el.tagName == 'LINK' && el.rel == 'stylesheet' && el.href)
          headURLs[el.href] = true;
      }
      var requests = win.performance.getEntriesByType("resource");
      var doneCritical = false;
      for (var j = 0; j < requests.length; j++) {
        if (!doneCritical &&
            headURLs[requests[j].name] &&
           (requests[j].initiatorType == 'script' || requests[j].initiatorType == 'link')) {
          var requestEnd = requests[j].responseEnd;
          if (firstPaint === undefined || requestEnd > firstPaint)
            firstPaint = requestEnd;
        } else {
          doneCritical = true;
        }
      }
    }
    firstPaint = Math.max(firstPaint, 0);
  };

  // Sort and group all of the paint rects by time and use them to
  // calculate the visual progress
  var CalculateVisualProgress = function() {
    var paints = {'0':0};
    var total = 0;
    for (var i = 0; i < rects.length; i++) {
      var tm = firstPaint;
      if ('tm' in rects[i] && rects[i].tm > firstPaint)
        tm = rects[i].tm;
      if (paints[tm] === undefined)
        paints[tm] = 0;
      paints[tm] += rects[i].area;
      total += rects[i].area;
    }
    // Add a paint area for the page background (count 10% of the pixels not
    // covered by existing paint rects.
    var pixels = Math.max(doc.documentElement.clientWidth, win.innerWidth || 0) *
                 Math.max(doc.documentElement.clientHeight, win.innerHeight || 0);
    if (pixels > 0 ) {
      pixels = Math.max(pixels - total, 0) * pageBackgroundWeight;
      if (paints[firstPaint] === undefined)
        paints[firstPaint] = 0;
      paints[firstPaint] += pixels;
      total += pixels;
    }
    // Calculate the visual progress
    if (total) {
      for (var time in paints) {
        if (paints.hasOwnProperty(time)) {
          progress.push({'tm': time, 'area': paints[time]});
        }
      }
      progress.sort(function(a,b){return a.tm - b.tm;});
      var accumulated = 0;
      for (var j = 0; j < progress.length; j++) {
        accumulated += progress[j].area;
        progress[j].progress = accumulated / total;
      }
    }
  };

  // Given the visual progress information, Calculate the speed index.
  var CalculateSpeedIndex = function() {
    if (progress.length) {
      SpeedIndex = 0;
      var lastTime = 0;
      var lastProgress = 0;
      for (var i = 0; i < progress.length; i++) {
        var elapsed = progress[i].tm - lastTime;
        if (elapsed > 0 && lastProgress < 1)
          SpeedIndex += (1 - lastProgress) * elapsed;
        lastTime = progress[i].tm;
        lastProgress = progress[i].progress;
      }
    } else {
      SpeedIndex = firstPaint;
    }
  };

  /****************************************************************************
    Main flow
  ****************************************************************************/
  var rects = [];
  var progress = [];
  var firstPaint;
  var SpeedIndex;
  var pageBackgroundWeight = 0.1;
  try {
    var navStart = win.performance.timing.navigationStart;
    GetRects();
    GetRectTimings();
    GetFirstPaint();
    CalculateVisualProgress();
    CalculateSpeedIndex();
  } catch(e) {
  }
  /* Debug output for testing
  var dbg = '';
  dbg += "Paint Rects\n";
  for (var i = 0; i < rects.length; i++)
    dbg += '(' + rects[i].area + ') ' + rects[i].tm + ' - ' + rects[i].url + "\n";
  dbg += "Visual Progress\n";
  for (var i = 0; i < progress.length; i++)
    dbg += '(' + progress[i].area + ') ' + progress[i].tm + ' - ' + progress[i].progress + "\n";
  dbg += 'First Paint: ' + firstPaint + "\n";
  dbg += 'Speed Index: ' + SpeedIndex + "\n";
  console.log(dbg);
  */
  return SpeedIndex;
};

if (typeof module !== 'undefined' && module.exports) {
  module.exports = RUMSpeedIndex;
}
<commit_msg>Use new chrome api - Chrome 64 to deprecate the chrome.loadTimes() API<commit_after>/******************************************************************************
Copyright (c) 2014, Google Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the <ORGANIZATION> nor the names of its contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/

/******************************************************************************
*******************************************************************************
  Calculates the Speed Index for a page by:
  - Collecting a list of visible rectangles for elements that loaded
    external resources (images, background images, fonts)
  - Gets the time when the external resource for those elements loaded
    through Resource Timing
  - Calculates the likely time that the background painted
  - Runs the various paint rectangles through the SpeedIndex calculation:
    https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/metrics/speed-index

  TODO:
  - Improve the start render estimate
  - Handle overlapping rects (though maybe counting the area as multiple paints
    will work out well)
  - Detect elements with Custom fonts and the time that the respective font
    loaded
  - Better error handling for browsers that don't support resource timing
*******************************************************************************
******************************************************************************/

var RUMSpeedIndex = function(win) {
  win = win || window;
  var doc = win.document;

  /****************************************************************************
    Support Routines
  ****************************************************************************/
  // Get the rect for the visible portion of the provided DOM element
  var GetElementViewportRect = function(el) {
    var intersect = false;
    if (el.getBoundingClientRect) {
      var elRect = el.getBoundingClientRect();
      intersect = {'top': Math.max(elRect.top, 0),
                       'left': Math.max(elRect.left, 0),
                       'bottom': Math.min(elRect.bottom, (win.innerHeight || doc.documentElement.clientHeight)),
                       'right': Math.min(elRect.right, (win.innerWidth || doc.documentElement.clientWidth))};
      if (intersect.bottom <= intersect.top ||
          intersect.right <= intersect.left) {
        intersect = false;
      } else {
        intersect.area = (intersect.bottom - intersect.top) * (intersect.right - intersect.left);
      }
    }
    return intersect;
  };

  // Check a given element to see if it is visible
  var CheckElement = function(el, url) {
    if (url) {
      var rect = GetElementViewportRect(el);
      if (rect) {
        rects.push({'url': url,
                     'area': rect.area,
                     'rect': rect});
      }
    }
  };

  // Get the visible rectangles for elements that we care about
  var GetRects = function() {
    // Walk all of the elements in the DOM (try to only do this once)
    var elements = doc.getElementsByTagName('*');
    var re = /url\(.*(http.*)\)/ig;
    for (var i = 0; i < elements.length; i++) {
      var el = elements[i];
      var style = win.getComputedStyle(el);

      // check for Images
      if (el.tagName == 'IMG') {
        CheckElement(el, el.currentSrc || el.src);
      }
      // Check for background images
      if (style['background-image']) {
        re.lastIndex = 0;
        var matches = re.exec(style['background-image']);
        if (matches && matches.length > 1)
          CheckElement(el, matches[1].replace('"', ''));
      }
      // recursively walk any iFrames
      if (el.tagName == 'IFRAME') {
        try {
          var rect = GetElementViewportRect(el);
          if (rect) {
            var tm = RUMSpeedIndex(el.contentWindow);
            if (tm) {
              rects.push({'tm': tm,
                          'area': rect.area,
                          'rect': rect});
            }
        }
        } catch(e) {
        }
      }
    }
  };

  // Get the time at which each external resource loaded
  var GetRectTimings = function() {
    var timings = {};
    var requests = win.performance.getEntriesByType("resource");
    for (var i = 0; i < requests.length; i++)
      timings[requests[i].name] = requests[i].responseEnd;
    for (var j = 0; j < rects.length; j++) {
      if (!('tm' in rects[j]))
        rects[j].tm = timings[rects[j].url] !== undefined ? timings[rects[j].url] : 0;
    }
  };

  function startLoadTime() {
    // If the browser supports the Navigation Timing 2 and HR Time APIs, use
    // them, otherwise fall back to the Navigation Timing 1 API.
    if (win.PerformanceNavigationTiming && performance.timeOrigin) {
        const ntEntry = performance.getEntriesByType('navigation')[0];
        return (ntEntry.startTime + performance.timeOrigin) / 1000;
    } else {
        return performance.timing.navigationStart / 1000;
    }
}

function requestTime() {
    // If the browser supports the Navigation Timing 2 and HR Time APIs, use
    // them, otherwise fall back to the Navigation Timing 1 API.
    if (win.PerformanceNavigationTiming && performance.timeOrigin) {
        const ntEntry = performance.getEntriesByType('navigation')[0];
        return (ntEntry.startTime + performance.timeOrigin) / 1000;
    } else {
        return performance.timing.navigationStart / 1000;
    }
}

function firstPaintTime() {
    if (win.PerformancePaintTiming) {
      const fpEntry = performance.getEntriesByType('paint')[0];
      return (fpEntry.startTime + performance.timeOrigin) / 1000;
    }
  }

  // Get the first paint time.
  var GetFirstPaint = function() {
    // Navigation Timing 2 and HR Time APIs
    try {
      var startTime = startLoadTime();
      startTime = requestTime();
      firstPaint = (firstPaintTime() - startTime) * 1000.0;
    } catch(e) {
    }

    // Try the standardized paint timing api
    if (startTime === undefined && firstPaint === undefined && !isNaN(firstPaint)) {
      try {
        var entries = performance.getEntriesByType('paint');
        for (var i = 0; i < entries.length; i++) {
          if (entries[i]['name'] == 'first-paint') {
            navStart = performance.getEntriesByType("navigation")[0].startTime;
            firstPaint = entries[i].startTime - navStart;
            break;
          }
        }
      } catch(e) {
      }
    }

    // If the browser supports a first paint event, just use what the browser reports
    if (firstPaint === undefined && 'msFirstPaint' in win.performance.timing)
      firstPaint = win.performance.timing.msFirstPaint - navStart;
    if (firstPaint === undefined && 'chrome' in win && 'loadTimes' in win.chrome) {
      var chromeTimes = win.chrome.loadTimes();
      if ('firstPaintTime' in chromeTimes && chromeTimes.firstPaintTime > 0) {
        var startTime = chromeTimes.startLoadTime;
        if ('requestTime' in chromeTimes)
          startTime = chromeTimes.requestTime;
        if (chromeTimes.firstPaintTime >= startTime)
          firstPaint = (chromeTimes.firstPaintTime - startTime) * 1000.0;
      }
    }
    // For browsers that don't support first-paint or where we get insane values,
    // use the time of the last non-async script or css from the head.
    if (firstPaint === undefined || firstPaint < 0 || firstPaint > 120000) {
      firstPaint = win.performance.timing.responseStart - navStart;
      var headURLs = {};
      var headElements = doc.getElementsByTagName('head')[0].children;
      for (var i = 0; i < headElements.length; i++) {
        var el = headElements[i];
        if (el.tagName == 'SCRIPT' && el.src && !el.async)
          headURLs[el.src] = true;
        if (el.tagName == 'LINK' && el.rel == 'stylesheet' && el.href)
          headURLs[el.href] = true;
      }
      var requests = win.performance.getEntriesByType("resource");
      var doneCritical = false;
      for (var j = 0; j < requests.length; j++) {
        if (!doneCritical &&
            headURLs[requests[j].name] &&
           (requests[j].initiatorType == 'script' || requests[j].initiatorType == 'link')) {
          var requestEnd = requests[j].responseEnd;
          if (firstPaint === undefined || requestEnd > firstPaint)
            firstPaint = requestEnd;
        } else {
          doneCritical = true;
        }
      }
    }
    firstPaint = Math.max(firstPaint, 0);
  };

  // Sort and group all of the paint rects by time and use them to
  // calculate the visual progress
  var CalculateVisualProgress = function() {
    var paints = {'0':0};
    var total = 0;
    for (var i = 0; i < rects.length; i++) {
      var tm = firstPaint;
      if ('tm' in rects[i] && rects[i].tm > firstPaint)
        tm = rects[i].tm;
      if (paints[tm] === undefined)
        paints[tm] = 0;
      paints[tm] += rects[i].area;
      total += rects[i].area;
    }
    // Add a paint area for the page background (count 10% of the pixels not
    // covered by existing paint rects.
    var pixels = Math.max(doc.documentElement.clientWidth, win.innerWidth || 0) *
                 Math.max(doc.documentElement.clientHeight, win.innerHeight || 0);
    if (pixels > 0 ) {
      pixels = Math.max(pixels - total, 0) * pageBackgroundWeight;
      if (paints[firstPaint] === undefined)
        paints[firstPaint] = 0;
      paints[firstPaint] += pixels;
      total += pixels;
    }
    // Calculate the visual progress
    if (total) {
      for (var time in paints) {
        if (paints.hasOwnProperty(time)) {
          progress.push({'tm': time, 'area': paints[time]});
        }
      }
      progress.sort(function(a,b){return a.tm - b.tm;});
      var accumulated = 0;
      for (var j = 0; j < progress.length; j++) {
        accumulated += progress[j].area;
        progress[j].progress = accumulated / total;
      }
    }
  };

  // Given the visual progress information, Calculate the speed index.
  var CalculateSpeedIndex = function() {
    if (progress.length) {
      SpeedIndex = 0;
      var lastTime = 0;
      var lastProgress = 0;
      for (var i = 0; i < progress.length; i++) {
        var elapsed = progress[i].tm - lastTime;
        if (elapsed > 0 && lastProgress < 1)
          SpeedIndex += (1 - lastProgress) * elapsed;
        lastTime = progress[i].tm;
        lastProgress = progress[i].progress;
      }
    } else {
      SpeedIndex = firstPaint;
    }
  };

  /****************************************************************************
    Main flow
  ****************************************************************************/
  var rects = [];
  var progress = [];
  var firstPaint;
  var SpeedIndex;
  var pageBackgroundWeight = 0.1;
  try {
    var navStart = win.performance.timing.navigationStart;
    GetRects();
    GetRectTimings();
    GetFirstPaint();
    CalculateVisualProgress();
    CalculateSpeedIndex();
  } catch(e) {
  }
  /* Debug output for testing
  var dbg = '';
  dbg += "Paint Rects\n";
  for (var i = 0; i < rects.length; i++)
    dbg += '(' + rects[i].area + ') ' + rects[i].tm + ' - ' + rects[i].url + "\n";
  dbg += "Visual Progress\n";
  for (var i = 0; i < progress.length; i++)
    dbg += '(' + progress[i].area + ') ' + progress[i].tm + ' - ' + progress[i].progress + "\n";
  dbg += 'First Paint: ' + firstPaint + "\n";
  dbg += 'Speed Index: ' + SpeedIndex + "\n";
  console.log(dbg);
  */
  return SpeedIndex;
};

if (typeof module !== 'undefined' && module.exports) {
  module.exports = RUMSpeedIndex;
}
<|endoftext|>


<commit_before>/**
 * Interval scale
 * @module echarts/coord/scale/Interval
 *
 * http://en.wikipedia.org/wiki/Level_of_measurement
 */

define(function (require) {

    var mathFloor = Math.floor;
    var mathCeil = Math.ceil;
    /**
     * @alias module:echarts/coord/scale/Interval
     * @param {Array.<number>} data
     * @constructor
     */
    var IntervalScale = function (data) {

        /**
         * Extent
         * @type {Array.<number>}
         * @private
         */
        this._extent = [Infinity, -Infinity];

        if (data) {
            this.setExtentFromData(data);
        }

        /**
         * Step is calculated in adjustExtent
         * @type {Array.<number>}
         * @private
         */
        this._interval = 0;
    };

    IntervalScale.prototype = {

        constructor: IntervalScale,

        type: 'interval',

        /**
         * Normalize value to linear [0, 1]
         * @param {number} val
         * @return {number}
         */
        normalize: function (val) {
            var extent = this._extent;
            return (val - extent[0]) / (extent[1] - extent[0]);
        },

        /**
         * Scale normalized value
         * @param {number} val
         * @return {number}
         */
        scale: function (val) {
            var extent = this._extent;
            return val * (extent[1] - extent[0]) + extent[0];
        },

        /**
         * Set extent from data
         * @param {Array.<number>} data
         * @param {boolean} union If union with current extent
         */
        setExtentFromData: function (data, union) {
            var extent = this._extent;
            var max = union ? extent[1] : -Infinity;
            var min = union ? extent[0] : Infinity;
            for (var i = 0; i < data.length; i++) {
                data[i] > max && (max = data[i]);
                data[i] < min && (min = data[i]);
            }

            this.setExtent(min, max);
        },

        /**
         * Get extent
         * @return {Array.<number>}
         */
        getExtent: function () {
            return this._extent.slice();
        },

        /**
         * Set extent
         * @param {number} start
         * @param {number} end
         */
        setExtent: function (start, end) {
            var thisExtent = this._extent;
            if (! isNaN(start)) {
                thisExtent[0] = start;
            }
            if (! isNaN(end)) {
                thisExtent[1] = end;
            }
        },

        /**
         * Get interval
         */
        getInterval: function () {
            if (! this._interval) {
                this.niceTicks();
            }
            return this._interval;
        },

        /**
         * Set interval
         * @param {number} interval
         */
        setInterval: function (interval) {
            this._interval = interval;
        },

        /**
         * @return {Array.<number>}
         */
        getTicks: function () {
            var interval = this.getInterval();
            var extent = this._extent;
            var ticks = [];

            if (interval) {
                var niceExtent = this._niceExtent;
                if (extent[0] < niceExtent[0]) {
                    ticks.push(extent[0]);
                }
                var tick = niceExtent[0];
                while (tick <= niceExtent[1]) {
                    ticks.push(tick);
                    tick += interval;
                }
                if (extent[1] > niceExtent[1]) {
                    ticks.push(extent[1]);
                }
            }

            return ticks;
        },

        /**
         * @return {Array.<string>}
         */
        getTicksLabels: function () {
            var labels = [];
            var ticks = this.getTicks();
            for (var i = 0; i < ticks.length; i++) {
                labels.push(ticks[i].toString());
            }
            return labels;
        },

        /**
         * Update interval and extent of intervals for nice ticks
         * Algorithm from d3.js
         * @param  {number} [approxTickNum = 10] Given approx tick number
         */
        niceTicks: function (approxTickNum) {
            approxTickNum = approxTickNum || 10;
            var extent = this._extent;
            var span = extent[1] - extent[0];
            if (span === Infinity || span <= 0) {
                return;
            }

            // Figure out step quantity, for example 0.1, 1, 10, 100
            var interval = Math.pow(10, Math.floor(Math.log(span / approxTickNum) / Math.LN10));
            var err = approxTickNum / span * interval;

            // Filter ticks to get closer to the desired count.
            if (err <= .15) {
                interval *= 10;
            }
            else if (err <= .3) {
                interval *= 5;
            }
            else if (err <= .5) {
                interval *= 3;
            }
            else if (err <= .75) {
                interval *= 2;
            }

            var niceExtent = [];
            niceExtent[0] = mathCeil(extent[0] / interval) * interval;
            niceExtent[1] = mathFloor(extent[1] / interval) * interval;

            this._interval = interval;
            this._niceExtent = niceExtent;
        },

        /**
         * Nice extent.
         * @param {number} [approxTickNum = 10] Given approx tick number
         * @param {boolean} [fixMin=false]
         * @param {boolean} [fixMax=false]
         */
        niceExtent: function (approxTickNum, fixMin, fixMax) {
            this.niceTicks(approxTickNum);

            var extent = this._extent;
            var interval = this._interval;

            if (! fixMin) {
                extent[0] = mathFloor(extent[0] / interval) * interval;
            }
            if (! fixMax) {
                extent[1] = mathCeil(extent[1] / interval) * interval;
            }
        }
    };

    return IntervalScale;
});<commit_msg>getExtent consider null<commit_after>/**
 * Interval scale
 * @module echarts/coord/scale/Interval
 *
 * http://en.wikipedia.org/wiki/Level_of_measurement
 */

define(function (require) {

    var mathFloor = Math.floor;
    var mathCeil = Math.ceil;
    /**
     * @alias module:echarts/coord/scale/Interval
     * @param {Array.<number>} data
     * @constructor
     */
    var IntervalScale = function (data) {

        /**
         * Extent
         * @type {Array.<number>}
         * @private
         */
        this._extent = [Infinity, -Infinity];

        if (data) {
            this.setExtentFromData(data);
        }

        /**
         * Step is calculated in adjustExtent
         * @type {Array.<number>}
         * @private
         */
        this._interval = 0;
    };

    IntervalScale.prototype = {

        constructor: IntervalScale,

        type: 'interval',

        /**
         * Normalize value to linear [0, 1]
         * @param {number} val
         * @return {number}
         */
        normalize: function (val) {
            var extent = this._extent;
            return (val - extent[0]) / (extent[1] - extent[0]);
        },

        /**
         * Scale normalized value
         * @param {number} val
         * @return {number}
         */
        scale: function (val) {
            var extent = this._extent;
            return val * (extent[1] - extent[0]) + extent[0];
        },

        /**
         * Set extent from data
         * @param {Array.<number>} data
         * @param {boolean} union If union with current extent
         */
        setExtentFromData: function (data, union) {
            var extent = this._extent;
            var max = union ? extent[1] : -Infinity;
            var min = union ? extent[0] : Infinity;

            for (var i = 0; i < data.length; i++) {
                if (data[i] != null) {
                    data[i] > max && (max = data[i]);
                    data[i] < min && (min = data[i]);
                }
            }

            this.setExtent(min, max);
        },

        /**
         * Get extent
         * @return {Array.<number>}
         */
        getExtent: function () {
            return this._extent.slice();
        },

        /**
         * Set extent
         * @param {number} start
         * @param {number} end
         */
        setExtent: function (start, end) {
            var thisExtent = this._extent;
            if (! isNaN(start)) {
                thisExtent[0] = start;
            }
            if (! isNaN(end)) {
                thisExtent[1] = end;
            }
        },

        /**
         * Get interval
         */
        getInterval: function () {
            if (! this._interval) {
                this.niceTicks();
            }
            return this._interval;
        },

        /**
         * Set interval
         * @param {number} interval
         */
        setInterval: function (interval) {
            this._interval = interval;
        },

        /**
         * @return {Array.<number>}
         */
        getTicks: function () {
            var interval = this.getInterval();
            var extent = this._extent;
            var ticks = [];

            if (interval) {
                var niceExtent = this._niceExtent;
                if (extent[0] < niceExtent[0]) {
                    ticks.push(extent[0]);
                }
                var tick = niceExtent[0];
                while (tick <= niceExtent[1]) {
                    ticks.push(tick);
                    tick += interval;
                }
                if (extent[1] > niceExtent[1]) {
                    ticks.push(extent[1]);
                }
            }

            return ticks;
        },

        /**
         * @return {Array.<string>}
         */
        getTicksLabels: function () {
            var labels = [];
            var ticks = this.getTicks();
            for (var i = 0; i < ticks.length; i++) {
                labels.push(ticks[i].toString());
            }
            return labels;
        },

        /**
         * Update interval and extent of intervals for nice ticks
         * Algorithm from d3.js
         * @param  {number} [approxTickNum = 10] Given approx tick number
         */
        niceTicks: function (approxTickNum) {
            approxTickNum = approxTickNum || 10;
            var extent = this._extent;
            var span = extent[1] - extent[0];
            if (span === Infinity || span <= 0) {
                return;
            }

            // Figure out step quantity, for example 0.1, 1, 10, 100
            var interval = Math.pow(10, Math.floor(Math.log(span / approxTickNum) / Math.LN10));
            var err = approxTickNum / span * interval;

            // Filter ticks to get closer to the desired count.
            if (err <= .15) {
                interval *= 10;
            }
            else if (err <= .3) {
                interval *= 5;
            }
            else if (err <= .5) {
                interval *= 3;
            }
            else if (err <= .75) {
                interval *= 2;
            }

            var niceExtent = [];
            niceExtent[0] = mathCeil(extent[0] / interval) * interval;
            niceExtent[1] = mathFloor(extent[1] / interval) * interval;

            this._interval = interval;
            this._niceExtent = niceExtent;
        },

        /**
         * Nice extent.
         * @param {number} [approxTickNum = 10] Given approx tick number
         * @param {boolean} [fixMin=false]
         * @param {boolean} [fixMax=false]
         */
        niceExtent: function (approxTickNum, fixMin, fixMax) {
            this.niceTicks(approxTickNum);

            var extent = this._extent;
            var interval = this._interval;

            if (! fixMin) {
                extent[0] = mathFloor(extent[0] / interval) * interval;
            }
            if (! fixMax) {
                extent[1] = mathCeil(extent[1] / interval) * interval;
            }
        }
    };

    return IntervalScale;
});<|endoftext|>