<commit_before>"use strict";
/*
 *
 * ooiui/static/js/models/aa/AlertFilterView.js
 * Validation model for Alerts and Alarms Page.
 *
 * Dependencies
 * Partials
 * - ooiui/static/js/partials/compiled/alertPage.js
 * Libs
 * - ooiui/static/lib/underscore/underscore.js
 * - ooiui/static/lib/backbone/backbone.js
 * Usage
 *
 */
Backbone.Validation.configure({
  forceUpdate: true
});

_.extend(Backbone.Validation.callbacks, {
  valid: function (view, attr, selector) {
    var $el = view.$('[name=' + attr + ']'),
      $group = $el.closest('.form-group');

    $group.removeClass('has-error');
    $group.find('.help-block').html('').addClass('hidden');
  },
  invalid: function (view, attr, error, selector) {
    var $el = view.$('[name=' + attr + ']'),
      $group = $el.closest('.form-group');

    $group.addClass('has-error');
    $group.find('.help-block').html(error).removeClass('hidden');
  }
});

var AlertFilterView = Backbone.View.extend({
  events: {
    /*'click #saveAlert': function (e) {
     e.preventDefault();
     this.submit();
     }*/
  },

  // Use stickit to perform binding between
  // the model and the view
  bindings: {
    '[name=array]': {
      observe: 'array',
      selectOptions: {
        collection: []
      },

      setOptions: {
        validate:false
      }
    },
    '[name=Platform]': {
      observe: 'Platform',
      setOptions: {
        validate: false
      }
    },
    '[name=Instrument]': {
      observe: 'Instrument',
      selectOptions:{
        collections: []
      },
      setOptions: {
        validate: false
      }
    },
    '[name=Email]': {
      observe: 'Email',
      setOptions: {
        validate: false
      }
    },
    '[name=Redmine]': {
      observe: 'Redmine',
      setOptions: {
        validate: false
      }
    },
    '[name=PhoneCall]': {
      observe: 'PhoneCall',
      setOptions: {
        validate: true
      }
    },

    '[name=TextMessage]': {
      observe: 'TextMessage',
      setOptions: {
        validate: false
      }
    },
    '[name=LogEvent]': {
      observe: 'LogEvent',
      setOptions: {
        validate: false
      }
    }
  },
  initialRender: function() {
    this.$el.html(' ');
  },
  initialize: function () {
    Backbone.Validation.bind(this);
    _.bindAll(this, "render","filterOptionsbyInstrument","showtypeoptions","showenabledoptions" );
    var self = this;
    self.modalDialog = new ModalDialogView();
    //this.listenTo(this.collection, 'reset', function(){
    //self.render();
    //});
  },

  render: function () {
    var self = this;

    var AlertsFullCollectionPage = Backbone.PageableCollection.extend({
      model: AlertModel,
      url:"/api/aa/alerts",
      state: {
        pageSize: 20
      },
      mode: "client",
      parse: function(response, options) {
        //for the response after asset query
        if(response.alert_alarm_definition){
          return response.alert_alarm_definition;
        }
        else{
          return response
        }
      }
    });

    var pageablealerts = new AlertsFullCollectionPage();
    self.collection = pageablealerts;

    var HtmlCell = Backgrid.HtmlCell = Backgrid.Cell.extend({
      className: "html-cell",
      initialize: function () {
        Backgrid.Cell.prototype.initialize.apply(this, arguments);
      },
      render: function () {
        this.$el.empty();
        var rawValue = this.model.get(this.column.get("name"));
        var formattedValue = this.formatter.fromRaw(rawValue, this.model);
        this.$el.append(formattedValue);
        this.delegateEvents();
        return this;
      }
    });

    var columns = [
      {
        name: "uframe_filter_id", // The key of the model attribute
        label: "ID", // The name to display in the header
        editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
        cell: "string"
      },
      {
        name: "array_name", // The key of the model attribute
        label: "Array", // The name to display in the header
        editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
        cell: "string"
      },
      {
        name: "instrument_name",
        label: "Instrument",
        editable: false,
        cell: "string",
        sortValue: function (model, colName) {
          return model.attributes[colName]['instrument_name'];
        }
      },
      {
        name: "severity",
        label: "Severity",
        editable: false,
        cell: "string",
        formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
          fromRaw: function (rawValue, model) {
            return rawValue;
          }
        })
      },
      {
        name: "created_time",
        label: "Created",
        editable: false,
        cell: "string"
      },
      {
        name: "stream",
        label: "Stream Name",
        editable: false,
        cell: "string"
      },
      {
        name: "description",
        label: "Description",
        editable: false,
        cell: "string"
      },
      {
        name: "event_type",
        editable: false,
        label: "Type",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            //place holder right now for triggered events
            if(rawValue =='alarm'){
              return "Alarm";
            }
            else if(rawValue =='alert'){
              return "Alert";
            }
          }
        })
      },
      {
        name: "active",
        editable: false,
        label: "Active",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            //console.log(rawValue);
            //place holder right now for triggered events
            if(rawValue == 1){
              //return "Active";
              return "Active";
            }
            else if(rawValue == 0){
              //return "Disabled";
              return "Disabled";
            }
          }
        })
      },
      {
        name: "active",
        editable: false,
        label: "O/I",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            console.log('Active Field');
            console.log(rawValue);
            console.log(model.attributes.retired);
            //place holder right now for triggered events
            if (model.attributes.retired == 0) {
              if (rawValue == 1) {
                return "<button type=\"button\" id=\"toggleActiveBtn\" class=\"btn btn-primary\">" +
                  " Disable Alert/Alarm" +
                  "</button>";
              }
              else if (rawValue == 0) {
                return "<button type=\"button\" id=\"toggleActiveBtn\" class=\"btn btn-primary\">" +
                  " Enable Alert/Alarm" +
                  "</button>";
              }
            }
            else {
              return "Retired";
            }
          }
        })
      },
      {
        name: "retired",
        editable: false,
        label: "Retire",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            console.log('Retired Field');
            console.log(rawValue);
            //console.log(rawValue);
            //place holder right now for triggered events
            if(rawValue == 1){
              return "Retired";
            }
            else {
              return "<button disabled type=\"button\" id=\"toggleRetireBtn\" class=\"btn btn-primary\">" +
                " Retire" +
                "</button>";
            }
          }
        })
      }
    ];

    //add click event
    var ClickableRow = Backgrid.Row.extend({
      highlightColor: "#eee",
      events: {
        "click": "onClick",
        mouseover: "rowFocused",
        mouseout: "rowLostFocus"
      },
      onClick: function (e) {
        if ($(e.target).is('td')){
          ooi.trigger("deployrowclicked", this.model);
        }

        this.el.style.backgroundColor = this.highlightColor;

        // Clicked the toggle active/inactive button
        if (e.target.id=='toggleActiveBtn') {
          console.log('clicked toggle active btn for def id: ' + this.model.attributes.id);
          ooi.trigger('alertToggleActiveFormViewTrigger:onClick',
            {
              model: this.model,
              active: this.model.attributes.active
            }
          );
        }

        //check to see if the condtion met item has ben clicked and open triggered events
        if(e.target.id=='condition_met'){
          this.triggeredalertView = new TriggeredAlertDialogView();
          $('.container-fluid').first().append(this.triggeredalertView.el);

          this.triggeredalertView.show({
            instrument: "Instrument Name: "  + this.model.attributes.Instrument,
            recent: "None at this time",
            history: "",
            variable: this.model.attributes.reference_designator,
            //ctype: "alert",
            //title: this.model.attributes.display_name,
            ack: function() { console.log("Closed");}
          });
        }
      },
      rowFocused: function() {
        this.el.style.backgroundColor = this.highlightColor;
      },
      rowLostFocus: function() {
        this.el.style.backgroundColor = '#FFF';
        //document.getElementById('toggleActiveBtn').style.display = "none";
      }
    });

    // Set up a grid to use the pageable collection
    var pageableGrid = new Backgrid.Grid({
      columns: columns,
      collection: pageablealerts,
      row: ClickableRow
    });

    // Render the grid and attach the root to your HTML document
    $("#alertslist").append(pageableGrid.render().el);

    // Initialize the paginator
    var paginator = new Backgrid.Extension.Paginator({
      collection: pageablealerts
    });

    // Render the paginator
    $("#alertslist").after(paginator.render().el);

    var AssetFilter = Backgrid.Extension.ClientSideFilter.extend({
      placeholder: "Search Alerts and Alarms",
      makeMatcher: function(query){
        var q = '';
        if(query!=""){
          q = String(query).toUpperCase();
        }
        return function (model) {
          var queryhit= false;
          if(model.attributes['instrument_name']){
            if(String(model.attributes['instrument_name']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['description']){
            if(String(model.attributes['description']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['array_name']){
            if(String(model.attributes['array_name']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['priority']){
            if(String(model.attributes['priority']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          return queryhit;
        };
      }
    });

    var filter = new AssetFilter({
      collection: pageablealerts
    });
    self.filter = filter;

    // Render the filter
    $("#alertslist").before(filter.render().el);
    // Add some space to the filter and move it to the right
    $(filter.el).css({float: "right", margin: "0px 0px 25px 0px"});
    $(filter.el).find("input").attr('id', 'alert_search_box');

    pageablealerts.fetch({reset: true,
      error: (function (e) {
        alert(' Service request failure: ' + e);
      }),
      complete: (function (e) {
        $('#loading_alerts').html('');
      })
    });

    self.rownum = 1;

    //remove row
    $("#delete_row").click(function(){
      if(self.rownum == 2){
        $("#delete_row").hide();
      }
      if(self.rownum>1){
        $("#addr"+(self.rownum-1)).html('');
        self.rownum--;
      }
    });

    //needs model to stickit not using right now
    //this.stickit();
  },

  filterOptionsbyInstrument: function(instru_id,name){
    var self = this;
    return "<select class='form-control' data-container='body' id='conditions_dd"+this.rownum+"'><option value='m_lon'>Longitude</option><option value='m_lat'>Latitude</option><option value='sci_salinity'>Salinity</option><option value='sci_water_temp'>Temperature</option><option  value='sci_water_cond'>Water Speed</option><option value='sci_wave_height'>Wave Height</option><option value='sci_water_pressure'>Water Pressure</option></select>";
  },

  showtypeoptions:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='type_dd"+this.rownum+"'><option  data-icon='glyphicon-flag' value='alert'>Alert</option><option data-icon='glyphicon-exclamation-sign' value='alarm'> Alarm</option></select>";
  },

  showenabledoptions:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='enabled_dd"+this.rownum+"'><option data-icon='glyphicon-ok-sign' value='true'>True</option><option data-icon='glyphicon-minus-sign' value='false'> False</option></select>";
  },

  showenabledoperators:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='operator_dd"+this.rownum+"'><option value='>'>></option><option  value='<'> <</option><option  value='='> =</option><option  value='<>'> <></option><option  value='>='> >=</option><option  value='<='> <=</option><option  value='outside'> outside</option><option  value='inside'> inside</option></select>";
  }

});
<commit_msg>changed search box to float left<commit_after>"use strict";
/*
 *
 * ooiui/static/js/models/aa/AlertFilterView.js
 * Validation model for Alerts and Alarms Page.
 *
 * Dependencies
 * Partials
 * - ooiui/static/js/partials/compiled/alertPage.js
 * Libs
 * - ooiui/static/lib/underscore/underscore.js
 * - ooiui/static/lib/backbone/backbone.js
 * Usage
 *
 */
Backbone.Validation.configure({
  forceUpdate: true
});

_.extend(Backbone.Validation.callbacks, {
  valid: function (view, attr, selector) {
    var $el = view.$('[name=' + attr + ']'),
      $group = $el.closest('.form-group');

    $group.removeClass('has-error');
    $group.find('.help-block').html('').addClass('hidden');
  },
  invalid: function (view, attr, error, selector) {
    var $el = view.$('[name=' + attr + ']'),
      $group = $el.closest('.form-group');

    $group.addClass('has-error');
    $group.find('.help-block').html(error).removeClass('hidden');
  }
});

var AlertFilterView = Backbone.View.extend({
  events: {
    /*'click #saveAlert': function (e) {
     e.preventDefault();
     this.submit();
     }*/
  },

  // Use stickit to perform binding between
  // the model and the view
  bindings: {
    '[name=array]': {
      observe: 'array',
      selectOptions: {
        collection: []
      },

      setOptions: {
        validate:false
      }
    },
    '[name=Platform]': {
      observe: 'Platform',
      setOptions: {
        validate: false
      }
    },
    '[name=Instrument]': {
      observe: 'Instrument',
      selectOptions:{
        collections: []
      },
      setOptions: {
        validate: false
      }
    },
    '[name=Email]': {
      observe: 'Email',
      setOptions: {
        validate: false
      }
    },
    '[name=Redmine]': {
      observe: 'Redmine',
      setOptions: {
        validate: false
      }
    },
    '[name=PhoneCall]': {
      observe: 'PhoneCall',
      setOptions: {
        validate: true
      }
    },

    '[name=TextMessage]': {
      observe: 'TextMessage',
      setOptions: {
        validate: false
      }
    },
    '[name=LogEvent]': {
      observe: 'LogEvent',
      setOptions: {
        validate: false
      }
    }
  },
  initialRender: function() {
    this.$el.html(' ');
  },
  initialize: function () {
    Backbone.Validation.bind(this);
    _.bindAll(this, "render","filterOptionsbyInstrument","showtypeoptions","showenabledoptions" );
    var self = this;
    self.modalDialog = new ModalDialogView();
    //this.listenTo(this.collection, 'reset', function(){
    //self.render();
    //});
  },

  render: function () {
    var self = this;

    var AlertsFullCollectionPage = Backbone.PageableCollection.extend({
      model: AlertModel,
      url:"/api/aa/alerts",
      state: {
        pageSize: 20
      },
      mode: "client",
      parse: function(response, options) {
        //for the response after asset query
        if(response.alert_alarm_definition){
          return response.alert_alarm_definition;
        }
        else{
          return response
        }
      }
    });

    var pageablealerts = new AlertsFullCollectionPage();
    self.collection = pageablealerts;

    var HtmlCell = Backgrid.HtmlCell = Backgrid.Cell.extend({
      className: "html-cell",
      initialize: function () {
        Backgrid.Cell.prototype.initialize.apply(this, arguments);
      },
      render: function () {
        this.$el.empty();
        var rawValue = this.model.get(this.column.get("name"));
        var formattedValue = this.formatter.fromRaw(rawValue, this.model);
        this.$el.append(formattedValue);
        this.delegateEvents();
        return this;
      }
    });

    var columns = [
      {
        name: "uframe_filter_id", // The key of the model attribute
        label: "ID", // The name to display in the header
        editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
        cell: "string"
      },
      {
        name: "array_name", // The key of the model attribute
        label: "Array", // The name to display in the header
        editable: false, // By default every cell in a column is editable, but *ID* shouldn't be
        cell: "string"
      },
      {
        name: "instrument_name",
        label: "Instrument",
        editable: false,
        cell: "string",
        sortValue: function (model, colName) {
          return model.attributes[colName]['instrument_name'];
        }
      },
      {
        name: "severity",
        label: "Severity",
        editable: false,
        cell: "string",
        formatter: _.extend({}, Backgrid.CellFormatter.prototype, {
          fromRaw: function (rawValue, model) {
            return rawValue;
          }
        })
      },
      {
        name: "created_time",
        label: "Created",
        editable: false,
        cell: "string"
      },
      {
        name: "stream",
        label: "Stream Name",
        editable: false,
        cell: "string"
      },
      {
        name: "description",
        label: "Description",
        editable: false,
        cell: "string"
      },
      {
        name: "event_type",
        editable: false,
        label: "Type",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            //place holder right now for triggered events
            if(rawValue =='alarm'){
              return "Alarm";
            }
            else if(rawValue =='alert'){
              return "Alert";
            }
          }
        })
      },
      {
        name: "active",
        editable: false,
        label: "Active",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            //console.log(rawValue);
            //place holder right now for triggered events
            if(rawValue == 1){
              //return "Active";
              return "Active";
            }
            else if(rawValue == 0){
              //return "Disabled";
              return "Disabled";
            }
          }
        })
      },
      {
        name: "active",
        editable: false,
        label: "O/I",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            console.log('Active Field');
            console.log(rawValue);
            console.log(model.attributes.retired);
            //place holder right now for triggered events
            if (model.attributes.retired == 0) {
              if (rawValue == 1) {
                return "<button type=\"button\" id=\"toggleActiveBtn\" class=\"btn btn-primary\">" +
                  " Disable Alert/Alarm" +
                  "</button>";
              }
              else if (rawValue == 0) {
                return "<button type=\"button\" id=\"toggleActiveBtn\" class=\"btn btn-primary\">" +
                  " Enable Alert/Alarm" +
                  "</button>";
              }
            }
            else {
              return "Retired";
            }
          }
        })
      },
      {
        name: "retired",
        editable: false,
        label: "Retire",
        cell: HtmlCell,
        formatter: _.extend({}, Backgrid.Cell.prototype, {
          fromRaw: function (rawValue, model) {
            console.log('Retired Field');
            console.log(rawValue);
            //console.log(rawValue);
            //place holder right now for triggered events
            if(rawValue == 1){
              return "Retired";
            }
            else {
              return "<button disabled type=\"button\" id=\"toggleRetireBtn\" class=\"btn btn-primary\">" +
                " Retire" +
                "</button>";
            }
          }
        })
      }
    ];

    //add click event
    var ClickableRow = Backgrid.Row.extend({
      highlightColor: "#eee",
      events: {
        "click": "onClick",
        mouseover: "rowFocused",
        mouseout: "rowLostFocus"
      },
      onClick: function (e) {
        if ($(e.target).is('td')){
          ooi.trigger("deployrowclicked", this.model);
        }

        this.el.style.backgroundColor = this.highlightColor;

        // Clicked the toggle active/inactive button
        if (e.target.id=='toggleActiveBtn') {
          console.log('clicked toggle active btn for def id: ' + this.model.attributes.id);
          ooi.trigger('alertToggleActiveFormViewTrigger:onClick',
            {
              model: this.model,
              active: this.model.attributes.active
            }
          );
        }

        //check to see if the condtion met item has ben clicked and open triggered events
        if(e.target.id=='condition_met'){
          this.triggeredalertView = new TriggeredAlertDialogView();
          $('.container-fluid').first().append(this.triggeredalertView.el);

          this.triggeredalertView.show({
            instrument: "Instrument Name: "  + this.model.attributes.Instrument,
            recent: "None at this time",
            history: "",
            variable: this.model.attributes.reference_designator,
            //ctype: "alert",
            //title: this.model.attributes.display_name,
            ack: function() { console.log("Closed");}
          });
        }
      },
      rowFocused: function() {
        this.el.style.backgroundColor = this.highlightColor;
      },
      rowLostFocus: function() {
        this.el.style.backgroundColor = '#FFF';
        //document.getElementById('toggleActiveBtn').style.display = "none";
      }
    });

    // Set up a grid to use the pageable collection
    var pageableGrid = new Backgrid.Grid({
      columns: columns,
      collection: pageablealerts,
      row: ClickableRow
    });

    // Render the grid and attach the root to your HTML document
    $("#alertslist").append(pageableGrid.render().el);

    // Initialize the paginator
    var paginator = new Backgrid.Extension.Paginator({
      collection: pageablealerts
    });

    // Render the paginator
    $("#alertslist").after(paginator.render().el);

    var AssetFilter = Backgrid.Extension.ClientSideFilter.extend({
      placeholder: "Search Alerts and Alarms",
      makeMatcher: function(query){
        var q = '';
        if(query!=""){
          q = String(query).toUpperCase();
        }
        return function (model) {
          var queryhit= false;
          if(model.attributes['instrument_name']){
            if(String(model.attributes['instrument_name']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['description']){
            if(String(model.attributes['description']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['array_name']){
            if(String(model.attributes['array_name']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          if(model.attributes['priority']){
            if(String(model.attributes['priority']).toUpperCase().search(q)>-1){
              queryhit= true;
            }
          }
          return queryhit;
        };
      }
    });

    var filter = new AssetFilter({
      collection: pageablealerts
    });
    self.filter = filter;

    // Render the filter
    $("#alertslist").before(filter.render().el);
    // Add some space to the filter and move it to the right
    $(filter.el).css({float: "left", margin: "0px 10px 25px 0px"});
    $(filter.el).find("input").attr('id', 'alert_search_box');

    pageablealerts.fetch({reset: true,
      error: (function (e) {
        alert(' Service request failure: ' + e);
      }),
      complete: (function (e) {
        $('#loading_alerts').html('');
      })
    });

    self.rownum = 1;

    //remove row
    $("#delete_row").click(function(){
      if(self.rownum == 2){
        $("#delete_row").hide();
      }
      if(self.rownum>1){
        $("#addr"+(self.rownum-1)).html('');
        self.rownum--;
      }
    });

    //needs model to stickit not using right now
    //this.stickit();
  },

  filterOptionsbyInstrument: function(instru_id,name){
    var self = this;
    return "<select class='form-control' data-container='body' id='conditions_dd"+this.rownum+"'><option value='m_lon'>Longitude</option><option value='m_lat'>Latitude</option><option value='sci_salinity'>Salinity</option><option value='sci_water_temp'>Temperature</option><option  value='sci_water_cond'>Water Speed</option><option value='sci_wave_height'>Wave Height</option><option value='sci_water_pressure'>Water Pressure</option></select>";
  },

  showtypeoptions:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='type_dd"+this.rownum+"'><option  data-icon='glyphicon-flag' value='alert'>Alert</option><option data-icon='glyphicon-exclamation-sign' value='alarm'> Alarm</option></select>";
  },

  showenabledoptions:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='enabled_dd"+this.rownum+"'><option data-icon='glyphicon-ok-sign' value='true'>True</option><option data-icon='glyphicon-minus-sign' value='false'> False</option></select>";
  },

  showenabledoperators:function(){
    return "<select data-show-icon='true' data-container='body' class='form-control' id='operator_dd"+this.rownum+"'><option value='>'>></option><option  value='<'> <</option><option  value='='> =</option><option  value='<>'> <></option><option  value='>='> >=</option><option  value='<='> <=</option><option  value='outside'> outside</option><option  value='inside'> inside</option></select>";
  }

});
<|endoftext|>


<commit_before>"use strict";

var url = require('url'),
    QS = require('querystring'),
    $ = require('jquery'),
    L = require('leaflet'),
    console = require('console-browserify');

exports.getLastUrlSegment = function(urlString) {
    var parts = getUrlSegments(urlString);
    return parts[parts.length - 1];
};

var getUrlSegments = exports.getUrlSegments = function(inputUrl) {
    var pathname = url.parse(inputUrl || window.location.href, false).pathname;

    if (endsWith(pathname, '/')) {
        pathname = pathname.substring(0, pathname.length - 1);
    }

    return pathname.split('/');
};

exports.removeLastUrlSegment = function(inputUrl) {
    var updatedurl = url.parse(inputUrl || window.location.href, false);
    var segs = getUrlSegments(inputUrl);
    segs.pop();

    updatedurl.pathname = segs.join('/');

    if (updatedurl.pathname[updatedurl.pathname.length - 1] != '/') {
        updatedurl.pathname += '/';
    }

    return url.format(updatedurl);
};

exports.appendSegmentToUrl = function (segment, inputUrl, appendSlash) {
    var parsedUrl = url.parse(inputUrl || window.location.href, false);
    var segs = getUrlSegments(inputUrl);

    segs.push(segment);

    parsedUrl.pathname = segs.join('/');

    var formattedUrl = url.format(parsedUrl);

    if (appendSlash) {
        formattedUrl += '/';
    }

    return formattedUrl;
};

var parseQueryString = exports.parseQueryString = function () {
    var match,
        pl     = /\+/g,  // Regex for replacing addition symbol with a space
        search = /([^&=]+)=?([^&]*)/g,
        decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
        query  = window.location.search.substring(1),
        urlParams = {};

    while ((match = search.exec(query))) {
        urlParams[decode(match[1])] = decode(match[2]);
    }

    return urlParams;
};

exports.$find = function (selector, $parent) {
    // Find 'selector' via JQuery, inside $parent if specified.
    // Log to console if not found.
    var $el;
    if ($parent) {
        $el = $parent.find(selector);
    } else {
        $el = $(selector);
    }
    if ($el.length === 0) {
        console.warn('Selector not found: ' + selector);
    }
    return $el;
};

exports.webMercatorToLatLng = function(x, y) {
    var originShift =  (2.0 * Math.PI * 6378137.0 / 2.0) / 180.0;
    var d2r = Math.PI / 180.0;
    var r2d = 180.0 / Math.PI;

    var lat = r2d * ((2.0 * Math.atan(Math.exp(d2r * y / originShift))) - Math.PI / 2.0);
    return {lat: lat, lng: x / originShift};

};

exports.webMercatorToLeafletLatLng = function(x, y) {
    var latLng = exports.webMercatorToLatLng(x, y),
        leafletLatLng = L.latLng(latLng.lat, latLng.lng);
    return leafletLatLng;
};

exports.lonLatToWebMercator = function(lon, lat) {
    var originShift = (2.0 * Math.PI * (6378137.0 / 2.0)) / 180.0;
    return {
        x: originShift * lon,
        y: originShift * (Math.log(Math.tan((90.0 + lat) * (Math.PI / 360.0)))) / (Math.PI / 180.0)
    };
};

var endsWith = exports.endsWith = function(str, ends) {
    if (ends === '') return true;
    if (str === null || ends === null) return false;

    return str.length >= ends.length &&
        str.slice(str.length - ends.length) === ends;
};
<commit_msg>Restore mistakenly-deleted utility function `getUpdatedQueryString`<commit_after>"use strict";

var url = require('url'),
    QS = require('querystring'),
    $ = require('jquery'),
    L = require('leaflet'),
    console = require('console-browserify');

exports.getUpdatedQueryString = function (k, v) {
    var parsedUrl = url.parse(window.location.href, true);
    var query = parsedUrl.query || {};

    query[k] = v;

    return QS.stringify(query);
};

exports.getLastUrlSegment = function(urlString) {
    var parts = getUrlSegments(urlString);
    return parts[parts.length - 1];
};

var getUrlSegments = exports.getUrlSegments = function(inputUrl) {
    var pathname = url.parse(inputUrl || window.location.href, false).pathname;

    if (endsWith(pathname, '/')) {
        pathname = pathname.substring(0, pathname.length - 1);
    }

    return pathname.split('/');
};

exports.removeLastUrlSegment = function(inputUrl) {
    var updatedurl = url.parse(inputUrl || window.location.href, false);
    var segs = getUrlSegments(inputUrl);
    segs.pop();

    updatedurl.pathname = segs.join('/');

    if (updatedurl.pathname[updatedurl.pathname.length - 1] != '/') {
        updatedurl.pathname += '/';
    }

    return url.format(updatedurl);
};

exports.appendSegmentToUrl = function (segment, inputUrl, appendSlash) {
    var parsedUrl = url.parse(inputUrl || window.location.href, false);
    var segs = getUrlSegments(inputUrl);

    segs.push(segment);

    parsedUrl.pathname = segs.join('/');

    var formattedUrl = url.format(parsedUrl);

    if (appendSlash) {
        formattedUrl += '/';
    }

    return formattedUrl;
};

var parseQueryString = exports.parseQueryString = function () {
    var match,
        pl     = /\+/g,  // Regex for replacing addition symbol with a space
        search = /([^&=]+)=?([^&]*)/g,
        decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
        query  = window.location.search.substring(1),
        urlParams = {};

    while ((match = search.exec(query))) {
        urlParams[decode(match[1])] = decode(match[2]);
    }

    return urlParams;
};

exports.$find = function (selector, $parent) {
    // Find 'selector' via JQuery, inside $parent if specified.
    // Log to console if not found.
    var $el;
    if ($parent) {
        $el = $parent.find(selector);
    } else {
        $el = $(selector);
    }
    if ($el.length === 0) {
        console.warn('Selector not found: ' + selector);
    }
    return $el;
};

exports.webMercatorToLatLng = function(x, y) {
    var originShift =  (2.0 * Math.PI * 6378137.0 / 2.0) / 180.0;
    var d2r = Math.PI / 180.0;
    var r2d = 180.0 / Math.PI;

    var lat = r2d * ((2.0 * Math.atan(Math.exp(d2r * y / originShift))) - Math.PI / 2.0);
    return {lat: lat, lng: x / originShift};

};

exports.webMercatorToLeafletLatLng = function(x, y) {
    var latLng = exports.webMercatorToLatLng(x, y),
        leafletLatLng = L.latLng(latLng.lat, latLng.lng);
    return leafletLatLng;
};

exports.lonLatToWebMercator = function(lon, lat) {
    var originShift = (2.0 * Math.PI * (6378137.0 / 2.0)) / 180.0;
    return {
        x: originShift * lon,
        y: originShift * (Math.log(Math.tan((90.0 + lat) * (Math.PI / 360.0)))) / (Math.PI / 180.0)
    };
};

var endsWith = exports.endsWith = function(str, ends) {
    if (ends === '') return true;
    if (str === null || ends === null) return false;

    return str.length >= ends.length &&
        str.slice(str.length - ends.length) === ends;
};
<|endoftext|>


<commit_before>import {Fragment} from "./fragment"
import {Mark} from "./mark"
import {Slice, replace} from "./replace"
import {ResolvedPos} from "./resolvedpos"
import {compareDeep} from "./comparedeep"

const emptyAttrs = Object.create(null)

// ::- This class represents a node in the tree that makes up a
// ProseMirror document. So a document is an instance of `Node`, with
// children that are also instances of `Node`.
//
// Nodes are persistent data structures. Instead of changing them, you
// create new ones with the content you want. Old ones keep pointing
// at the old document shape. This is made cheaper by sharing
// structure between the old and new data as much as possible, which a
// tree shape like this (without back pointers) makes easy.
//
// **Do not** directly mutate the properties of a `Node` object. See
// [the guide](/docs/guide/#doc) for more information.
export class Node {
  constructor(type, attrs, content, marks) {
    // :: NodeType
    // The type of node that this is.
    this.type = type

    // :: Object
    // An object mapping attribute names to values. The kind of
    // attributes allowed and required are
    // [determined](#model.NodeSpec.attrs) by the node type.
    this.attrs = attrs

    // :: Fragment
    // A container holding the node's children.
    this.content = content || Fragment.empty

    // :: [Mark]
    // The marks (things like whether it is emphasized or part of a
    // link) applied to this node.
    this.marks = marks || Mark.none
  }

  // text:: ?string
  // For text nodes, this contains the node's text content.

  // :: number
  // The size of this node, as defined by the integer-based [indexing
  // scheme](/docs/guide/#doc.indexing). For text nodes, this is the
  // amount of characters. For other leaf nodes, it is one. For
  // non-leaf nodes, it is the size of the content plus two (the start
  // and end token).
  get nodeSize() { return this.isLeaf ? 1 : 2 + this.content.size }

  // :: number
  // The number of children that the node has.
  get childCount() { return this.content.childCount }

  // :: (number) → Node
  // Get the child node at the given index. Raises an error when the
  // index is out of range.
  child(index) { return this.content.child(index) }

  // :: (number) → ?Node
  // Get the child node at the given index, if it exists.
  maybeChild(index) { return this.content.maybeChild(index) }

  // :: ((node: Node, offset: number, index: number))
  // Call `f` for every child node, passing the node, its offset
  // into this parent node, and its index.
  forEach(f) { this.content.forEach(f) }

  // :: (number, number, (node: Node, pos: number, parent: Node, index: number) → ?bool)
  // Invoke a callback for all descendant nodes recursively between
  // the given two positions that are relative to start of this node's
  // content. The callback is invoked with the node, its
  // parent-relative position, its parent node, and its child index.
  // When the callback returns false for a given node, that node's
  // children will not be recursed over.
  nodesBetween(from, to, f, pos = 0) {
    this.content.nodesBetween(from, to, f, pos, this)
  }

  // :: ((node: Node, pos: number, parent: Node) → ?bool)
  // Call the given callback for every descendant node. Doesn't
  // descend into a node when the callback returns `false`.
  descendants(f) {
    this.nodesBetween(0, this.content.size, f)
  }

  // :: string
  // Concatenates all the text nodes found in this fragment and its
  // children.
  get textContent() { return this.textBetween(0, this.content.size, "") }

  // :: (number, number, ?string, ?string) → string
  // Get all text between positions `from` and `to`. When
  // `blockSeparator` is given, it will be inserted whenever a new
  // block node is started. When `leafText` is given, it'll be
  // inserted for every non-text leaf node encountered.
  textBetween(from, to, blockSeparator, leafText) {
    return this.content.textBetween(from, to, blockSeparator, leafText)
  }

  // :: ?Node
  // Returns this node's first child, or `null` if there are no
  // children.
  get firstChild() { return this.content.firstChild }

  // :: ?Node
  // Returns this node's last child, or `null` if there are no
  // children.
  get lastChild() { return this.content.lastChild }

  // :: (Node) → bool
  // Test whether two nodes represent the same piece of document.
  eq(other) {
    return this == other || (this.sameMarkup(other) && this.content.eq(other.content))
  }

  // :: (Node) → bool
  // Compare the markup (type, attributes, and marks) of this node to
  // those of another. Returns `true` if both have the same markup.
  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  // :: (NodeType, ?Object, ?[Mark]) → bool
  // Check whether this node's markup correspond to the given type,
  // attributes, and marks.
  hasMarkup(type, attrs, marks) {
    return this.type == type &&
      compareDeep(this.attrs, attrs || type.defaultAttrs || emptyAttrs) &&
      Mark.sameSet(this.marks, marks || Mark.none)
  }

  // :: (?Fragment) → Node
  // Create a new node with the same markup as this node, containing
  // the given content (or empty, if no content is given).
  copy(content = null) {
    if (content == this.content) return this
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  // :: ([Mark]) → Node
  // Create a copy of this node, with the given set of marks instead
  // of the node's own marks.
  mark(marks) {
    return marks == this.marks ? this : new this.constructor(this.type, this.attrs, this.content, marks)
  }

  // :: (number, ?number) → Node
  // Create a copy of this node with only the content between the
  // given positions. If `to` is not given, it defaults to the end of
  // the node.
  cut(from, to) {
    if (from == 0 && to == this.content.size) return this
    return this.copy(this.content.cut(from, to))
  }

  // :: (number, ?number) → Slice
  // Cut out the part of the document between the given positions, and
  // return it as a `Slice` object.
  slice(from, to = this.content.size, includeParents = false) {
    if (from == to) return Slice.empty

    let $from = this.resolve(from), $to = this.resolve(to)
    let depth = includeParents ? 0 : $from.sharedDepth(to)
    let start = $from.start(depth), node = $from.node(depth)
    let content = node.content.cut($from.pos - start, $to.pos - start)
    return new Slice(content, $from.depth - depth, $to.depth - depth)
  }

  // :: (number, number, Slice) → Node
  // Replace the part of the document between the given positions with
  // the given slice. The slice must 'fit', meaning its open sides
  // must be able to connect to the surrounding content, and its
  // content nodes must be valid children for the node they are placed
  // into. If any of this is violated, an error of type
  // [`ReplaceError`](#model.ReplaceError) is thrown.
  replace(from, to, slice) {
    return replace(this.resolve(from), this.resolve(to), slice)
  }

  // :: (number) → ?Node
  // Find the node starting at the given position.
  nodeAt(pos) {
    for (let node = this;;) {
      let {index, offset} = node.content.findIndex(pos)
      node = node.maybeChild(index)
      if (!node) return null
      if (offset == pos || node.isText) return node
      pos -= offset + 1
    }
  }

  // :: (number) → {node: ?Node, index: number, offset: number}
  // Find the (direct) child node after the given offset, if any,
  // and return it along with its index and offset relative to this
  // node.
  childAfter(pos) {
    let {index, offset} = this.content.findIndex(pos)
    return {node: this.content.maybeChild(index), index, offset}
  }

  // :: (number) → {node: ?Node, index: number, offset: number}
  // Find the (direct) child node before the given offset, if any,
  // and return it along with its index and offset relative to this
  // node.
  childBefore(pos) {
    if (pos == 0) return {node: null, index: 0, offset: 0}
    let {index, offset} = this.content.findIndex(pos)
    if (offset < pos) return {node: this.content.child(index), index, offset}
    let node = this.content.child(index - 1)
    return {node, index: index - 1, offset: offset - node.nodeSize}
  }

  // :: (number) → ResolvedPos
  // Resolve the given position in the document, returning an
  // [object](#model.ResolvedPos) with information about its context.
  resolve(pos) { return ResolvedPos.resolveCached(this, pos) }

  resolveNoCache(pos) { return ResolvedPos.resolve(this, pos) }

  // :: (number, number, MarkType) → bool
  // Test whether a mark of the given type occurs in this document
  // between the two given positions.
  rangeHasMark(from, to, type) {
    let found = false
    this.nodesBetween(from, to, node => {
      if (type.isInSet(node.marks)) found = true
      return !found
    })
    return found
  }

  // :: bool
  // True when this is a block (non-inline node)
  get isBlock() { return this.type.isBlock }

  // :: bool
  // True when this is a textblock node, a block node with inline
  // content.
  get isTextblock() { return this.type.isTextblock }

  // :: bool
  // True when this node has inline content.
  get inlineContent() { return this.type.inlineContent }

  // :: bool
  // True when this is an inline node (a text node or a node that can
  // appear among text).
  get isInline() { return this.type.isInline }

  // :: bool
  // True when this is a text node.
  get isText() { return this.type.isText }

  // :: bool
  // True when this is a leaf node.
  get isLeaf() { return this.type.isLeaf }

  // :: bool
  // True when this is an atom, i.e. when it does not have directly
  // editable content. This is usually the same as `isLeaf`, but can
  // be configured with the [`atom` property](#model.NodeSpec.atom) on
  // a node's spec (typically used when the node is displayed as an
  // uneditable [node view](#view.NodeView)).
  get isAtom() { return this.type.isAtom }

  // :: () → string
  // Return a string representation of this node for debugging
  // purposes.
  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toStringInner() + ")"
    return wrapMarks(this.marks, name)
  }

  // :: (number) → ContentMatch
  // Get the content match in this node at the given index.
  contentMatchAt(index) {
    return this.type.contentMatch.matchFragment(this.content, 0, index)
  }

  // :: (number, number, ?Fragment, ?number, ?number) → bool
  // Test whether replacing the range between `from` and `to` (by
  // child index) with the given replacement fragment (which defaults
  // to the empty fragment) would leave the node's content valid. You
  // can optionally pass `start` and `end` indices into the
  // replacement fragment.
  canReplace(from, to, replacement = Fragment.empty, start = 0, end = replacement.childCount) {
    let one = this.contentMatchAt(from).matchFragment(replacement, start, end)
    let two = one && one.matchFragment(this.content, to)
    if (!two || !two.validEnd) return false
    for (let i = start; i < end; i++) if (!this.type.allowsMarks(replacement.child(i).marks)) return false
    return true
  }

  // :: (number, number, NodeType, ?[Mark]) → bool
  // Test whether replacing the range `from` to `to` (by index) with a
  // node of the given type.
  canReplaceWith(from, to, type, marks) {
    if (marks && !this.type.allowsMarks(marks)) return false
    let start = this.contentMatchAt(from).matchType(type)
    let end = start && start.matchFragment(this.content, to)
    return end ? end.validEnd : false
  }

  // :: (Node) → bool
  // Test whether the given node's content could be appended to this
  // node. If that node is empty, this will only return true if there
  // is at least one node type that can appear in both nodes (to avoid
  // merging completely incompatible nodes).
  canAppend(other) {
    if (other.content.size) return this.canReplace(this.childCount, this.childCount, other.content)
    else return this.type.compatibleContent(other.type)
  }

  defaultContentType(at) {
    return this.contentMatchAt(at).defaultType
  }

  // :: ()
  // Check whether this node and its descendants conform to the
  // schema, and raise error when they do not.
  check() {
    if (!this.type.validContent(this.content))
      throw new RangeError(`Invalid content for node ${this.type.name}: ${this.content.toString().slice(0, 50)}`)
    this.content.forEach(node => node.check())
  }

  // :: () → Object
  // Return a JSON-serializeable representation of this node.
  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.content.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks.map(n => n.toJSON())
    return obj
  }

  // :: (Schema, Object) → Node
  // Deserialize a node from its JSON representation.
  static fromJSON(schema, json) {
    let marks = json.marks && json.marks.map(schema.markFromJSON)
    if (json.type == "text") return schema.text(json.text, marks)
    let type = schema.nodeType(json.type)
    if (!type) throw new RangeError(`There is no node type ${json.type} in this schema`)
    return type.create(json.attrs, Fragment.fromJSON(schema, json.content), marks)
  }
}

export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)

    if (!content) throw new RangeError("Empty text nodes are not allowed")

    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  textBetween(from, to) { return this.text.slice(from, to) }

  get nodeSize() { return this.text.length }

  mark(marks) {
    return marks == this.marks ? this : new TextNode(this.type, this.attrs, this.text, marks)
  }

  withText(text) {
    if (text == this.text) return this
    return new TextNode(this.type, this.attrs, text, this.marks)
  }

  cut(from = 0, to = this.text.length) {
    if (from == 0 && to == this.text.length) return this
    return this.withText(this.text.slice(from, to))
  }

  eq(other) {
    return this.sameMarkup(other) && this.text == other.text
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}
<commit_msg>Fix a broken sentence in the docstring for Node.canReplaceWith<commit_after>import {Fragment} from "./fragment"
import {Mark} from "./mark"
import {Slice, replace} from "./replace"
import {ResolvedPos} from "./resolvedpos"
import {compareDeep} from "./comparedeep"

