<commit_before>/*
 * Helper functions
 */

function reachToRadius(reach) {
  var level = IncidencyReachs.findOne(reach).level;
  return 3 * Math.sqrt(level / Math.PI);
}

function setTooltip(title, val, color){
  if(title){
    $(tooltip_title).html(title);
    $(tooltip_value).html(val);
    $(tooltip).css({background:color}).show();
  }else{
    $(tooltip).hide();
  }
}

var delta_drag = 3;
var drag1 = [];
var drag2 = [];
var win_w;
var win_h;
var bar_h = 80;
var filter_h = 25;

function dragging() {
	return (
		( drag1[0] >= drag2[0] + delta_drag || drag1[0] <= drag2[0] - delta_drag  ) &&
		( drag1[1] >= drag2[1] + delta_drag || drag1[1] <= drag2[1] - delta_drag  )
	);
}

function resize_explore(){

	// $(popup_content).height($(popup).height() - 150);
	$(map_container).height(win_h - bar_h);
	$(filters).height(win_h - bar_h - 40);
	$(filters_list).height(win_h - bar_h - 97);

}

/*
 * Variables & Configs
 */

var scale = 5;
var zoom_factor = 1.5;
var zoom_limits = [1,30];
var dur = 350;
var dur2 = 550;
var svg_map_area;
var svg_map;
var data = {};
var nodes;
var pack = d3.layout.pack()
  .value(function(d) { return d.size; })
  .sort(function(a,b){ return a.value + b.value; })
  .padding(.3)
  .size([100, 100]);

/*
 * Data
 */

function getSignalData() {
  data.children = Themes.find({}).map(function(theme){
    var group = {
      id: theme._id,
      color: theme.color,
      node: theme,
      label: theme.pt,
      name: theme.pt,
      children: []
    }

    group.children = Signals.find({mainThemes: theme._id}).map(function(signal){
      return {
        id: signal._id,
        color: theme.color,
        group: group.name,
        node: signal,
        name: signal.name,
        label: signal.name,
        size: reachToRadius(signal.incidencyReach)
      }
    });

    return group;
  });

  nodes = pack.nodes(data);

}

function getHubData() {

  data = {};

  data.children = Natures.find({}).map(function(nature){
    var group = {
      id: nature._id,
      color: nature.color,
      node: nature,
      label: nature.pt,
      name: nature.pt,
      children: []
    }

    group.children = Hubs.find({nature: nature._id}).map(function(hub){
      return {
        id: hub._id,
        color: nature.color,
        group: group.name,
        node: hub,
        name: hub.name,
        label: hub.name,
        size: reachToRadius(hub.incidencyReach)
      }
    });

    return group;
  });

  nodes = pack.nodes(data);

}

/*
 * Map
 */

function refreshMap(template, filters) {

  if (svg_map) {
    svg_map.selectAll('circle').remove();

    if (filters) {

      var selectedFilters = {};
      var fields = _.keys(filters);

      // get filters ids into arrays
      _.each(fields, function(field){
        var fieldValues = [];
        _.each(filters[field], function(f){
          if (f.selected) fieldValues.push(f._id);
        })
        if (fieldValues.length > 0) {
          selectedFilters[field] = fieldValues;
        }
      });

      // get methods after mechanisms
      if (selectedFilters['mechanisms']) {
        selectedFilters['methods'] = Methods
          .find({mechanism: {$in: selectedFilters['mechanisms']}}, {fields: {_id: true}})
          .map(function(i) {return i._id} );

        delete selectedFilters.mechanisms;
      }

      var isSponsor = _.map(_.where(filters['isSponsor'], {selected: true}), function(i){return i._id});
      if (isSponsor.length > 0) {
        selectedFilters['isSponsor'] = isSponsor;
      }

      fields = _.keys(selectedFilters);

    }

    svg_map.selectAll('circle')
  		.data(nodes)
  		.enter()
  			.append('circle')
  			.attr('cx', function(d) { return d.x })
  			.attr('cy', function(d) { return d.y })
  			.attr('transform','translate(-50 -50)')
  			.attr('r', 0 )
  			.attr('stroke-width', '0')
  			.attr('opacity', function(d){
  				if(d.depth == 1) return 0.1;
  				if(d.depth == 2) return 1;
  			})
  			.attr('id', function (d){
  				return 'c' + d.id;
  			})
  			.attr('fill', function (d){
  				if (d.name) return d.color;
  				else return 'none';
  			})
  			.each(function (d, i) {
  				c = d3.select(this);
  				c.node = d.node;
  				c.d = d;
  				d.circle = c;

          if (d.depth == 2) {
            if (filters) {

              var selected = true;

              // check if node belongs to all filters
              _.each(fields, function(field){

                if (selected && !_.intersection(selectedFilters[field], [].concat( d.node[field] )).length) {
                  selected = false
                }
              });

              if (selected) {
                d.circle
                  .attr('opacity', 1)
              } else {
                d.circle
                  .attr('opacity', .1)
              }
            }
          }
  			})
  			.style('cursor', 'pointer')
  			.on('mouseover', function(d){
  				if(d.depth == 2){
  					setTooltip(d.group, d.node.name, d.color);
  				} else if(d.depth == 1) {
  					setTooltip(d.label, '', d.color);
  				}
  			})
  			.on('mouseout', function(d){
  				setTooltip(false);
  			})
  			.on('click', function(d){
          if (!dragging()) {
            if (d.depth == 2) {
              Session.set('popupContent', JSON.stringify(d.node));
              Session.set('showPopup', true);
            }
          }
  			})
  		 	// .transition().duration(1000)
  			.attr('r', function(d) { return d.r } )

  }

}

function applyFilters(filters) {

  if (!filters) return;
  console.log('applyFilters');
  console.log(filters);

  var selectedFilters = {};
  var fields = _.keys(filters);

  // get filters ids into arrays
  _.each(fields, function(field){
    var fieldValues = [];
    _.each(filters[field], function(f){
      if (f.selected) fieldValues.push(f._id);
    })
    if (fieldValues.length > 0) {
      selectedFilters[field] = fieldValues;
    }
  });

  // get methods after mechanisms
  if (selectedFilters['mechanisms']) {
    selectedFilters['methods'] = Methods
      .find({mechanism: {$in: selectedFilters['mechanisms']}}, {fields: {_id: true}})
      .map(function(i) {return i._id} );

    delete selectedFilters.mechanisms;
  }

  fields = _.keys(selectedFilters);

  console.log('selectedFilters');
  console.log(selectedFilters);

  // if map has been drawn
  if (svg_map) {
    svg_map.selectAll('circle')
      .each( function (d, i) {
        if (d.depth == 2) {
          var selected = true;

          // check if node belongs to all filters
          _.each(fields, function(field){
            if (selected && !_.intersection(selectedFilters[field], [].concat( d.node[field] )).length) {
              selected = false
            }
          });

          if (selected) {
            d.circle
              .transition()
              .duration(dur/2)
              .attr('opacity', 1)
          } else {
            d.circle
              .transition()
              .duration(dur/2)
              .attr('opacity', .1)
          }
        }
      });
  }
}

Template.map.onRendered(function(){
  svg_map_area = d3.select('#map')
    .append('svg')
    .attr('id', 'svg_map_area');

  svg_map = svg_map_area
    .append('g')

  getSignalData();
  refreshMap(self);

  win_w = $(window).width();
  win_h = $(window).height();

  /*
   * Tool tip
   */

  $(window).mousemove(function( event ){
    mouse_x = event.clientX - $(tooltip).width()/2 - 30;
    mouse_y = event.clientY - 60;
    $(tooltip).css({ left:mouse_x, top:mouse_y });
  });

  /*
   * Zoom & Pan
   */

  var zoom = d3.behavior.zoom()
  		.scaleExtent(zoom_limits)
  		.center([win_w/2,win_h/2])
  		.on("zoom", function(){
        svg_map.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
      	scale = d3.event.scale;
      })
  		.translate([win_w/2,win_h/2])
  		.scale(scale);

  function center_group(c) {

  	var target = map_data[c];

  	scale = win_w/target.r/5;
  	if(scale > zoom_limits[1]) scale = zoom_limits[1];

  	var center_x = win_w/2 - ((target.x-50)*scale );
  	var center_y = win_h/2 - ((target.y-50)*scale );

  	zoom.translate([ center_x, center_y ]).scale(scale).event(svg_map.transition().duration(dur2));
  }

  function zoomed() {
  	svg_map.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
  	scale = d3.event.scale;
  }

  svg_map.on( "mousedown", function(){
  	drag1 = zoom.translate();
  });
  svg_map.on( "mouseup", function(){
  	drag2 = zoom.translate();
  });

	svg_map_area.on("mousedown.zoom", null);
	svg_map_area.on("mousemove.zoom", null);
	svg_map_area.on("dblclick.zoom", null);
	svg_map_area.on("touchstart.zoom", null);
	svg_map_area.on("wheel.zoom", null);
	svg_map_area.on("mousewheel.zoom", null);
	svg_map_area.on("MozMousePixelScroll.zoom", null);

	$(zoom_in).on('click', function(){
		if(scale < zoom_limits[1]){
			scale *= zoom_factor;
			zoom.scale(scale).event(svg_map.transition().duration(dur));
		}
	});

	$(zoom_ext).on('click', function(){
		scale = initial_scale;
		zoom.translate([win_w/2,win_h/2]).scale(scale).event(svg_map.transition().duration(dur2));
	});

	$(zoom_out).on('click', function(){
		if(scale > zoom_limits[0]){
			scale = scale/zoom_factor;
			zoom.scale(scale).event(svg_map.transition().duration(dur));
		}
	});

  zoom.translate([win_w/2,win_h/2]).scale(scale).event(svg_map);
  svg_map.attr("transform","translate( "+win_w/2+", "+win_h/2+" ) scale(" + scale + ")");

  svg_map_area.call(zoom);

  resize_explore();
});

Template.map.helpers({
  mapUpdateTrigger: function(){

    // load config
    var context = Session.get('currentContext');
    var filters = JSON.parse(Session.get('filters'));
    filters = _.extend(filters['general'], filters[context]);

    // update data
    if (context == 'signals') getSignalData();
    else getHubData();

    // update map
    refreshMap(Template.instance(), filters);

    // trigger update
    return new Date();
  }
});
<commit_msg>filter is carried between contexts<commit_after>/*
 * Helper functions
 */

function reachToRadius(reach) {
  var level = IncidencyReachs.findOne(reach).level;
  return 3 * Math.sqrt(level / Math.PI);
}

function setTooltip(title, val, color){
  if(title){
    $(tooltip_title).html(title);
    $(tooltip_value).html(val);
    $(tooltip).css({background:color}).show();
  }else{
    $(tooltip).hide();
  }
}

var delta_drag = 3;
var drag1 = [];
var drag2 = [];
var win_w;
var win_h;
var bar_h = 80;
var filter_h = 25;

function dragging() {
	return (
		( drag1[0] >= drag2[0] + delta_drag || drag1[0] <= drag2[0] - delta_drag  ) &&
		( drag1[1] >= drag2[1] + delta_drag || drag1[1] <= drag2[1] - delta_drag  )
	);
}

function resize_explore(){

	// $(popup_content).height($(popup).height() - 150);
	$(map_container).height(win_h - bar_h);
	$(filters).height(win_h - bar_h - 40);
	$(filters_list).height(win_h - bar_h - 97);

}

/*
 * Variables & Configs
 */

var scale = 5;
var zoom_factor = 1.5;
var zoom_limits = [1,30];
var dur = 350;
var dur2 = 550;
var svg_map_area;
var svg_map;
var data = {};
var nodes;
var pack = d3.layout.pack()
  .value(function(d) { return d.size; })
  .sort(function(a,b){ return a.value + b.value; })
  .padding(.3)
  .size([100, 100]);