const emptyAttrs = Object.create(null)

// ::- This class represents a node in the tree that makes up a
// ProseMirror document. So a document is an instance of `Node`, with
// children that are also instances of `Node`.
//
// Nodes are persistent data structures. Instead of changing them, you
// create new ones with the content you want. Old ones keep pointing
// at the old document shape. This is made cheaper by sharing
// structure between the old and new data as much as possible, which a
// tree shape like this (without back pointers) makes easy.
//
// **Do not** directly mutate the properties of a `Node` object. See
// [the guide](/docs/guide/#doc) for more information.
export class Node {
  constructor(type, attrs, content, marks) {
    // :: NodeType
    // The type of node that this is.
    this.type = type

    // :: Object
    // An object mapping attribute names to values. The kind of
    // attributes allowed and required are
    // [determined](#model.NodeSpec.attrs) by the node type.
    this.attrs = attrs

    // :: Fragment
    // A container holding the node's children.
    this.content = content || Fragment.empty

    // :: [Mark]
    // The marks (things like whether it is emphasized or part of a
    // link) applied to this node.
    this.marks = marks || Mark.none
  }

  // text:: ?string
  // For text nodes, this contains the node's text content.

  // :: number
  // The size of this node, as defined by the integer-based [indexing
  // scheme](/docs/guide/#doc.indexing). For text nodes, this is the
  // amount of characters. For other leaf nodes, it is one. For
  // non-leaf nodes, it is the size of the content plus two (the start
  // and end token).
  get nodeSize() { return this.isLeaf ? 1 : 2 + this.content.size }

  // :: number
  // The number of children that the node has.
  get childCount() { return this.content.childCount }

  // :: (number) → Node
  // Get the child node at the given index. Raises an error when the
  // index is out of range.
  child(index) { return this.content.child(index) }

  // :: (number) → ?Node
  // Get the child node at the given index, if it exists.
  maybeChild(index) { return this.content.maybeChild(index) }

  // :: ((node: Node, offset: number, index: number))
  // Call `f` for every child node, passing the node, its offset
  // into this parent node, and its index.
  forEach(f) { this.content.forEach(f) }

  // :: (number, number, (node: Node, pos: number, parent: Node, index: number) → ?bool)
  // Invoke a callback for all descendant nodes recursively between
  // the given two positions that are relative to start of this node's
  // content. The callback is invoked with the node, its
  // parent-relative position, its parent node, and its child index.
  // When the callback returns false for a given node, that node's
  // children will not be recursed over.
  nodesBetween(from, to, f, pos = 0) {
    this.content.nodesBetween(from, to, f, pos, this)
  }

  // :: ((node: Node, pos: number, parent: Node) → ?bool)
  // Call the given callback for every descendant node. Doesn't
  // descend into a node when the callback returns `false`.
  descendants(f) {
    this.nodesBetween(0, this.content.size, f)
  }