/*
 * Data
 */

function getSignalData() {
  data.children = Themes.find({}).map(function(theme){
    var group = {
      id: theme._id,
      color: theme.color,
      node: theme,
      label: theme.pt,
      name: theme.pt,
      children: []
    }

    group.children = Signals.find({mainThemes: theme._id}).map(function(signal){
      return {
        id: signal._id,
        color: theme.color,
        group: group.name,
        node: signal,
        name: signal.name,
        label: signal.name,
        size: reachToRadius(signal.incidencyReach)
      }
    });

    return group;
  });

  nodes = pack.nodes(data);

}

function getHubData() {

  data = {};

  data.children = Natures.find({}).map(function(nature){
    var group = {
      id: nature._id,
      color: nature.color,
      node: nature,
      label: nature.pt,
      name: nature.pt,
      children: []
    }

    group.children = Hubs.find({nature: nature._id}).map(function(hub){
      return {
        id: hub._id,
        color: nature.color,
        group: group.name,
        node: hub,
        name: hub.name,
        label: hub.name,
        size: reachToRadius(hub.incidencyReach)
      }
    });

    return group;
  });

  nodes = pack.nodes(data);

}

/*
 * Map
 */

function refreshMap(template, filters) {

  if (svg_map) {
    svg_map.selectAll('circle').remove();

    if (filters) {

      var selectedFilters = {};
      var fields = _.keys(filters);

      // get filters ids into arrays
      _.each(fields, function(field){
        var fieldValues = [];
        _.each(filters[field], function(f){
          if (f.selected) fieldValues.push(f._id);
        })
        if (fieldValues.length > 0) {
          selectedFilters[field] = fieldValues;
        }
      });

      // get methods after mechanisms
      if (selectedFilters['mechanisms']) {
        selectedFilters['methods'] = Methods
          .find({mechanism: {$in: selectedFilters['mechanisms']}}, {fields: {_id: true}})
          .map(function(i) {return i._id} );

        delete selectedFilters.mechanisms;
      }

      var isSponsor = _.map(_.where(filters['isSponsor'], {selected: true}), function(i){return i._id});
      if (isSponsor.length > 0) {
        selectedFilters['isSponsor'] = isSponsor;
      }

      fields = _.keys(selectedFilters);

    }

    svg_map.selectAll('circle')
  		.data(nodes)
  		.enter()
  			.append('circle')
  			.attr('cx', function(d) { return d.x })
  			.attr('cy', function(d) { return d.y })
  			.attr('transform','translate(-50 -50)')
  			.attr('r', 0 )
  			.attr('stroke-width', '0')
  			.attr('opacity', function(d){
  				if(d.depth == 1) return 0.1;
  				if(d.depth == 2) return 1;
  			})
  			.attr('id', function (d){
  				return 'c' + d.id;
  			})
  			.attr('fill', function (d){
  				if (d.name) return d.color;
  				else return 'none';
  			})
  			.each(function (d, i) {
  				c = d3.select(this);
  				c.node = d.node;
  				c.d = d;
  				d.circle = c;

          if (d.depth == 2) {
            if (filters) {

              var selected = true;

              // check if node belongs to all filters
              _.each(fields, function(field){

                if (selected && !_.intersection(selectedFilters[field], [].concat( d.node[field] )).length) {
                  selected = false
                }
              });

              if (selected) {
                d.circle
                  .attr('opacity', 1)
              } else {
                d.circle
                  .attr('opacity', .1)
              }
            }
          }
  			})
  			.style('cursor', 'pointer')
  			.on('mouseover', function(d){
  				if(d.depth == 2){
  					setTooltip(d.group, d.node.name, d.color);
  				} else if(d.depth == 1) {
  					setTooltip(d.label, '', d.color);
  				}
  			})
  			.on('mouseout', function(d){
  				setTooltip(false);
  			})
  			.on('click', function(d){
          if (!dragging()) {
            if (d.depth == 2) {
              Session.set('popupContent', JSON.stringify(d.node));
              Session.set('showPopup', true);
            }
          }
  			})
  		 	// .transition().duration(1000)
  			.attr('r', function(d) { return d.r } )

  }

}

function applyFilters(filters) {

  if (!filters) return;
  console.log('applyFilters');
  console.log(filters);

  var selectedFilters = {};
  var fields = _.keys(filters);

  // get filters ids into arrays
  _.each(fields, function(field){
    var fieldValues = [];
    _.each(filters[field], function(f){
      if (f.selected) fieldValues.push(f._id);
    })
    if (fieldValues.length > 0) {
      selectedFilters[field] = fieldValues;
    }
  });

  // get methods after mechanisms
  if (selectedFilters['mechanisms']) {
    selectedFilters['methods'] = Methods
      .find({mechanism: {$in: selectedFilters['mechanisms']}}, {fields: {_id: true}})
      .map(function(i) {return i._id} );

    delete selectedFilters.mechanisms;
  }

  fields = _.keys(selectedFilters);

  console.log('selectedFilters');
  console.log(selectedFilters);

  // if map has been drawn
  if (svg_map) {
    svg_map.selectAll('circle')
      .each( function (d, i) {
        if (d.depth == 2) {
          var selected = true;

          // check if node belongs to all filters
          _.each(fields, function(field){
            if (selected && !_.intersection(selectedFilters[field], [].concat( d.node[field] )).length) {
              selected = false
            }
          });

          if (selected) {
            d.circle
              .transition()
              .duration(dur/2)
              .attr('opacity', 1)
          } else {
            d.circle
              .transition()
              .duration(dur/2)
              .attr('opacity', .1)
          }
        }
      });
  }
}

Template.map.onRendered(function(){
  svg_map_area = d3.select('#map')
    .append('svg')
    .attr('id', 'svg_map_area');

  svg_map = svg_map_area
    .append('g')

  // load config
  var context = Session.get('currentContext');
  var filters = JSON.parse(Session.get('filters'));
  filters = _.extend(filters['general'], filters[context]);

  // update data
  if (context == 'signals') getSignalData();
  else getHubData();

  refreshMap(self, filters);

  win_w = $(window).width();
  win_h = $(window).height();

  /*
   * Tool tip
   */

  $(window).mousemove(function( event ){
    mouse_x = event.clientX - $(tooltip).width()/2 - 30;
    mouse_y = event.clientY - 60;
    $(tooltip).css({ left:mouse_x, top:mouse_y });
  });

  /*
   * Zoom & Pan
   */

  var zoom = d3.behavior.zoom()
  		.scaleExtent(zoom_limits)
  		.center([win_w/2,win_h/2])
  		.on("zoom", function(){
        svg_map.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
      	scale = d3.event.scale;
      })
  		.translate([win_w/2,win_h/2])
  		.scale(scale);

  function center_group(c) {

  	var target = map_data[c];

  	scale = win_w/target.r/5;
  	if(scale > zoom_limits[1]) scale = zoom_limits[1];

  	var center_x = win_w/2 - ((target.x-50)*scale );
  	var center_y = win_h/2 - ((target.y-50)*scale );

  	zoom.translate([ center_x, center_y ]).scale(scale).event(svg_map.transition().duration(dur2));
  }

  function zoomed() {
  	svg_map.attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
  	scale = d3.event.scale;
  }

  svg_map.on( "mousedown", function(){
  	drag1 = zoom.translate();
  });
  svg_map.on( "mouseup", function(){
  	drag2 = zoom.translate();
  });

	svg_map_area.on("mousedown.zoom", null);
	svg_map_area.on("mousemove.zoom", null);
	svg_map_area.on("dblclick.zoom", null);
	svg_map_area.on("touchstart.zoom", null);
	svg_map_area.on("wheel.zoom", null);
	svg_map_area.on("mousewheel.zoom", null);
	svg_map_area.on("MozMousePixelScroll.zoom", null);

	$(zoom_in).on('click', function(){
		if(scale < zoom_limits[1]){
			scale *= zoom_factor;
			zoom.scale(scale).event(svg_map.transition().duration(dur));
		}
	});

	$(zoom_ext).on('click', function(){
		scale = initial_scale;
		zoom.translate([win_w/2,win_h/2]).scale(scale).event(svg_map.transition().duration(dur2));
	});

	$(zoom_out).on('click', function(){
		if(scale > zoom_limits[0]){
			scale = scale/zoom_factor;
			zoom.scale(scale).event(svg_map.transition().duration(dur));
		}
	});

  zoom.translate([win_w/2,win_h/2]).scale(scale).event(svg_map);
  svg_map.attr("transform","translate( "+win_w/2+", "+win_h/2+" ) scale(" + scale + ")");

  svg_map_area.call(zoom);

  resize_explore();
});

Template.map.helpers({
  mapUpdateTrigger: function(){

    // load config
    var context = Session.get('currentContext');
    var filters = JSON.parse(Session.get('filters'));
    filters = _.extend(filters['general'], filters[context]);

    // update data
    if (context == 'signals') getSignalData();
    else getHubData();

    // update map
    refreshMap(Template.instance(), filters);

    // trigger update
    return new Date();
  }
});
<|endoftext|>