  // :: string
  // Concatenates all the text nodes found in this fragment and its
  // children.
  get textContent() { return this.textBetween(0, this.content.size, "") }

  // :: (number, number, ?string, ?string) → string
  // Get all text between positions `from` and `to`. When
  // `blockSeparator` is given, it will be inserted whenever a new
  // block node is started. When `leafText` is given, it'll be
  // inserted for every non-text leaf node encountered.
  textBetween(from, to, blockSeparator, leafText) {
    return this.content.textBetween(from, to, blockSeparator, leafText)
  }

  // :: ?Node
  // Returns this node's first child, or `null` if there are no
  // children.
  get firstChild() { return this.content.firstChild }

  // :: ?Node
  // Returns this node's last child, or `null` if there are no
  // children.
  get lastChild() { return this.content.lastChild }

  // :: (Node) → bool
  // Test whether two nodes represent the same piece of document.
  eq(other) {
    return this == other || (this.sameMarkup(other) && this.content.eq(other.content))
  }

  // :: (Node) → bool
  // Compare the markup (type, attributes, and marks) of this node to
  // those of another. Returns `true` if both have the same markup.
  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  // :: (NodeType, ?Object, ?[Mark]) → bool
  // Check whether this node's markup correspond to the given type,
  // attributes, and marks.
  hasMarkup(type, attrs, marks) {
    return this.type == type &&
      compareDeep(this.attrs, attrs || type.defaultAttrs || emptyAttrs) &&
      Mark.sameSet(this.marks, marks || Mark.none)
  }