<commit_before>// code modified from flot AJAX example
// http://www.flotcharts.org/flot/examples/ajax/index.html

	$(function() {

		// flot plot options!
		var options = {
			lines: {
				show: true
			},
			points: {
				show: false
			},
			legend: {
				show: true
			},
			grid: {
        		hoverable: true
      		}
	};

		// GLOBAL VARIABLES AND FUNCTIONS

		console.log('Initializing variables.');
		// initialize the arrays hold the data for plotting
		var data = [];
		var dataTarget = [];

		// IP address to access our UI
		// (empty for relative URLs)
		var publicIP = '';

		// building blocks of metadata table
		var tableTop = "
";
		var tableBottom = "
";
		var tableHeader = "
ID	Mean	StDev	Level	Blarg
";
		var metadataTable = tableTop + tableHeader;

		// helps with managing the uploaded time series
		var files;

		// initialize the timeseries plot (will be empty)
		console.log('Initializing flot plot (empty plot).');
		$.plot("#placeholder", data, options);

		// ACTIONS TO COMPLETE WHEN THE `SUBMIT` BUTTON IS CLICKED
		$("button#tsQuery").click(function () {

			console.log('Submit button has been clicked.');

			// show the progress bar so the user knows that stuff
			// is going on behind the scenes

			console.log('Adding in ticking progress bar.');
			$( "#progressbar" ).progressbar({
  				value: false
			});

			// reset everything in preparation for new data
			console.log('Resetting helper variables in preparation for new data.');
			document.getElementById('timeseriesMetadata').innerHTML = '';
			data = [];
			var simIDs = [];
			var idNum = {};
			var counter = 0;
			metadataTable = tableTop + tableHeader;

			$.plot("#placeholder", data, options);

			// retrieve the time series specified by the user, and the number of
			//   most similar time series
			var timeSeriesID = document.getElementById("tsID").value;
			var numSim = document.getElementById("n_closest").value;
			console.log('Retrieved timeseries ID #' + timeSeriesID + 'and will retrieve the ' + numSim + 'most similar timeseries.');

			// CONSTRUCT OUR QUERY
			// if the user has specified an ID, use that;
			//   otherwise assume there's an uploaded file.
			if (timeSeriesID != '') {

				// DECIDE WHETHER WE NEED A SIMQUERY
				// if the user hasn't provided a number of similar TS, or the
				//   number is zero, we won't bother sending off a simquery
				if (numSim == '' | numSim == 0) {

					// add on the ID number of the target TS
					simIDs.push(timeSeriesID);

					// now we loop over all the IDs and collect their data
					console.log("Collecting data of specified time series.");

					var dataurl = publicIP + "/timeseries/" + timeSeriesID;

					console.log('Retrieving data associated with TS ID#'+ timeSeriesID + '.');

					$.ajax({
						url: dataurl,
						type: "GET",
						dataType: "json",
						success: onDataReceived
					});

				} else {

					var simurl = publicIP + "/simquery?id=" + timeSeriesID + "&topn=" + numSim;
					console.log('Constructed simquery:', simurl);

					// get the IDs of the similar time series
					console.log('Retrieving the ID numbers of the similar TS (ID number supplied).');
					$.ajax({
						url: simurl,
						type: "GET",
						dataType: "json",
						success: function(data) {

									// parse the ID numbers returned from the server
									getSimIDs(data);

									// add on the ID number of the target TS
									simIDs.push(timeSeriesID);

									// now we loop over all the IDs and collect their data
									console.log("Looping over the retrieved ID numbers and collecting their data.");

									for(var i=0; i < simIDs.length; i++) {

										idNum   = simIDs[i];

										var dataurl = publicIP + "/timeseries/" + idNum;

										console.log('Retrieving data associated with TS ID#'+ idNum + '.');
										// solution from http://stackoverflow.com/questions/4201934/
										//               jquery-ajax-pass-additional-argument-to-success-callback
										$.ajax({
											url: dataurl,
											type: "GET",
											dataType: "json",
											success: onDataReceived
										});

									}
						 }
					});
				}

			} else if ( document.getElementById("fileSelect").files.length > 0  ) {

				// DECIDE WHETHER WE NEED A SIMQUERY
				// if the user hasn't provided a number of similar TS, or the
				//   number is zero, we won't bother sending off a simquery
				if (numSim == '' | numSim == 0) {

					console.log("Locally received data uploaded by user.");
					console.log( document.getElementById("fileSelect").files[0] );

					// parse the uploaded file
					// https://www.html5rocks.com/en/tutorials/file/dndfiles/
					read = new FileReader();
					read.readAsText(document.getElementById("fileSelect").files[0]);

					read.onloadend = function(){
						//console.log(read.result);
						//console.log(JSON.parse(read.result))
						//console.log(JSON.parse(read.result).ts)
						var dataTemp = JSON.parse(read.result).ts;
						dataTarget = dataTemp[0].map(function (e, i) {
												return [e, dataTemp[1][i]];});

						tsData = {label: "uploaded timeseries", data: dataTarget, lines: {lineWidth:6}};

						console.log('Appending uploaded time series to Flot data.');

						data.push(tsData);

						console.log(tsData);

						console.log('Plotting the data of the uploaded TS.');

						$.plot("#placeholder", data, options);

						console.log("Destroy the progress bar -- we don't need it anymore.");
						$("#progressbar").progressbar( "destroy" );

					}

				} else {

					console.log("Locally received data uploaded by user.");
					console.log( document.getElementById("fileSelect").files[0] );

					var simurl = publicIP + "/simquery?id=" + "&topn=" + numSim;

					console.log("Sending user-loaded data off to be processed.");
					$.ajax({
						url: simurl,
						type: 'POST',
						data: document.getElementById("fileSelect").files[0],
						cache: false,
						contentType: 'application/json; charset=utf-8',
						dataType: 'json',
						processData: false, // Don't process the files
						contentType: false, // Set content type to false as jQuery will tell the server its a query string request
						error: function(jqXHR, textStatus, errorThrown) {
					 			// Handle errors here
									console.log('ERRORS: ' + textStatus);
						}
					})
						.done(function (result) {

							simIDs = result.id;

							$.ajax({
								url: simurl,
								type: "GET",
								dataType: "json",
								success: function(data) {

										// loop over all the IDs and collect their data
										console.log("Looping over the retrieved ID numbers and collecting their data.");

										for(var i=0; i < simIDs.length; i++) {

											idNum   = simIDs[i];

											var dataurl = publicIP + "/timeseries/" + idNum;

											console.log('Retrieving data associated with TS ID#' + idNum + '.');
											// solution from http://stackoverflow.com/questions/4201934/
											//               jquery-ajax-pass-additional-argument-to-success-callback
											$.ajax({
												url: dataurl,
												type: "GET",
												dataType: "json",
												success: onDataReceived
											});

										}
							 }
						});

					// parse the uploaded file
					// https://www.html5rocks.com/en/tutorials/file/dndfiles/
					read = new FileReader();
					read.readAsText(document.getElementById("fileSelect").files[0]);

					read.onloadend = function(){
						//console.log(read.result);
						//console.log(JSON.parse(read.result))
						//console.log(JSON.parse(read.result).ts)
						var dataTemp = JSON.parse(read.result).ts;
						dataTarget = dataTemp[0].map(function (e, i) {
												return [e, dataTemp[1][i]];});

						tsData = {label: "uploaded timeseries", data: dataTarget, lines: {lineWidth:6}};

						console.log('Appending uploaded time series to Flot data.');

						data.push(tsData);

						console.log(tsData);

						console.log('Plotting the data of the uploaded TS.');

						$.plot("#placeholder", data, options);

					}
			});
				}

			} else {
				// user hasn't specified anything!
				alert("You gotta give us something valid to work with...");

				$("#progressbar").progressbar( "destroy" );
			}

			function onDataReceived(series) {

				// pull the similar TS
				//

				// * solution from Stack Overflow:
				//   http://stackoverflow.com/questions/22015684/
				//             how-do-i-zip-two-arrays-in-javascript
				console.log('Zipping TS ID#' + simIDs[counter] + ' into appropriate form for Flot to plot.');
				var dataTarget = series.ts[0].map(function (e, i) {
					return [e, series.ts[1][i]];
				});

				tsData = {label: "timeseries " + simIDs[counter], data: dataTarget};

				console.log('Appending TS ID#' + simIDs[counter] +' to Flot data.');
				data.push(tsData);

				$.plot("#placeholder", data, options);

				// if (1) we've reached the target TS ID, in the case of a specified TS ID
				//    (2) we've reached the end of the similar IDs, in the case of
				//        an uploaded TS,
				// we're done putting together the metadata table.
				// highlight the row of the targt TS ID and show the table to the world
				if( series.metadata[0].id == timeSeriesID ) {

					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					metadataTable += tableRow;

					console.log("Put together + display the metadata table.");
					metadataTable += tableBottom;
					document.getElementById('timeseriesMetadata').innerHTML = metadataTable;
					//console.log("Destroy the progress bar -- we don't need it anymore.");
					//$("#progressbar").progressbar( "destroy" );

				} else if ( counter == simIDs.length - 1 ) {

					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					metadataTable += tableRow;

					console.log("Put together + display the metadata table.");
					metadataTable += tableBottom;
					document.getElementById('timeseriesMetadata').innerHTML = metadataTable;
					console.log("Destroy the progress bar -- we don't need it anymore.");
					$("#progressbar").progressbar( "destroy" );

				} else {

					// put together the metadata
					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					metadataTable += tableRow;

				}

				// keeps track of where we are in the array of TS IDs to plot
				counter++;

			}

			function getSimIDs(series) {
				for(var i=0; i < series.id.length; i++) {
					simIDs.push(series.id[i]);
				}
			}

		});

		// Add the Flot version string to the footer
		$("#footer").prepend("Flot " + $.plot.version + " + ");
	});<commit_msg>Minor progress bar error<commit_after>// code modified from flot AJAX example