  // :: (?Fragment) → Node
  // Create a new node with the same markup as this node, containing
  // the given content (or empty, if no content is given).
  copy(content = null) {
    if (content == this.content) return this
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  // :: ([Mark]) → Node
  // Create a copy of this node, with the given set of marks instead
  // of the node's own marks.
  mark(marks) {
    return marks == this.marks ? this : new this.constructor(this.type, this.attrs, this.content, marks)
  }

  // :: (number, ?number) → Node
  // Create a copy of this node with only the content between the
  // given positions. If `to` is not given, it defaults to the end of
  // the node.
  cut(from, to) {
    if (from == 0 && to == this.content.size) return this
    return this.copy(this.content.cut(from, to))
  }

  // :: (number, ?number) → Slice
  // Cut out the part of the document between the given positions, and
  // return it as a `Slice` object.
  slice(from, to = this.content.size, includeParents = false) {
    if (from == to) return Slice.empty

    let $from = this.resolve(from), $to = this.resolve(to)
    let depth = includeParents ? 0 : $from.sharedDepth(to)
    let start = $from.start(depth), node = $from.node(depth)
    let content = node.content.cut($from.pos - start, $to.pos - start)
    return new Slice(content, $from.depth - depth, $to.depth - depth)
  }

  // :: (number, number, Slice) → Node
  // Replace the part of the document between the given positions with
  // the given slice. The slice must 'fit', meaning its open sides
  // must be able to connect to the surrounding content, and its
  // content nodes must be valid children for the node they are placed
  // into. If any of this is violated, an error of type
  // [`ReplaceError`](#model.ReplaceError) is thrown.
  replace(from, to, slice) {
    return replace(this.resolve(from), this.resolve(to), slice)
  }

  // :: (number) → ?Node
  // Find the node starting at the given position.
  nodeAt(pos) {
    for (let node = this;;) {
      let {index, offset} = node.content.findIndex(pos)
      node = node.maybeChild(index)
      if (!node) return null
      if (offset == pos || node.isText) return node
      pos -= offset + 1
    }
  }

  // :: (number) → {node: ?Node, index: number, offset: number}
  // Find the (direct) child node after the given offset, if any,
  // and return it along with its index and offset relative to this
  // node.
  childAfter(pos) {
    let {index, offset} = this.content.findIndex(pos)
    return {node: this.content.maybeChild(index), index, offset}
  }

  // :: (number) → {node: ?Node, index: number, offset: number}
  // Find the (direct) child node before the given offset, if any,
  // and return it along with its index and offset relative to this
  // node.
  childBefore(pos) {
    if (pos == 0) return {node: null, index: 0, offset: 0}
    let {index, offset} = this.content.findIndex(pos)
    if (offset < pos) return {node: this.content.child(index), index, offset}
    let node = this.content.child(index - 1)
    return {node, index: index - 1, offset: offset - node.nodeSize}
  }

  // :: (number) → ResolvedPos
  // Resolve the given position in the document, returning an
  // [object](#model.ResolvedPos) with information about its context.
  resolve(pos) { return ResolvedPos.resolveCached(this, pos) }

  resolveNoCache(pos) { return ResolvedPos.resolve(this, pos) }

  // :: (number, number, MarkType) → bool
  // Test whether a mark of the given type occurs in this document
  // between the two given positions.
  rangeHasMark(from, to, type) {
    let found = false
    this.nodesBetween(from, to, node => {
      if (type.isInSet(node.marks)) found = true
      return !found
    })
    return found
  }

  // :: bool
  // True when this is a block (non-inline node)
  get isBlock() { return this.type.isBlock }

  // :: bool
  // True when this is a textblock node, a block node with inline
  // content.
  get isTextblock() { return this.type.isTextblock }

  // :: bool
  // True when this node has inline content.
  get inlineContent() { return this.type.inlineContent }

  // :: bool
  // True when this is an inline node (a text node or a node that can
  // appear among text).
  get isInline() { return this.type.isInline }

  // :: bool
  // True when this is a text node.
  get isText() { return this.type.isText }

  // :: bool
  // True when this is a leaf node.
  get isLeaf() { return this.type.isLeaf }

  // :: bool
  // True when this is an atom, i.e. when it does not have directly
  // editable content. This is usually the same as `isLeaf`, but can
  // be configured with the [`atom` property](#model.NodeSpec.atom) on
  // a node's spec (typically used when the node is displayed as an
  // uneditable [node view](#view.NodeView)).
  get isAtom() { return this.type.isAtom }

  // :: () → string
  // Return a string representation of this node for debugging
  // purposes.
  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toStringInner() + ")"
    return wrapMarks(this.marks, name)
  }