// http://www.flotcharts.org/flot/examples/ajax/index.html

	$(function() {

		// flot plot options!
		var options = {
			lines: {
				show: true
			},
			points: {
				show: false
			},
			legend: {
				show: true
			},
			grid: {
        		hoverable: true
      		}
	};

		// GLOBAL VARIABLES AND FUNCTIONS

		console.log('Initializing variables.');
		// initialize the arrays hold the data for plotting
		var data = [];
		var dataTarget = [];

		// IP address to access our UI
		// (empty for relative URLs)
		var publicIP = '';

		// building blocks of metadata table
		var tableTop = "
";
		var tableBottom = "
";
		var tableHeader = "
ID	Mean	StDev	Level	Blarg
";
		var metadataTable = tableTop + tableHeader;

		// helps with managing the uploaded time series
		var files;

		// initialize the timeseries plot (will be empty)
		console.log('Initializing flot plot (empty plot).');
		$.plot("#placeholder", data, options);

		// ACTIONS TO COMPLETE WHEN THE `SUBMIT` BUTTON IS CLICKED
		$("button#tsQuery").click(function () {

			console.log('Submit button has been clicked.');

			// show the progress bar so the user knows that stuff
			// is going on behind the scenes

			console.log('Adding in ticking progress bar.');
			$( "#progressbar" ).progressbar({
  				value: false
			});

			// reset everything in preparation for new data
			console.log('Resetting helper variables in preparation for new data.');
			document.getElementById('timeseriesMetadata').innerHTML = '';
			data = [];
			var simIDs = [];
			var idNum = {};
			var counter = 0;
			metadataTable = tableTop + tableHeader;

			$.plot("#placeholder", data, options);

			// retrieve the time series specified by the user, and the number of
			//   most similar time series
			var timeSeriesID = document.getElementById("tsID").value;
			var numSim = document.getElementById("n_closest").value;
			console.log('Retrieved timeseries ID #' + timeSeriesID + 'and will retrieve the ' + numSim + 'most similar timeseries.');

			// CONSTRUCT OUR QUERY
			// if the user has specified an ID, use that;
			//   otherwise assume there's an uploaded file.
			if (timeSeriesID != '') {

				// DECIDE WHETHER WE NEED A SIMQUERY
				// if the user hasn't provided a number of similar TS, or the
				//   number is zero, we won't bother sending off a simquery
				if (numSim == '' | numSim == 0) {

					// add on the ID number of the target TS
					simIDs.push(timeSeriesID);

					// now we loop over all the IDs and collect their data
					console.log("Collecting data of specified time series.");

					var dataurl = publicIP + "/timeseries/" + timeSeriesID;

					console.log('Retrieving data associated with TS ID#'+ timeSeriesID + '.');

					$.ajax({
						url: dataurl,
						type: "GET",
						dataType: "json",
						success: onDataReceived
					});

				} else {

					var simurl = publicIP + "/simquery?id=" + timeSeriesID + "&topn=" + numSim;
					console.log('Constructed simquery:', simurl);

					// get the IDs of the similar time series
					console.log('Retrieving the ID numbers of the similar TS (ID number supplied).');
					$.ajax({
						url: simurl,
						type: "GET",
						dataType: "json",
						success: function(data) {

									// parse the ID numbers returned from the server
									getSimIDs(data);

									// add on the ID number of the target TS
									simIDs.push(timeSeriesID);

									// now we loop over all the IDs and collect their data
									console.log("Looping over the retrieved ID numbers and collecting their data.");

									for(var i=0; i < simIDs.length; i++) {

										idNum   = simIDs[i];

										var dataurl = publicIP + "/timeseries/" + idNum;

										console.log('Retrieving data associated with TS ID#'+ idNum + '.');
										// solution from http://stackoverflow.com/questions/4201934/
										//               jquery-ajax-pass-additional-argument-to-success-callback
										$.ajax({
											url: dataurl,
											type: "GET",
											dataType: "json",
											success: onDataReceived
										});

									}
						 }
					});
				}

			} else if ( document.getElementById("fileSelect").files.length > 0  ) {

				// DECIDE WHETHER WE NEED A SIMQUERY
				// if the user hasn't provided a number of similar TS, or the
				//   number is zero, we won't bother sending off a simquery
				if (numSim == '' | numSim == 0) {

					console.log("Locally received data uploaded by user.");
					console.log( document.getElementById("fileSelect").files[0] );

					// parse the uploaded file
					// https://www.html5rocks.com/en/tutorials/file/dndfiles/
					read = new FileReader();
					read.readAsText(document.getElementById("fileSelect").files[0]);

					read.onloadend = function(){
						//console.log(read.result);
						//console.log(JSON.parse(read.result))
						//console.log(JSON.parse(read.result).ts)
						var dataTemp = JSON.parse(read.result).ts;
						dataTarget = dataTemp[0].map(function (e, i) {
												return [e, dataTemp[1][i]];});

						tsData = {label: "uploaded timeseries", data: dataTarget, lines: {lineWidth:6}};

						console.log('Appending uploaded time series to Flot data.');

						data.push(tsData);

						console.log(tsData);

						console.log('Plotting the data of the uploaded TS.');

						$.plot("#placeholder", data, options);

						console.log("Destroy the progress bar -- we don't need it anymore.");
						$("#progressbar").progressbar( "destroy" );

					}

				} else {

					console.log("Locally received data uploaded by user.");
					console.log( document.getElementById("fileSelect").files[0] );

					var simurl = publicIP + "/simquery?id=" + "&topn=" + numSim;

					console.log("Sending user-loaded data off to be processed.");
					$.ajax({
						url: simurl,
						type: 'POST',
						data: document.getElementById("fileSelect").files[0],
						cache: false,
						contentType: 'application/json; charset=utf-8',
						dataType: 'json',
						processData: false, // Don't process the files
						contentType: false, // Set content type to false as jQuery will tell the server its a query string request
						error: function(jqXHR, textStatus, errorThrown) {
					 			// Handle errors here
									console.log('ERRORS: ' + textStatus);
						}
					})
						.done(function (result) {

							simIDs = result.id;

							$.ajax({
								url: simurl,
								type: "GET",
								dataType: "json",
								success: function(data) {

										// loop over all the IDs and collect their data
										console.log("Looping over the retrieved ID numbers and collecting their data.");

										for(var i=0; i < simIDs.length; i++) {

											idNum   = simIDs[i];

											var dataurl = publicIP + "/timeseries/" + idNum;

											console.log('Retrieving data associated with TS ID#' + idNum + '.');
											// solution from http://stackoverflow.com/questions/4201934/
											//               jquery-ajax-pass-additional-argument-to-success-callback
											$.ajax({
												url: dataurl,
												type: "GET",
												dataType: "json",
												success: onDataReceived
											});

										}
							 }
						});

					// parse the uploaded file
					// https://www.html5rocks.com/en/tutorials/file/dndfiles/
					read = new FileReader();
					read.readAsText(document.getElementById("fileSelect").files[0]);

					read.onloadend = function(){
						//console.log(read.result);
						//console.log(JSON.parse(read.result))
						//console.log(JSON.parse(read.result).ts)
						var dataTemp = JSON.parse(read.result).ts;
						dataTarget = dataTemp[0].map(function (e, i) {
												return [e, dataTemp[1][i]];});

						tsData = {label: "uploaded timeseries", data: dataTarget, lines: {lineWidth:6}};

						console.log('Appending uploaded time series to Flot data.');

						data.push(tsData);

						console.log(tsData);

						console.log('Plotting the data of the uploaded TS.');

						$.plot("#placeholder", data, options);

					}
			});
				}

			} else {
				// user hasn't specified anything!
				alert("You gotta give us something valid to work with...");

				$("#progressbar").progressbar( "destroy" );
			}

			function onDataReceived(series) {

				// pull the similar TS
				//

				// * solution from Stack Overflow:
				//   http://stackoverflow.com/questions/22015684/
				//             how-do-i-zip-two-arrays-in-javascript
				console.log('Zipping TS ID#' + simIDs[counter] + ' into appropriate form for Flot to plot.');
				var dataTarget = series.ts[0].map(function (e, i) {
					return [e, series.ts[1][i]];
				});

				tsData = {label: "timeseries " + simIDs[counter], data: dataTarget};

				console.log('Appending TS ID#' + simIDs[counter] +' to Flot data.');
				data.push(tsData);

				$.plot("#placeholder", data, options);

				// if (1) we've reached the target TS ID, in the case of a specified TS ID
				//    (2) we've reached the end of the similar IDs, in the case of
				//        an uploaded TS,
				// we're done putting together the metadata table.
				// highlight the row of the targt TS ID and show the table to the world
				if( series.metadata[0].id == timeSeriesID ) {

					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					console.log("Adding ID #" + series.metadata[0].id + "to metadata table.")
					metadataTable += tableRow;

					console.log("Put together + display the metadata table.");
					metadataTable += tableBottom;
					document.getElementById('timeseriesMetadata').innerHTML = metadataTable;
					console.log("Destroy the progress bar -- we don't need it anymore.");
					$("#progressbar").progressbar( "destroy" );

				} else if ( counter == simIDs.length - 1 ) {

					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					console.log("Adding ID #" + series.metadata[0].id + "to metadata table.")
					metadataTable += tableRow;

					console.log("Put together + display the metadata table.");
					metadataTable += tableBottom;
					document.getElementById('timeseriesMetadata').innerHTML = metadataTable;
					console.log("Destroy the progress bar -- we don't need it anymore.");
					$("#progressbar").progressbar( "destroy" );

				} else {

					// put together the metadata
					var tableRow = "
" + series.metadata[0].id + "	";
					tableRow    += "" +series.metadata[0].mean  + "	";
					tableRow    += "" +series.metadata[0].std   + "	";
					tableRow    += "" +series.metadata[0].level + "	";
					tableRow    += "" +series.metadata[0].blarg + "
";

					console.log("Adding ID #" + series.metadata[0].id + "to metadata table.")
					metadataTable += tableRow;

				}

				// keeps track of where we are in the array of TS IDs to plot
				counter++;

			}

			function getSimIDs(series) {
				for(var i=0; i < series.id.length; i++) {
					simIDs.push(series.id[i]);
				}
			}

		});

		// Add the Flot version string to the footer
		$("#footer").prepend("Flot " + $.plot.version + " + ");
	});<|endoftext|>


<commit_before>import {
  plusReady
} from 'uni-shared'
import emitter from './emitter'

let resetTimer
let isAndroid
let osVersion
let keyboardHeight
let keyboardChangeCallback
let webviewStyle
if (__PLATFORM__ === 'app-plus') {
  plusReady(() => {
    isAndroid = plus.os.name.toLowerCase() === 'android'
    osVersion = plus.os.version
    // iOS 14.6 调用同步方法导致键盘弹卡顿
    if (!isAndroid && parseFloat(osVersion) >= 14.6) {
      const currentWebview = plus.webview.currentWebview()
      webviewStyle = currentWebview.getStyle() || {}
    }
  })
  document.addEventListener('keyboardchange', function (event) {
    keyboardHeight = event.height
    keyboardChangeCallback && keyboardChangeCallback()
  }, false)
}

/**
 * 保证iOS点击输入框外隐藏键盘
 */
function iosHideKeyboard () { }

function setSoftinputTemporary (vm, reset) {
  plusReady(() => {
    const MODE_ADJUSTRESIZE = 'adjustResize'
    const MODE_ADJUSTPAN = 'adjustPan'
    const MODE_NOTHING = 'nothing'
    const currentWebview = plus.webview.currentWebview()
    // iOS 14.6 调用同步方法导致键盘弹卡顿
    const style = webviewStyle || currentWebview.getStyle() || {}
    const options = {
      mode: (reset || style.softinputMode === MODE_ADJUSTRESIZE) ? MODE_ADJUSTRESIZE : (vm.adjustPosition ? MODE_ADJUSTPAN : MODE_NOTHING),
      position: {
        top: 0,
        height: 0
      }
    }
    if (options.mode === MODE_ADJUSTPAN) {
      const rect = vm.$el.getBoundingClientRect()
      options.position.top = rect.top
      options.position.height = rect.height + (Number(vm.cursorSpacing) || 0)
    }
    currentWebview.setSoftinputTemporary(options)
  })
}

function setSoftinputNavBar (vm) {
  if (vm.showConfirmBar === 'auto') {
    delete vm.__softinputNavBar
    return
  }
  plusReady(() => {
    const currentWebview = plus.webview.currentWebview()
    const { softinputNavBar } = currentWebview.getStyle() || {}
    const showConfirmBar = softinputNavBar !== 'none'
    if (showConfirmBar !== vm.showConfirmBar) {
      vm.__softinputNavBar = softinputNavBar || 'auto'
      currentWebview.setStyle({
        softinputNavBar: vm.showConfirmBar ? 'auto' : 'none'
      })
    } else {
      delete vm.__softinputNavBar
    }
  })
}

function resetSoftinputNavBar (vm) {
  const softinputNavBar = vm.__softinputNavBar
  if (softinputNavBar) {
    plusReady(() => {
      const currentWebview = plus.webview.currentWebview()
      currentWebview.setStyle({
        softinputNavBar
      })
    })
  }
}

export default {
  name: 'Keyboard',
  mixins: [emitter],
  props: {
    cursorSpacing: {
      type: [Number, String],
      default: 0
    },
    showConfirmBar: {
      type: [Boolean, String],
      default: 'auto'
    },
    adjustPosition: {
      type: [Boolean, String],
      default: true
    },
    autoBlur: {
      type: [Boolean, String],
      default: false
    }
  },
  directives: {
    keyboard: {
      inserted (el, binding, vnode) {
        vnode.context.initKeyboard(el)
      }
    }
  },
  methods: {
    initKeyboard (el) {
      let focus

      const keyboardChange = () => {
        this.$trigger('keyboardheightchange', {}, {
          height: keyboardHeight,
          duration: 0.25
        })
        // 安卓切换不同键盘类型时会导致键盘收回，需重新设置
        if (focus && keyboardHeight === 0) {
          setSoftinputTemporary(this)
        }
        // 安卓/iOS13收起键盘时主动失去焦点
        if (this.autoBlur && focus && keyboardHeight === 0 && (isAndroid || parseInt(osVersion) >= 13)) {
          document.activeElement.blur()
        }
      }

      el.addEventListener('focus', () => {
        focus = true
        clearTimeout(resetTimer)
        document.addEventListener('click', iosHideKeyboard, false)

        if (__PLATFORM__ === 'app-plus') {
          keyboardChangeCallback = keyboardChange
          if (keyboardHeight) {
            this.$trigger('keyboardheightchange', {}, {
              height: keyboardHeight,
              duration: 0
            })
          }
          setSoftinputNavBar(this)
          setSoftinputTemporary(this)
        }
      })

      if (__PLATFORM__ === 'app-plus') {
        // 安卓单独隐藏键盘后点击输入框不会触发 focus 事件
        if (isAndroid) {
          el.addEventListener('click', () => {
            if (!this.disabled && focus && keyboardHeight === 0) {
              setSoftinputTemporary(this)
            }
          })
        }
        if (!isAndroid && parseInt(osVersion) < 12) {
          // iOS12 以下系统 focus 事件设置较迟，改在 touchstart 设置
          el.addEventListener('touchstart', () => {
            if (!this.disabled && !focus) {
              setSoftinputTemporary(this)
            }
          })
        }
      }

      const onKeyboardHide = () => {
        document.removeEventListener('click', iosHideKeyboard, false)

        if (__PLATFORM__ === 'app-plus') {
          keyboardChangeCallback = null
          if (keyboardHeight) {
            this.$trigger('keyboardheightchange', {}, {
              height: 0,
              duration: 0
            })
          }
          resetSoftinputNavBar(this)
          if (isAndroid) {
            // 还原安卓软键盘配置，避免影响 web-view 组件
            resetTimer = setTimeout(() => {
              setSoftinputTemporary(this, true)
            }, 300)
          }
        }

        // 修复ios端显示与点击位置错位的Bug by:wyq
        if (String(navigator.vendor).indexOf('Apple') === 0) {
          document.documentElement.scrollTo(document.documentElement.scrollLeft, document.documentElement.scrollTop)
        }
      }

      el.addEventListener('blur', () => {
        // 在iOS设备上，手动调用uni.hideKeyboard()，键盘收起并且触发blur，但实际并没有blur。
        // 此时如果再点击页面其他地方会重新聚焦，此处做处理
        el.blur()
        focus = false
        onKeyboardHide()
      })
    }
  }
}
<commit_msg>fix(App): 修复 iOS 端 "softinputMode": "adjustResize" 配置失效的问题 question/126632<commit_after>import {
  plusReady
} from 'uni-shared'
import emitter from './emitter'

let resetTimer
let isAndroid
let osVersion
let keyboardHeight
let keyboardChangeCallback
let webviewStyle
if (__PLATFORM__ === 'app-plus') {
  plusReady(() => {
    isAndroid = plus.os.name.toLowerCase() === 'android'
    osVersion = plus.os.version
  })
  document.addEventListener('keyboardchange', function (event) {
    keyboardHeight = event.height
    keyboardChangeCallback && keyboardChangeCallback()
  }, false)
}

/**
 * 保证iOS点击输入框外隐藏键盘
 */
function iosHideKeyboard () { }

function setSoftinputTemporary (vm, reset) {
  plusReady(() => {
    const MODE_ADJUSTRESIZE = 'adjustResize'
    const MODE_ADJUSTPAN = 'adjustPan'
    const MODE_NOTHING = 'nothing'
    const currentWebview = plus.webview.currentWebview()
    // iOS 14.6 调用同步方法导致键盘弹卡顿
    const style = webviewStyle || currentWebview.getStyle() || {}
    const options = {
      mode: (reset || style.softinputMode === MODE_ADJUSTRESIZE) ? MODE_ADJUSTRESIZE : (vm.adjustPosition ? MODE_ADJUSTPAN : MODE_NOTHING),
      position: {
        top: 0,
        height: 0
      }
    }
    if (options.mode === MODE_ADJUSTPAN) {
      const rect = vm.$el.getBoundingClientRect()
      options.position.top = rect.top
      options.position.height = rect.height + (Number(vm.cursorSpacing) || 0)
    }
    currentWebview.setSoftinputTemporary(options)
  })
}

function setSoftinputNavBar (vm) {
  if (vm.showConfirmBar === 'auto') {
    delete vm.__softinputNavBar
    return
  }
  plusReady(() => {
    const currentWebview = plus.webview.currentWebview()
    const { softinputNavBar } = currentWebview.getStyle() || {}
    const showConfirmBar = softinputNavBar !== 'none'
    if (showConfirmBar !== vm.showConfirmBar) {
      vm.__softinputNavBar = softinputNavBar || 'auto'
      currentWebview.setStyle({
        softinputNavBar: vm.showConfirmBar ? 'auto' : 'none'
      })
    } else {
      delete vm.__softinputNavBar
    }
  })
}

function resetSoftinputNavBar (vm) {
  const softinputNavBar = vm.__softinputNavBar
  if (softinputNavBar) {
    plusReady(() => {
      const currentWebview = plus.webview.currentWebview()
      currentWebview.setStyle({
        softinputNavBar
      })
    })
  }
}

export default {
  name: 'Keyboard',
  mixins: [emitter],
  props: {
    cursorSpacing: {
      type: [Number, String],
      default: 0
    },
    showConfirmBar: {
      type: [Boolean, String],
      default: 'auto'
    },
    adjustPosition: {
      type: [Boolean, String],
      default: true
    },
    autoBlur: {
      type: [Boolean, String],
      default: false
    }
  },
  directives: {
    keyboard: {
      inserted (el, binding, vnode) {
        vnode.context.initKeyboard(el)
      }
    }
  },
  methods: {
    initKeyboard (el) {
      let focus

      const keyboardChange = () => {
        this.$trigger('keyboardheightchange', {}, {
          height: keyboardHeight,
          duration: 0.25
        })
        // 安卓切换不同键盘类型时会导致键盘收回，需重新设置
        if (focus && keyboardHeight === 0) {
          setSoftinputTemporary(this)
        }
        // 安卓/iOS13收起键盘时主动失去焦点
        if (this.autoBlur && focus && keyboardHeight === 0 && (isAndroid || parseInt(osVersion) >= 13)) {
          document.activeElement.blur()
        }
      }

      el.addEventListener('focus', () => {
        focus = true
        clearTimeout(resetTimer)
        document.addEventListener('click', iosHideKeyboard, false)

        if (__PLATFORM__ === 'app-plus') {
          keyboardChangeCallback = keyboardChange
          if (keyboardHeight) {
            this.$trigger('keyboardheightchange', {}, {
              height: keyboardHeight,
              duration: 0
            })
          }
          setSoftinputNavBar(this)
          setSoftinputTemporary(this)
        }
      })

      if (__PLATFORM__ === 'app-plus') {
        // 安卓单独隐藏键盘后点击输入框不会触发 focus 事件
        if (isAndroid) {
          el.addEventListener('click', () => {
            if (!this.disabled && focus && keyboardHeight === 0) {
              setSoftinputTemporary(this)
            }
          })
        }
        if (!isAndroid) {
          // iOS12 以下系统 focus 事件设置较迟，改在 touchstart 设置
          if (parseInt(osVersion) < 12) {
            el.addEventListener('touchstart', () => {
              if (!this.disabled && !focus) {
                setSoftinputTemporary(this)
              }
            })
          }
          // iOS 14.6 调用同步方法导致键盘弹卡顿
          if (parseFloat(osVersion) >= 14.6 && !webviewStyle) {
            plusReady(() => {
              const currentWebview = plus.webview.currentWebview()
              webviewStyle = currentWebview.getStyle() || {}
            })
          }
        }
      }

      const onKeyboardHide = () => {
        document.removeEventListener('click', iosHideKeyboard, false)

        if (__PLATFORM__ === 'app-plus') {
          keyboardChangeCallback = null
          if (keyboardHeight) {
            this.$trigger('keyboardheightchange', {}, {
              height: 0,
              duration: 0
            })
          }
          resetSoftinputNavBar(this)
          if (isAndroid) {
            // 还原安卓软键盘配置，避免影响 web-view 组件
            resetTimer = setTimeout(() => {
              setSoftinputTemporary(this, true)
            }, 300)
          }
        }

        // 修复ios端显示与点击位置错位的Bug by:wyq
        if (String(navigator.vendor).indexOf('Apple') === 0) {
          document.documentElement.scrollTo(document.documentElement.scrollLeft, document.documentElement.scrollTop)
        }
      }

      el.addEventListener('blur', () => {
        // 在iOS设备上，手动调用uni.hideKeyboard()，键盘收起并且触发blur，但实际并没有blur。
        // 此时如果再点击页面其他地方会重新聚焦，此处做处理
        el.blur()
        focus = false
        onKeyboardHide()
      })
    }
  }
}
<|endoftext|>


<commit_before>/*
    Copyright 2013-2014 appPlant UG

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

var LocalNotification = function () {
    this._defaults = {
        message:    '',
        title:      '',
        autoCancel: false,
        badge:      0,
        id:         '0',
        json:       '',
        repeat:     ''
    };
};

LocalNotification.prototype = {
    /**
     * Returns the default settings
     *
     * @return {Object}
     */
    getDefaults: function () {
        return this._defaults;
    },

    /**
     * Overwrite default settings
     *
     * @param {Object} defaults
     */
    setDefaults: function (newDefaults) {
        var defaults = this.getDefaults();

        for (var key in defaults) {
            if (newDefaults[key] !== undefined) {
                defaults[key] = newDefaults[key];
            }
        }
    },

    /**
     * @private
     * Merge settings with default values
     *
     * @param {Object} options
     * @retrun {Object}
     */
    mergeWithDefaults: function (options) {
        var defaults = this.getDefaults();

        for (var key in defaults) {
            if (options[key] === undefined) {
                options[key] = defaults[key];
            }
        }

        return options;
    },

    /**
     * @private
     */
    applyPlatformSpecificOptions: function () {
        var defaults = this._defaults;

        switch (device.platform) {
        case 'Android':
            defaults.icon       = 'icon';
            defaults.smallIcon  = null;
            defaults.ongoing    = false;
            defaults.sound      = 'TYPE_NOTIFICATION'; break;
        case 'iOS':
            defaults.sound      = ''; break;
        case 'WinCE': case 'Win32NT':
            defaults.smallImage = null;
            defaults.image      = null;
            defaults.wideImage  = null;
        };
    },

    /**
     * Add a new entry to the registry
     *
     * @param {Object} options
     * @return {Number} The notification's ID
     */
    add: function (options) {
        var options    = this.mergeWithDefaults(options),
            callbackFn = null;

        if (options.id) {
            options.id = options.id.toString();
        }

        if (options.date === undefined) {
            options.date = new Date();
        }

        if (typeof options.date == 'object') {
            options.date = Math.round(options.date.getTime()/1000);
        }

        if (['WinCE', 'Win32NT'].indexOf(device.platform)) {
            callbackFn = function (cmd) {
                eval(cmd);
            };
        }

        cordova.exec(callbackFn, null, 'LocalNotification', 'add', [options]);

        return options.id;
    },

    /**
     * Cancels the specified notification
     *
     * @param {String} id of the notification
     */
    cancel: function (id) {
        cordova.exec(null, null, 'LocalNotification', 'cancel', [id.toString()]);
    },

    /**
     * Removes all previously registered notifications
     */
    cancelAll: function () {
        cordova.exec(null, null, 'LocalNotification', 'cancelAll', []);
    },

    /**
     * @async
     *
     * Retrieves a list with all currently pending notifications.
     *
     * @param {Function} callback
     */
    getScheduledIds: function (callback) {
        cordova.exec(callback, null, 'LocalNotification', 'getScheduledIds', []);
    },

    /**
     * @async
     *
     * Checks wether a notification with an ID is scheduled.
     *
     * @param {String}   id
     * @param {Function} callback
     */
    isScheduled: function (id, callback) {
        cordova.exec(callback, null, 'LocalNotification', 'isScheduled', [id.toString()]);
    },

    /**
     * Occurs when a notification was added.
     *
     * @param {String} id    The ID of the notification
     * @param {String} state Either "foreground" or "background"
     * @param {String} json  A custom (JSON) string
     */
    onadd: function (id, state, json) {},

    /**
     * Occurs when the notification is triggered.
     *
     * @param {String} id    The ID of the notification
     * @param {String} state Either "foreground" or "background"
     * @param {String} json  A custom (JSON) string
     */
    ontrigger: function (id, state, json) {},

    /**
     * Fires after the notification was clicked.
     *
     * @param {String} id    The ID of the notification
     * @param {String} state Either "foreground" or "background"
     * @param {String} json  A custom (JSON) string
     */
    onclick: function (id, state, json) {},

    /**
     * Fires if the notification was canceled.
     *
     * @param {String} id    The ID of the notification
     * @param {String} state Either "foreground" or "background"
     * @param {String} json  A custom (JSON) string
     */
    oncancel: function (id, state, json) {}
};

var plugin  = new LocalNotification(),
    channel = require('cordova/channel');

channel.deviceready.subscribe( function () {
    cordova.exec(null, null, 'LocalNotification', 'deviceready', []);
});