  // :: (number) → ContentMatch
  // Get the content match in this node at the given index.
  contentMatchAt(index) {
    return this.type.contentMatch.matchFragment(this.content, 0, index)
  }

  // :: (number, number, ?Fragment, ?number, ?number) → bool
  // Test whether replacing the range between `from` and `to` (by
  // child index) with the given replacement fragment (which defaults
  // to the empty fragment) would leave the node's content valid. You
  // can optionally pass `start` and `end` indices into the
  // replacement fragment.
  canReplace(from, to, replacement = Fragment.empty, start = 0, end = replacement.childCount) {
    let one = this.contentMatchAt(from).matchFragment(replacement, start, end)
    let two = one && one.matchFragment(this.content, to)
    if (!two || !two.validEnd) return false
    for (let i = start; i < end; i++) if (!this.type.allowsMarks(replacement.child(i).marks)) return false
    return true
  }

  // :: (number, number, NodeType, ?[Mark]) → bool
  // Test whether replacing the range `from` to `to` (by index) with a
  // node of the given type would leave the node's content valid.
  canReplaceWith(from, to, type, marks) {
    if (marks && !this.type.allowsMarks(marks)) return false
    let start = this.contentMatchAt(from).matchType(type)
    let end = start && start.matchFragment(this.content, to)
    return end ? end.validEnd : false
  }