channel.onCordovaReady.subscribe( function () {
    channel.onCordovaInfoReady.subscribe( function () {
        if (device.platform == 'Android') {
            channel.onPause.subscribe( function () {
                cordova.exec(null, null, 'LocalNotification', 'pause', []);
            });

            channel.onResume.subscribe( function () {
                cordova.exec(null, null, 'LocalNotification', 'resume', []);
            });

            cordova.exec(null, null, 'LocalNotification', 'resume', []);
        }

        plugin.applyPlatformSpecificOptions();
    });
});

module.exports = plugin;
<commit_msg>Small coding changes<commit_after>/*
    Copyright 2013-2014 appPlant UG

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.
*/

var LocalNotification = function () {
    this._defaults = {
        message:    '',
        title:      '',
        autoCancel: false,
        badge:      0,
        id:         '0',
        json:       '',
        repeat:     ''
    };
};

LocalNotification.prototype = {
    /**
     * Returns the default settings
     *
     * @return {Object}
     */
    getDefaults: function () {
        return this._defaults;
    },

    /**
     * Overwrite default settings
     *
     * @param {Object} defaults
     */
    setDefaults: function (newDefaults) {
        var defaults = this.getDefaults();

        for (var key in defaults) {
            if (newDefaults[key] !== undefined) {
                defaults[key] = newDefaults[key];
            }
        }
    },

    /**
     * @private
     *
     * Merges custom properties with the default values.
     *
     * @param {Object} options
     *      Set of custom values
     *
     * @retrun {Object}
     *      The merged property list
     */
    mergeWithDefaults: function (options) {
        var defaults = this.getDefaults();

        for (var key in defaults) {
            if (options[key] === undefined) {
                options[key] = defaults[key];
            }
        }

        return options;
    },

    /**
     * @private
     *
     * Merges the platform specific properties into the default properties.
     *
     * @return {Object}
     *      The default properties for the platform
     */
    applyPlatformSpecificOptions: function () {
        var defaults = this._defaults;

        switch (device.platform) {
        case 'Android':
            defaults.icon       = 'icon';
            defaults.smallIcon  = null;
            defaults.ongoing    = false;
            defaults.sound      = 'TYPE_NOTIFICATION'; break;
        case 'iOS':
            defaults.sound      = ''; break;
        case 'WinCE': case 'Win32NT':
            defaults.smallImage = null;
            defaults.image      = null;
            defaults.wideImage  = null;
        };

        return defaults;
    },

    /**
     * Add a new entry to the registry
     *
     * @param {Object} options
     *      The notification properties
     *
     * @return {Number}
     *      The notification's ID
     */
    add: function (options) {
        var options    = this.mergeWithDefaults(options),
            callbackFn = null;

        if (options.id) {
            options.id = options.id.toString();
        }

        if (options.date === undefined) {
            options.date = new Date();
        }

        if (typeof options.date == 'object') {
            options.date = Math.round(options.date.getTime()/1000);
        }

        if (['WinCE', 'Win32NT'].indexOf(device.platform)) {
            callbackFn = function (cmd) {
                eval(cmd);
            };
        }

        cordova.exec(callbackFn, null, 'LocalNotification', 'add', [options]);

        return options.id;
    },

    /**
     * Cancels the specified notification.
     *
     * @param {String} id
     *      The ID of the notification
     */
    cancel: function (id) {
        var id = id.toString();

        cordova.exec(null, null, 'LocalNotification', 'cancel', [id]);
    },

    /**
     * Removes all previously registered notifications.
     */
    cancelAll: function () {
        cordova.exec(null, null, 'LocalNotification', 'cancelAll', []);
    },

    /**
     * Retrieves a list with all currently pending notifications.
     *
     * @param {Function} callback
     *      A callback function to be called with the list
     */
    getScheduledIds: function (callback) {
        cordova.exec(callback, null, 'LocalNotification', 'getScheduledIds', []);
    },

    /**
     * Checks wether a notification with an ID is scheduled.
     *
     * @param {String} id
     *      The ID of the notification
     * @param {Function} callback
     *      A callback function to be called with the list
     */
    isScheduled: function (id, callback) {
        var id = id.toString();

        cordova.exec(callback, null, 'LocalNotification', 'isScheduled', [id]);
    },

    /**
     * Occurs when a notification was added.
     *
     * @param {String} id
     *      The ID of the notification
     * @param {String} state
     *      Either "foreground" or "background"
     * @param {String} json
     *      A custom (JSON) string
     */
    onadd: function (id, state, json) {},

    /**
     * Occurs when the notification is triggered.
     *
     * @param {String} id
     *      The ID of the notification
     * @param {String} state
     *      Either "foreground" or "background"
     * @param {String} json
     *      A custom (JSON) string
     */
    ontrigger: function (id, state, json) {},

    /**
     * Fires after the notification was clicked.
     *
     * @param {String} id
     *      The ID of the notification
     * @param {String} state
     *      Either "foreground" or "background"
     * @param {String} json
     *      A custom (JSON) string
     */
    onclick: function (id, state, json) {},

    /**
     * Fires if the notification was canceled.
     *
     * @param {String} id
     *      The ID of the notification
     * @param {String} state
     *      Either "foreground" or "background"
     * @param {String} json
     *      A custom (JSON) string
     */
    oncancel: function (id, state, json) {}
};

var plugin  = new LocalNotification(),
    channel = require('cordova/channel');

// Called after all 'deviceready' listener are called
channel.deviceready.subscribe( function () {
    // Device is ready now, the listeners are registered and all queued events
    // can be executed now.
    cordova.exec(null, null, 'LocalNotification', 'deviceready', []);
});

channel.onCordovaReady.subscribe( function () {
    // The cordova device plugin is ready now
    channel.onCordovaInfoReady.subscribe( function () {
        if (device.platform == 'Android') {
            channel.onPause.subscribe( function () {
                // Necessary to set the state to `background`
                cordova.exec(null, null, 'LocalNotification', 'pause', []);
            });

            channel.onResume.subscribe( function () {
                // Necessary to set the state to `foreground`
                cordova.exec(null, null, 'LocalNotification', 'resume', []);
            });

            // Necessary to set the state to `foreground`
            cordova.exec(null, null, 'LocalNotification', 'resume', []);
        }

        // Merges the platform specific properties into the default properties
        plugin.applyPlatformSpecificOptions();
    });
});

module.exports = plugin;
<|endoftext|>


<commit_before>export const ALERT_BANNER_ACTIVE = true

// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  tag: 'Oct 19-21',
  url:
    'https://hashiconf.com/global/?utm_campaign=22Q3_WW_HASHICONFGLOBAL_EVENT-USER&utm_source=CorpBanner&utm_medium=EVT&utm_offer=EVENT-USER',
  text: 'The countdown to HashiConf Global is on. View the full schedule now.',
  linkText: 'View Schedule',
  // Set the expirationDate prop with a datetime string (e.g. '2020-01-31T12:00:00-07:00')
  // if you'd like the component to stop showing at or after a certain date
  expirationDate: '2021-10-19T12:00:00-07:00',
}
<commit_msg>Update alert banner for HashiConf<commit_after>export const ALERT_BANNER_ACTIVE = true

// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  tag: 'Oct 19-21',
  url: 'https://hashiconf.com/global/?utm_source=DocsBanner',
  text: 'HashiConf Global is live.',
  linkText: 'Join Now',
  // Set the expirationDate prop with a datetime string (e.g. '2020-01-31T12:00:00-07:00')
  // if you'd like the component to stop showing at or after a certain date
  expirationDate: '2021-10-21T12:00:00-07:00',
}
<|endoftext|>


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

/**
 * @fileoverview Functions for setting, getting and deleting cookies.
 *
 * @author arv@google.com (Erik Arvidsson)
 */

goog.provide('goog.net.Cookies');
goog.provide('goog.net.cookies');

/**
 * A class for handling browser cookies.
 * @param {Document} context The context document to get/set cookies on.
 * @constructor
 * @final
 */
goog.net.Cookies = function(context) {
  /**
   * The context document to get/set cookies on
   * @type {Document}
   * @private
   */
  this.document_ = context;
};

/**
 * Static constant for the size of cookies. Per the spec, there's a 4K limit
 * to the size of a cookie. To make sure users can't break this limit, we
 * should truncate long cookies at 3950 bytes, to be extra careful with dumb
 * browsers/proxies that interpret 4K as 4000 rather than 4096.
 * @type {number}
 */
goog.net.Cookies.MAX_COOKIE_LENGTH = 3950;

/**
 * RegExp used to split the cookies string.
 * @type {RegExp}
 * @private
 */
goog.net.Cookies.SPLIT_RE_ = /\s*;\s*/;

/**
 * Returns true if cookies are enabled.
 * @return {boolean} True if cookies are enabled.
 */
goog.net.Cookies.prototype.isEnabled = function() {
  return navigator.cookieEnabled;
};

/**
 * We do not allow '=', ';', or white space in the name.
 *
 * NOTE: The following are allowed by this method, but should be avoided for
 * cookies handled by the server.
 * - any name starting with '$'
 * - 'Comment'
 * - 'Domain'
 * - 'Expires'
 * - 'Max-Age'
 * - 'Path'
 * - 'Secure'
 * - 'Version'
 *
 * @param {string} name Cookie name.
 * @return {boolean} Whether name is valid.
 *
 * @see RFC 2109
 * @see RFC 2965
 */
goog.net.Cookies.prototype.isValidName = function(name) {
  return !(/[;=\s]/.test(name));
};

/**
 * We do not allow ';' or line break in the value.
 *
 * Spec does not mention any illegal characters, but in practice semi-colons
 * break parsing and line breaks truncate the name.
 *
 * @param {string} value Cookie value.
 * @return {boolean} Whether value is valid.
 *
 * @see RFC 2109
 * @see RFC 2965
 */
goog.net.Cookies.prototype.isValidValue = function(value) {
  return !(/[;\r\n]/.test(value));
};

/**
 * Sets a cookie.  The max_age can be -1 to set a session cookie. To remove and
 * expire cookies, use remove() instead.
 *
 * Neither the {@code name} nor the {@code value} are encoded in any way. It is
 * up to the callers of {@code get} and {@code set} (as well as all the other
 * methods) to handle any possible encoding and decoding.
 *
 * @throws {!Error} If the {@code name} fails #goog.net.cookies.isValidName.
 * @throws {!Error} If the {@code value} fails #goog.net.cookies.isValidValue.
 *
 * @param {string} name  The cookie name.
 * @param {string} value  The cookie value.
 * @param {number=} opt_maxAge  The max age in seconds (from now). Use -1 to
 *     set a session cookie. If not provided, the default is -1
 *     (i.e. set a session cookie).
 * @param {?string=} opt_path  The path of the cookie. If not present then this
 *     uses the full request path.
 * @param {?string=} opt_domain  The domain of the cookie, or null to not
 *     specify a domain attribute (browser will use the full request host name).
 *     If not provided, the default is null (i.e. let browser use full request
 *     host name).
 * @param {boolean=} opt_secure Whether the cookie should only be sent over
 *     a secure channel.
 */