  // :: (Node) → bool
  // Test whether the given node's content could be appended to this
  // node. If that node is empty, this will only return true if there
  // is at least one node type that can appear in both nodes (to avoid
  // merging completely incompatible nodes).
  canAppend(other) {
    if (other.content.size) return this.canReplace(this.childCount, this.childCount, other.content)
    else return this.type.compatibleContent(other.type)
  }

  defaultContentType(at) {
    return this.contentMatchAt(at).defaultType
  }

  // :: ()
  // Check whether this node and its descendants conform to the
  // schema, and raise error when they do not.
  check() {
    if (!this.type.validContent(this.content))
      throw new RangeError(`Invalid content for node ${this.type.name}: ${this.content.toString().slice(0, 50)}`)
    this.content.forEach(node => node.check())
  }

  // :: () → Object
  // Return a JSON-serializeable representation of this node.
  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.content.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks.map(n => n.toJSON())
    return obj
  }

  // :: (Schema, Object) → Node
  // Deserialize a node from its JSON representation.
  static fromJSON(schema, json) {
    let marks = json.marks && json.marks.map(schema.markFromJSON)
    if (json.type == "text") return schema.text(json.text, marks)
    let type = schema.nodeType(json.type)
    if (!type) throw new RangeError(`There is no node type ${json.type} in this schema`)
    return type.create(json.attrs, Fragment.fromJSON(schema, json.content), marks)
  }
}

export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)

    if (!content) throw new RangeError("Empty text nodes are not allowed")

    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  textBetween(from, to) { return this.text.slice(from, to) }

  get nodeSize() { return this.text.length }

  mark(marks) {
    return marks == this.marks ? this : new TextNode(this.type, this.attrs, this.text, marks)
  }

  withText(text) {
    if (text == this.text) return this
    return new TextNode(this.type, this.attrs, text, this.marks)
  }

  cut(from = 0, to = this.text.length) {
    if (from == 0 && to == this.text.length) return this
    return this.withText(this.text.slice(from, to))
  }

  eq(other) {
    return this.sameMarkup(other) && this.text == other.text
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var tty = require('tty');
var util = require('util');

/**
 * This is the Node.js implementation of `debug()`.
 *
 * Expose `debug()` as the module.
 */

exports = module.exports = require('./debug');
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;

/**
 * Colors.
 */

exports.colors = [ 6, 2, 3, 4, 5, 1 ];

try {
  var supportsColor = require('supports-color');
  if (supportsColor && supportsColor.level >= 2) {
    exports.colors = [
      20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68,
      69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134,
      135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
      172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204,
      205, 206, 207, 208, 209, 214, 215, 220, 221
    ];
  }
} catch (err) {
  // swallow - we only care if `supports-color` is available; it doesn't have to be.
}

/**
 * Build up the default `inspectOpts` object from the environment variables.
 *
 *   $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
 */

exports.inspectOpts = Object.keys(process.env).filter(function (key) {
  return /^debug_/i.test(key);
}).reduce(function (obj, key) {
  // camel-case
  var prop = key
    .substring(6)
    .toLowerCase()
    .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });

  // coerce string value into JS value
  var val = process.env[key];
  if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
  else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
  else if (val === 'null') val = null;
  else val = Number(val);

  obj[prop] = val;
  return obj;
}, {});

/**
 * Is stdout a TTY? Colored output is enabled when `true`.
 */

function useColors() {
  return 'colors' in exports.inspectOpts
    ? Boolean(exports.inspectOpts.colors)
    : tty.isatty(process.stderr.fd);
}

/**
 * Map %o to `util.inspect()`, all on a single line.
 */

exports.formatters.o = function(v) {
  this.inspectOpts.colors = this.useColors;
  return util.inspect(v, this.inspectOpts)
    .replace(/\s*\n\s*/g, ' ');
};

/**
 * Map %o to `util.inspect()`, allowing multiple lines if needed.
 */

exports.formatters.O = function(v) {
  this.inspectOpts.colors = this.useColors;
  return util.inspect(v, this.inspectOpts);
};

/**
 * Adds ANSI color escape codes if enabled.
 *
 * @api public
 */

function formatArgs(args) {
  var name = this.namespace;
  var useColors = this.useColors;

  if (useColors) {
    var c = this.color;
    var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c);
    var prefix = '  ' + colorCode + ';1m' + name + ' ' + '\u001b[0m';

    args[0] = prefix + args[0].split('\n').join('\n' + prefix);
    args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
  } else {
    args[0] = new Date().toISOString()
      + ' ' + name + ' ' + args[0];
  }
}

/**
 * Invokes `util.format()` with the specified arguments and writes to stderr.
 */

function log() {
  return process.stderr.write(util.format.apply(util, arguments) + '\n');
}

/**
 * Save `namespaces`.
 *
 * @param {String} namespaces
 * @api private
 */

function save(namespaces) {
  if (null == namespaces) {
    // If you set a process.env field to null or undefined, it gets cast to the
    // string 'null' or 'undefined'. Just delete instead.
    delete process.env.DEBUG;
  } else {
    process.env.DEBUG = namespaces;
  }
}

/**
 * Load `namespaces`.
 *
 * @return {String} returns the previously persisted debug modes
 * @api private
 */

function load() {
  return process.env.DEBUG;
}

/**
 * Init logic for `debug` instances.
 *
 * Create a new `inspectOpts` object in case `useColors` is set
 * differently for a particular `debug` instance.
 */

function init (debug) {
  debug.inspectOpts = {};

  var keys = Object.keys(exports.inspectOpts);
  for (var i = 0; i < keys.length; i++) {
    debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
  }
}

/**
 * Enable namespaces listed in `process.env.DEBUG` initially.
 */

exports.enable(load());
<commit_msg>remove ReDoS regexp in %o formatter (#504)<commit_after>/**
 * Module dependencies.
 */

var tty = require('tty');
var util = require('util');

/**
 * This is the Node.js implementation of `debug()`.
 *
 * Expose `debug()` as the module.
 */

exports = module.exports = require('./debug');
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;

/**
 * Colors.
 */

exports.colors = [ 6, 2, 3, 4, 5, 1 ];

try {
  var supportsColor = require('supports-color');
  if (supportsColor && supportsColor.level >= 2) {
    exports.colors = [
      20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68,
      69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134,
      135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
      172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204,
      205, 206, 207, 208, 209, 214, 215, 220, 221
    ];
  }
} catch (err) {
  // swallow - we only care if `supports-color` is available; it doesn't have to be.
}

/**
 * Build up the default `inspectOpts` object from the environment variables.
 *
 *   $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
 */

exports.inspectOpts = Object.keys(process.env).filter(function (key) {
  return /^debug_/i.test(key);
}).reduce(function (obj, key) {
  // camel-case
  var prop = key
    .substring(6)
    .toLowerCase()
    .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });

  // coerce string value into JS value
  var val = process.env[key];
  if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
  else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
  else if (val === 'null') val = null;
  else val = Number(val);

  obj[prop] = val;
  return obj;
}, {});

/**
 * Is stdout a TTY? Colored output is enabled when `true`.
 */

function useColors() {
  return 'colors' in exports.inspectOpts
    ? Boolean(exports.inspectOpts.colors)
    : tty.isatty(process.stderr.fd);
}

/**
 * Map %o to `util.inspect()`, all on a single line.
 */

exports.formatters.o = function(v) {
  this.inspectOpts.colors = this.useColors;
  return util.inspect(v, this.inspectOpts)
    .split('\n').map(function(str) {
      return str.trim()
    }).join(' ');
};

/**
 * Map %o to `util.inspect()`, allowing multiple lines if needed.
 */

exports.formatters.O = function(v) {
  this.inspectOpts.colors = this.useColors;
  return util.inspect(v, this.inspectOpts);
};

/**
 * Adds ANSI color escape codes if enabled.
 *
 * @api public
 */

function formatArgs(args) {
  var name = this.namespace;
  var useColors = this.useColors;

  if (useColors) {
    var c = this.color;
    var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c);
    var prefix = '  ' + colorCode + ';1m' + name + ' ' + '\u001b[0m';

    args[0] = prefix + args[0].split('\n').join('\n' + prefix);
    args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
  } else {
    args[0] = new Date().toISOString()
      + ' ' + name + ' ' + args[0];
  }
}

/**
 * Invokes `util.format()` with the specified arguments and writes to stderr.
 */

function log() {
  return process.stderr.write(util.format.apply(util, arguments) + '\n');
}

/**
 * Save `namespaces`.
 *
 * @param {String} namespaces
 * @api private
 */

function save(namespaces) {
  if (null == namespaces) {
    // If you set a process.env field to null or undefined, it gets cast to the
    // string 'null' or 'undefined'. Just delete instead.
    delete process.env.DEBUG;
  } else {
    process.env.DEBUG = namespaces;
  }
}

/**
 * Load `namespaces`.
 *
 * @return {String} returns the previously persisted debug modes
 * @api private
 */

function load() {
  return process.env.DEBUG;
}

/**
 * Init logic for `debug` instances.
 *
 * Create a new `inspectOpts` object in case `useColors` is set
 * differently for a particular `debug` instance.
 */

function init (debug) {
  debug.inspectOpts = {};

  var keys = Object.keys(exports.inspectOpts);
  for (var i = 0; i < keys.length; i++) {
    debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
  }
}

/**
 * Enable namespaces listed in `process.env.DEBUG` initially.
 */

exports.enable(load());
<|endoftext|>


<commit_before>import React from 'react';
import styles from './petition-header.scss';
import Header from 'components/Header';
import PageTitle from 'components/PageTitle';
import PetitionInfo from 'components/PetitionInfo';
import PetitionStats from 'components/PetitionStats';
import ProgressBar from 'components/ProgressBar';

const PetitionHeader = ({ title, info, metrics }) => (

      <Heading1 text={title} />

      <PetitionInfo {...info} />
      {
        metrics.supportersMetric &&
        metrics.supportersMetric.votingActive &&

            <ProgressBar
              animated
              percentage={metrics.supportersMetric.percentage}
              aria={metrics.supportersMetric.aria}
            />
            <PetitionStats {...metrics.supportersMetric} />

      }

);

export default PetitionHeader;
<commit_msg>Fix merge<commit_after>import React from 'react';
import styles from './petition-header.scss';
import Header from 'components/Header';
import PageTitle from 'components/PageTitle';
import PetitionInfo from 'components/PetitionInfo';
import PetitionStats from 'components/PetitionStats';
import ProgressBar from 'components/ProgressBar';

const PetitionHeader = ({ title, info, metrics }) => (

    <PageTitle title={title} />

      <PetitionInfo {...info} />
      {
        metrics.supportersMetric &&
        metrics.supportersMetric.votingActive &&

            <ProgressBar
              animated
              percentage={metrics.supportersMetric.percentage}
              aria={metrics.supportersMetric.aria}
            />
            <PetitionStats {...metrics.supportersMetric} />

      }

);

export default PetitionHeader;
<|endoftext|>