goog.net.Cookies.prototype.set = function(
    name, value, opt_maxAge, opt_path, opt_domain, opt_secure) {
  if (!this.isValidName(name)) {
    throw Error('Invalid cookie name "' + name + '"');
  }
  if (!this.isValidValue(value)) {
    throw Error('Invalid cookie value "' + value + '"');
  }

  if (!goog.isDef(opt_maxAge)) {
    opt_maxAge = -1;
  }

  var domainStr = opt_domain ? ';domain=' + opt_domain : '';
  var pathStr = opt_path ? ';path=' + opt_path : '';
  var secureStr = opt_secure ? ';secure' : '';

  var expiresStr;

  // Case 1: Set a session cookie.
  if (opt_maxAge < 0) {
    expiresStr = '';

    // Case 2: Remove the cookie.
    // Note: We don't tell people about this option in the function doc because
    // we prefer people to use remove() to remove cookies.
  } else if (opt_maxAge == 0) {
    // Note: Don't use Jan 1, 1970 for date because NS 4.76 will try to convert
    // it to local time, and if the local time is before Jan 1, 1970, then the
    // browser will ignore the Expires attribute altogether.
    var pastDate = new Date(1970, 1 /*Feb*/, 1);  // Feb 1, 1970
    expiresStr = ';expires=' + pastDate.toUTCString();

    // Case 3: Set a persistent cookie.
  } else {
    var futureDate = new Date(goog.now() + opt_maxAge * 1000);
    expiresStr = ';expires=' + futureDate.toUTCString();
  }

  this.setCookie_(
      name + '=' + value + domainStr + pathStr + expiresStr + secureStr);
};

/**
 * Returns the value for the first cookie with the given name.
 * @param {string} name  The name of the cookie to get.
 * @param {string=} opt_default  If not found this is returned instead.
 * @return {string|undefined}  The value of the cookie. If no cookie is set this
 *     returns opt_default or undefined if opt_default is not provided.
 */
goog.net.Cookies.prototype.get = function(name, opt_default) {
  var nameEq = name + '=';
  var parts = this.getParts_();
  for (var i = 0, part; part = parts[i]; i++) {
    // startsWith
    if (part.lastIndexOf(nameEq, 0) == 0) {
      return part.substr(nameEq.length);
    }
    if (part == name) {
      return '';
    }
  }
  return opt_default;
};

/**
 * Removes and expires a cookie.
 * @param {string} name  The cookie name.
 * @param {string=} opt_path  The path of the cookie, or null to expire a cookie
 *     set at the full request path. If not provided, the default is '/'
 *     (i.e. path=/).
 * @param {string=} opt_domain  The domain of the cookie, or null to expire a
 *     cookie set at the full request host name. If not provided, the default is
 *     null (i.e. cookie at full request host name).
 * @return {boolean} Whether the cookie existed before it was removed.
 */
goog.net.Cookies.prototype.remove = function(name, opt_path, opt_domain) {
  var rv = this.containsKey(name);
  this.set(name, '', 0, opt_path, opt_domain);
  return rv;
};

/**
 * Gets the names for all the cookies.
 * @return {Array<string>} An array with the names of the cookies.
 */
goog.net.Cookies.prototype.getKeys = function() {
  return this.getKeyValues_().keys;
};

/**
 * Gets the values for all the cookies.
 * @return {Array<string>} An array with the values of the cookies.
 */
goog.net.Cookies.prototype.getValues = function() {
  return this.getKeyValues_().values;
};

/**
 * @return {boolean} Whether there are any cookies for this document.
 */
goog.net.Cookies.prototype.isEmpty = function() {
  return !this.getCookie_();
};

/**
 * @return {number} The number of cookies for this document.
 */
goog.net.Cookies.prototype.getCount = function() {
  var cookie = this.getCookie_();
  if (!cookie) {
    return 0;
  }
  return this.getParts_().length;
};

/**
 * Returns whether there is a cookie with the given name.
 * @param {string} key The name of the cookie to test for.
 * @return {boolean} Whether there is a cookie by that name.
 */
goog.net.Cookies.prototype.containsKey = function(key) {
  // substring will return empty string if the key is not found, so the get
  // function will only return undefined
  return goog.isDef(this.get(key));
};

/**
 * Returns whether there is a cookie with the given value. (This is an O(n)
 * operation.)
 * @param {string} value  The value to check for.
 * @return {boolean} Whether there is a cookie with that value.
 */
goog.net.Cookies.prototype.containsValue = function(value) {
  // this O(n) in any case so lets do the trivial thing.
  var values = this.getKeyValues_().values;
  for (var i = 0; i < values.length; i++) {
    if (values[i] == value) {
      return true;
    }
  }
  return false;
};

/**
 * Removes all cookies for this document.  Note that this will only remove
 * cookies from the current path and domain.  If there are cookies set using a
 * subpath and/or another domain these will still be there.
 */
goog.net.Cookies.prototype.clear = function() {
  var keys = this.getKeyValues_().keys;
  for (var i = keys.length - 1; i >= 0; i--) {
    this.remove(keys[i]);
  }
};

/**
 * Private helper function to allow testing cookies without depending on the
 * browser.
 * @param {string} s The cookie string to set.
 * @private
 */
goog.net.Cookies.prototype.setCookie_ = function(s) {
  this.document_.cookie = s;
};

/**
 * Private helper function to allow testing cookies without depending on the
 * browser. IE6 can return null here.
 * @return {string} Returns the {@code document.cookie}.
 * @private
 */
goog.net.Cookies.prototype.getCookie_ = function() {
  return this.document_.cookie;
};

/**
 * @return {!Array<string>} The cookie split on semi colons.
 * @private
 */
goog.net.Cookies.prototype.getParts_ = function() {
  return (this.getCookie_() || '').split(goog.net.Cookies.SPLIT_RE_);
};

/**
 * Gets the names and values for all the cookies.
 * @return {!{keys:!Array<string>, values:!Array<string>}} An object with keys
 *     and values.
 * @private
 */
goog.net.Cookies.prototype.getKeyValues_ = function() {
  var parts = this.getParts_();
  var keys = [], values = [], index, part;
  for (var i = 0; part = parts[i]; i++) {
    index = part.indexOf('=');

    if (index == -1) {  // empty name
      keys.push('');
      values.push(part);
    } else {
      keys.push(part.substring(0, index));
      values.push(part.substring(index + 1));
    }
  }
  return {keys: keys, values: values};
};

/**
 * A static default instance.
 * @type {goog.net.Cookies}
 */
goog.net.cookies = new goog.net.Cookies(document);

/**
 * Define the constant on the instance in order not to break many references to
 * it.
 * @type {number}
 * @deprecated Use goog.net.Cookies.MAX_COOKIE_LENGTH instead.
 */
goog.net.cookies.MAX_COOKIE_LENGTH = goog.net.Cookies.MAX_COOKIE_LENGTH;
<commit_msg>Change goog.net.Cookies constructor to accept null. This allows safe instantiation of the goog.net.cookies singleton in a web worker environment.<commit_after>// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Functions for setting, getting and deleting cookies.
 *
 * @author arv@google.com (Erik Arvidsson)
 */

goog.provide('goog.net.Cookies');
goog.provide('goog.net.cookies');

/**
 * A class for handling browser cookies.
 * @param {?Document} context The context document to get/set cookies on.
 * @constructor
 * @final
 */
goog.net.Cookies = function(context) {
  /**
  * The context document to get/set cookies on. If no document context is
  * passed, use a fake one with only the "cookie" attribute. This allows
  * this class to be instantiated safely in web worker environments.
  * @private {{cookie: string}}
  */
  this.document_ = context || {cookie: ''};
};

/**
 * Static constant for the size of cookies. Per the spec, there's a 4K limit
 * to the size of a cookie. To make sure users can't break this limit, we
 * should truncate long cookies at 3950 bytes, to be extra careful with dumb
 * browsers/proxies that interpret 4K as 4000 rather than 4096.
 * @type {number}
 */
goog.net.Cookies.MAX_COOKIE_LENGTH = 3950;

/**
 * RegExp used to split the cookies string.
 * @type {RegExp}
 * @private
 */
goog.net.Cookies.SPLIT_RE_ = /\s*;\s*/;

/**
 * Returns true if cookies are enabled.
 * @return {boolean} True if cookies are enabled.
 */
goog.net.Cookies.prototype.isEnabled = function() {
  return navigator.cookieEnabled;
};

/**
 * We do not allow '=', ';', or white space in the name.
 *
 * NOTE: The following are allowed by this method, but should be avoided for
 * cookies handled by the server.
 * - any name starting with '$'
 * - 'Comment'
 * - 'Domain'
 * - 'Expires'
 * - 'Max-Age'
 * - 'Path'
 * - 'Secure'
 * - 'Version'
 *
 * @param {string} name Cookie name.
 * @return {boolean} Whether name is valid.
 *
 * @see RFC 2109
 * @see RFC 2965
 */
goog.net.Cookies.prototype.isValidName = function(name) {
  return !(/[;=\s]/.test(name));
};

/**
 * We do not allow ';' or line break in the value.
 *
 * Spec does not mention any illegal characters, but in practice semi-colons
 * break parsing and line breaks truncate the name.
 *
 * @param {string} value Cookie value.
 * @return {boolean} Whether value is valid.
 *
 * @see RFC 2109
 * @see RFC 2965
 */
goog.net.Cookies.prototype.isValidValue = function(value) {
  return !(/[;\r\n]/.test(value));
};

/**
 * Sets a cookie.  The max_age can be -1 to set a session cookie. To remove and
 * expire cookies, use remove() instead.
 *
 * Neither the {@code name} nor the {@code value} are encoded in any way. It is
 * up to the callers of {@code get} and {@code set} (as well as all the other
 * methods) to handle any possible encoding and decoding.
 *
 * @throws {!Error} If the {@code name} fails #goog.net.cookies.isValidName.
 * @throws {!Error} If the {@code value} fails #goog.net.cookies.isValidValue.
 *
 * @param {string} name  The cookie name.
 * @param {string} value  The cookie value.
 * @param {number=} opt_maxAge  The max age in seconds (from now). Use -1 to
 *     set a session cookie. If not provided, the default is -1
 *     (i.e. set a session cookie).
 * @param {?string=} opt_path  The path of the cookie. If not present then this
 *     uses the full request path.
 * @param {?string=} opt_domain  The domain of the cookie, or null to not
 *     specify a domain attribute (browser will use the full request host name).
 *     If not provided, the default is null (i.e. let browser use full request
 *     host name).
 * @param {boolean=} opt_secure Whether the cookie should only be sent over
 *     a secure channel.
 */
goog.net.Cookies.prototype.set = function(
    name, value, opt_maxAge, opt_path, opt_domain, opt_secure) {
  if (!this.isValidName(name)) {
    throw Error('Invalid cookie name "' + name + '"');
  }
  if (!this.isValidValue(value)) {
    throw Error('Invalid cookie value "' + value + '"');
  }

  if (!goog.isDef(opt_maxAge)) {
    opt_maxAge = -1;
  }

  var domainStr = opt_domain ? ';domain=' + opt_domain : '';
  var pathStr = opt_path ? ';path=' + opt_path : '';
  var secureStr = opt_secure ? ';secure' : '';

  var expiresStr;

  // Case 1: Set a session cookie.
  if (opt_maxAge < 0) {
    expiresStr = '';

    // Case 2: Remove the cookie.
    // Note: We don't tell people about this option in the function doc because
    // we prefer people to use remove() to remove cookies.
  } else if (opt_maxAge == 0) {
    // Note: Don't use Jan 1, 1970 for date because NS 4.76 will try to convert
    // it to local time, and if the local time is before Jan 1, 1970, then the
    // browser will ignore the Expires attribute altogether.
    var pastDate = new Date(1970, 1 /*Feb*/, 1);  // Feb 1, 1970
    expiresStr = ';expires=' + pastDate.toUTCString();

    // Case 3: Set a persistent cookie.
  } else {
    var futureDate = new Date(goog.now() + opt_maxAge * 1000);
    expiresStr = ';expires=' + futureDate.toUTCString();
  }

  this.setCookie_(
      name + '=' + value + domainStr + pathStr + expiresStr + secureStr);
};

/**
 * Returns the value for the first cookie with the given name.
 * @param {string} name  The name of the cookie to get.
 * @param {string=} opt_default  If not found this is returned instead.
 * @return {string|undefined}  The value of the cookie. If no cookie is set this
 *     returns opt_default or undefined if opt_default is not provided.
 */
goog.net.Cookies.prototype.get = function(name, opt_default) {
  var nameEq = name + '=';
  var parts = this.getParts_();
  for (var i = 0, part; part = parts[i]; i++) {
    // startsWith
    if (part.lastIndexOf(nameEq, 0) == 0) {
      return part.substr(nameEq.length);
    }
    if (part == name) {
      return '';
    }
  }
  return opt_default;
};

/**
 * Removes and expires a cookie.
 * @param {string} name  The cookie name.
 * @param {string=} opt_path  The path of the cookie, or null to expire a cookie
 *     set at the full request path. If not provided, the default is '/'
 *     (i.e. path=/).
 * @param {string=} opt_domain  The domain of the cookie, or null to expire a
 *     cookie set at the full request host name. If not provided, the default is
 *     null (i.e. cookie at full request host name).
 * @return {boolean} Whether the cookie existed before it was removed.
 */
goog.net.Cookies.prototype.remove = function(name, opt_path, opt_domain) {
  var rv = this.containsKey(name);
  this.set(name, '', 0, opt_path, opt_domain);
  return rv;
};

/**
 * Gets the names for all the cookies.
 * @return {Array<string>} An array with the names of the cookies.
 */
goog.net.Cookies.prototype.getKeys = function() {
  return this.getKeyValues_().keys;
};

/**
 * Gets the values for all the cookies.
 * @return {Array<string>} An array with the values of the cookies.
 */
goog.net.Cookies.prototype.getValues = function() {
  return this.getKeyValues_().values;
};

/**
 * @return {boolean} Whether there are any cookies for this document.
 */
goog.net.Cookies.prototype.isEmpty = function() {
  return !this.getCookie_();
};

/**
 * @return {number} The number of cookies for this document.
 */
goog.net.Cookies.prototype.getCount = function() {
  var cookie = this.getCookie_();
  if (!cookie) {
    return 0;
  }
  return this.getParts_().length;
};

/**
 * Returns whether there is a cookie with the given name.
 * @param {string} key The name of the cookie to test for.
 * @return {boolean} Whether there is a cookie by that name.
 */
goog.net.Cookies.prototype.containsKey = function(key) {
  // substring will return empty string if the key is not found, so the get
  // function will only return undefined
  return goog.isDef(this.get(key));
};

/**
 * Returns whether there is a cookie with the given value. (This is an O(n)
 * operation.)
 * @param {string} value  The value to check for.
 * @return {boolean} Whether there is a cookie with that value.
 */
goog.net.Cookies.prototype.containsValue = function(value) {
  // this O(n) in any case so lets do the trivial thing.
  var values = this.getKeyValues_().values;
  for (var i = 0; i < values.length; i++) {
    if (values[i] == value) {
      return true;
    }
  }
  return false;
};

/**
 * Removes all cookies for this document.  Note that this will only remove
 * cookies from the current path and domain.  If there are cookies set using a
 * subpath and/or another domain these will still be there.
 */
goog.net.Cookies.prototype.clear = function() {
  var keys = this.getKeyValues_().keys;
  for (var i = keys.length - 1; i >= 0; i--) {
    this.remove(keys[i]);
  }
};

/**
 * Private helper function to allow testing cookies without depending on the
 * browser.
 * @param {string} s The cookie string to set.
 * @private
 */
goog.net.Cookies.prototype.setCookie_ = function(s) {
  this.document_.cookie = s;
};

/**
 * Private helper function to allow testing cookies without depending on the
 * browser. IE6 can return null here.
 * @return {string} Returns the {@code document.cookie}.
 * @private
 */
goog.net.Cookies.prototype.getCookie_ = function() {
  return this.document_.cookie;
};

/**
 * @return {!Array<string>} The cookie split on semi colons.
 * @private
 */
goog.net.Cookies.prototype.getParts_ = function() {
  return (this.getCookie_() || '').split(goog.net.Cookies.SPLIT_RE_);
};

/**
 * Gets the names and values for all the cookies.
 * @return {!{keys:!Array<string>, values:!Array<string>}} An object with keys
 *     and values.
 * @private
 */
goog.net.Cookies.prototype.getKeyValues_ = function() {
  var parts = this.getParts_();
  var keys = [], values = [], index, part;
  for (var i = 0; part = parts[i]; i++) {
    index = part.indexOf('=');

    if (index == -1) {  // empty name
      keys.push('');
      values.push(part);
    } else {
      keys.push(part.substring(0, index));
      values.push(part.substring(index + 1));
    }
  }
  return {keys: keys, values: values};
};

// TODO(b/23687502): This should be a singleton getter instead of a static
// instance.
/**
 * A static default instance.
 * @type {goog.net.Cookies}
 */
goog.net.cookies =
    new goog.net.Cookies(typeof document == 'undefined' ? null : document);

/**
 * Define the constant on the instance in order not to break many references to
 * it.
 * @type {number}
 * @deprecated Use goog.net.Cookies.MAX_COOKIE_LENGTH instead.
 */
goog.net.cookies.MAX_COOKIE_LENGTH = goog.net.Cookies.MAX_COOKIE_LENGTH;
<|endoftext|>


<commit_before>export const ALERT_BANNER_ACTIVE = true
// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  tag: 'Blog post',
  url: 'https://www.hashicorp.com/blog/a-new-chapter-for-hashicorp',
  text: 'HashiCorp shares have begun trading on the Nasdaq. Read the blog from our founders, Mitchell Hashimoto and Armon Dadgar.',
  linkText: 'Read the post',
  // Set the expirationDate prop with a datetime string (e.g. '2020-01-31T12:00:00-07:00')
  // if you'd like the component to stop showing at or after a certain date
  expirationDate: '2021-12-17T23:00:00-07:00',
}
<commit_msg>website: Disable alert banner (#11688)<commit_after>export const ALERT_BANNER_ACTIVE = false
// https://github.com/hashicorp/web-components/tree/master/packages/alert-banner
export default {
  tag: 'Blog post',
  url: 'https://www.hashicorp.com/blog/a-new-chapter-for-hashicorp',
  text: 'HashiCorp shares have begun trading on the Nasdaq. Read the blog from our founders, Mitchell Hashimoto and Armon Dadgar.',
  linkText: 'Read the post',
  // Set the expirationDate prop with a datetime string (e.g. '2020-01-31T12:00:00-07:00')
  // if you'd like the component to stop showing at or after a certain date
  expirationDate: '2021-12-17T23:00:00-07:00',
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.net.XhrLike');

/**
 * Interface for the common parts of XMLHttpRequest.
 *
 * Mostly copied from externs/w3c_xml.js.
 *
 * @interface
 * @see http://www.w3.org/TR/XMLHttpRequest/
 */
goog.net.XhrLike = function() {};

/**
 * Typedef that refers to either native or custom-implemented XHR objects.
 * @typedef {!goog.net.XhrLike|!XMLHttpRequest}
 */
goog.net.XhrLike.OrNative;

/**
 * @type {function()|null|undefined}
 * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange
 */
goog.net.XhrLike.prototype.onreadystatechange;

/**
 * @type {?ArrayBuffer|?Blob|?Document|?Object|?string}
 * @see https://xhr.spec.whatwg.org/#response-object
 */
goog.net.XhrLike.prototype.response;

/**
 * @type {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
 */
goog.net.XhrLike.prototype.responseText;

/**
 * @type {string}
 * @see https://xhr.spec.whatwg.org/#the-responsetype-attribute
 */
goog.net.XhrLike.prototype.responseType;

/**
 * @type {Document}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute
 */
goog.net.XhrLike.prototype.responseXML;

/**
 * @type {number}
 * @see http://www.w3.org/TR/XMLHttpRequest/#readystate
 */
goog.net.XhrLike.prototype.readyState;

/**
 * @type {number}
 * @see http://www.w3.org/TR/XMLHttpRequest/#status
 */
goog.net.XhrLike.prototype.status;

/**
 * @type {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#statustext
 */
goog.net.XhrLike.prototype.statusText;

/**
 * @param {string} method
 * @param {string} url
 * @param {?boolean=} opt_async
 * @param {?string=} opt_user
 * @param {?string=} opt_password
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-open()-method
 */
goog.net.XhrLike.prototype.open = function(
    method, url, opt_async, opt_user, opt_password) {};

/**
 * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} opt_data
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-send()-method
 */
goog.net.XhrLike.prototype.send = function(opt_data) {};

/**
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method
 */
goog.net.XhrLike.prototype.abort = function() {};

/**
 * @param {string} header
 * @param {string} value
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method
 */
goog.net.XhrLike.prototype.setRequestHeader = function(header, value) {};

/**
 * @param {string} header
 * @return {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
 */
goog.net.XhrLike.prototype.getResponseHeader = function(header) {};

/**
 * @return {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method
 */
goog.net.XhrLike.prototype.getAllResponseHeaders = function() {};
<commit_msg>RELNOTES[INC]: XhrLike.prototype.getResponseHeader now returns a nullable string, per the spec.<commit_after>/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('goog.net.XhrLike');

/**
 * Interface for the common parts of XMLHttpRequest.
 *
 * Mostly copied from externs/w3c_xml.js.
 *
 * @interface
 * @see http://www.w3.org/TR/XMLHttpRequest/
 */
goog.net.XhrLike = function() {};

/**
 * Typedef that refers to either native or custom-implemented XHR objects.
 * @typedef {!goog.net.XhrLike|!XMLHttpRequest}
 */
goog.net.XhrLike.OrNative;

/**
 * @type {function()|null|undefined}
 * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange
 */
goog.net.XhrLike.prototype.onreadystatechange;

/**
 * @type {?ArrayBuffer|?Blob|?Document|?Object|?string}
 * @see https://xhr.spec.whatwg.org/#response-object
 */
goog.net.XhrLike.prototype.response;

/**
 * @type {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute
 */
goog.net.XhrLike.prototype.responseText;

/**
 * @type {string}
 * @see https://xhr.spec.whatwg.org/#the-responsetype-attribute
 */
goog.net.XhrLike.prototype.responseType;

/**
 * @type {Document}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute
 */
goog.net.XhrLike.prototype.responseXML;

/**
 * @type {number}
 * @see http://www.w3.org/TR/XMLHttpRequest/#readystate
 */
goog.net.XhrLike.prototype.readyState;

/**
 * @type {number}
 * @see http://www.w3.org/TR/XMLHttpRequest/#status
 */
goog.net.XhrLike.prototype.status;

/**
 * @type {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#statustext
 */
goog.net.XhrLike.prototype.statusText;

/**
 * @param {string} method
 * @param {string} url
 * @param {?boolean=} opt_async
 * @param {?string=} opt_user
 * @param {?string=} opt_password
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-open()-method
 */
goog.net.XhrLike.prototype.open = function(
    method, url, opt_async, opt_user, opt_password) {};

/**
 * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} opt_data
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-send()-method
 */
goog.net.XhrLike.prototype.send = function(opt_data) {};

/**
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method
 */
goog.net.XhrLike.prototype.abort = function() {};

/**
 * @param {string} header
 * @param {string} value
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method
 */
goog.net.XhrLike.prototype.setRequestHeader = function(header, value) {};

/**
 * @param {string} header
 * @return {?string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method
 */
goog.net.XhrLike.prototype.getResponseHeader = function(header) {};

/**
 * @return {string}
 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method
 */
goog.net.XhrLike.prototype.getAllResponseHeaders = function() {};
<|endoftext|>