<commit_before>var request = require('request'),
  terraformer = require('terraformer'),
  terraformerParser = require('terraformer-arcgis-parser'),
  extend = require('node.extend'),
  sm = require('sphericalmercator'),
  merc = new sm({size:256}),
  fs = require('fs'),
  base = require('../../base/controller.js');

// inherit from base controller
var Controller = extend({

  register: function(req, res){
    if ( !req.body.host ){
      res.send('Must provide a host to register:', 500);
    } else {
      AGOL.register( req.body.id, req.body.host, function(err, id){
        if (err) {
          res.send( err, 500);
        } else {
          res.json({ 'serviceId': id });
        }
    });
    }
  },

  list: function(req, res){
    AGOL.find(null, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        res.json( data );
      }
    });
  },

  find: function(req, res){
    AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        res.json( data );
      }
    });
  },

  findItem: function(req, res){
    if (req.params.format){
      Controller.findItemData(req, res);
    } else {
      AGOL.find(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          // Get the item
          AGOL.getItem( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              res.json( itemJson );
            }
          });
        }
      });
    }
  },

  findItemData: function(req, res){
    var _get = function(id, item, options, callback){
       AGOL.find( id, function( err, data ){
        if (err) {
          callback(err, null);
        } else {
          // Get the item
          if ( !parseInt(options.layer) ){
            options.layer = 0;
          }
          AGOL.getItemData( data.host, item, options, function(error, itemJson){
            if (error) {
              callback( error, null);
            } else if ( itemJson.koop_status ) {
              // return w/202
              res.json( { status: 'processing' }, 202);
            } else {
              if (itemJson.data[0].features.length > 1000){
                itemJson.data[0].features = itemJson.data[0].features.splice(0,1000);
              }
              callback( null, itemJson );
            }
          });
        }
      });
    };

    // if we have a layer then append it to the query params
    if ( req.params.layer ) {
      req.query.layer = req.params.layer;
    }

    // check format for exporting data
    if ( req.params.format ){
      // build the file key and look for the file
      //var key = [req.params.id, req.params.item, parseInt(req.query.layer) || 0 ].join(':');
      var key = [ 'agol', req.params.item, parseInt(req.query.layer) || 0 ].join(':');
      var fileName = [sails.config.data_dir + 'files', key, key + '.' + req.params.format].join('/');

      if (fs.existsSync( fileName )){
        res.sendfile( fileName );
      } else {

        // check the koop status table to see if we have a job running
          // if we do then return
          // else proceed
        req.query.format = req.params.format;
        _get(req.params.id, req.params.item, req.query, function( err, itemJson ){
          //console.log(itemJson.data[req.query.layer || 0]);
          if (err){
            res.send(err, 500 );
          } else if ( !itemJson.data[0].features.length ){
            res.send( 'No features exist for the requested FeatureService layer', 500 );
          } else {
            Exporter.exportToFormat( req.params.format, key, itemJson.data[0], function(err, result){
              if (err) {
                res.send( err, 500 );
              } else {
                res.sendfile( result );
              }
            });
          }
        });
      }

    } else {
      // get the esri json data for the service
      _get(req.params.id, req.params.item, req.query, function( err, itemJson ){
          if (err) {
            res.send( err, 500 );
          } else {
            res.send( itemJson );
          }
      });
    }
  },

  del: function(req, res){
    if ( !req.params.id ){
      res.send( 'Must specify a service id', 500 );
    } else {
      AGOL.remove(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          res.json( data );
        }
      });
    }
  },

  featureserver: function( req, res ){
    var callback = req.query.callback;
    delete req.query.callback;

    if (!req.params.layer){
      req.query.layer = 0;
    }

    AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        // Get the item
        AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
          if (error) {
            res.send( error, 500);
          } else {
            //GeoJSON.fromEsri( {features: itemJson.data.features}, function(err, geojson){
            //  if ( !geojson.length ) {
            //    geojson = [geojson];
            //  }
              // pass to the shared logic for FeatureService routing
              Controller._processFeatureServer( req, res, err, itemJson.data, callback);
            //});
          }
        });
      }
    });

  },

  thumbnail: function(req, res){

     AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {

        // check the image first and return if exists
        var key = ['agol', req.params.id, req.params.item, (req.params.layer || 0)].join(':');
        var dir = sails.config.data_dir + '/thumbs/';
        req.query.width = parseInt( req.query.width ) || 150;
        req.query.height = parseInt( req.query.height ) || 150;
        req.query.f_base = dir + key + '/' + req.query.width + '::' + req.query.height;
        // var png = req.query.f_base+'.png';

        var fileName = Thumbnail.exists(key, req.query);
        if ( fileName ){
          res.sendfile( fileName );
        } else {

          // if we have a layer then pass it along
          if ( req.params.layer ) {
            req.query.layer = req.params.layer;
          }

          // Get the item
          AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              GeoJSON.fromEsri({features: itemJson.data.features}, function(err, geojson){
                req.query.cache = false;

                if ( itemJson.extent ){
                  req.query.extent = {
                    xmin: itemJson.extent[0][0],
                    ymin: itemJson.extent[0][1],
                    xmax: itemJson.extent[1][0],
                    ymax: itemJson.extent[1][1]
                  };
                }

                // generate a thumbnail
                Thumbnail.generate( geojson, key, req.query, function(err, file){
                  if (err){
                    res.send(err, 500);
                  } else {
                    // send back image
                    res.sendfile( file );
                  }
                });

              });
            }
          });
        }
      }
    });

  },

  preview: function(req, res){
   res.view('demo/agol', { locals:{ host: req.params.id, item: req.params.item } });
  },

  tiles: function( req, res ){
    var key,
      layer = req.params.layer || 0;

    var _send = function( err, data ){
      req.params.key = key + ':' + layer;
        GeoJSON.fromEsri( data, function(err, geojson){
          Tiles.get( req.params, geojson, function(err, tile){
            if ( req.params.format == 'png'){
              res.sendfile( tile );
            } else {
              res.send( tile );
            }
          });
        });
    }

    // build the geometry from z,x,y
    var bounds = merc.bbox( req.params.x, req.params.y, req.params.z );
    req.query.geometry = {
        xmin: bounds[0],
        ymin: bounds[1],
        xmax: bounds[2],
        ymax: bounds[3],
        spatialReference: { wkid: 4326 }
    };

    var _sendImmediate = function( file ){
      if ( req.params.format == 'png'){
        res.sendfile( file );
      } else {
        res.sendfile( file );
      }
    };

    key = ['agol', req.params.id, req.params.item].join(':');
    var file = sails.config.data_dir + 'tiles/';
      file += key + ':' + layer + '/' + req.params.format;
      file += '/' + req.params.z + '/' + req.params.x + '/' + req.params.y + '.' + req.params.format;

    if ( !fs.existsSync( file ) ) {
      sails.config.log.info('NO tile in cache, go get the data', file);
      AGOL.find(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          // if we have a layer then pass it along
          if ( req.params.layer ) {
            req.query.layer = req.params.layer;
          }
          // Get the item
          AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              _send(error, itemJson.data);
            }
          });
        }
      });
    } else {
      _sendImmediate(file);
    }
  }

}, base);

module.exports = Controller;
<commit_msg>adding md5 hash to filenames<commit_after>var request = require('request'),
  terraformer = require('terraformer'),
  terraformerParser = require('terraformer-arcgis-parser'),
  extend = require('node.extend'),
  sm = require('sphericalmercator'),
  merc = new sm({size:256}),
  crypto = require('crypto'),
  fs = require('fs'),
  base = require('../../base/controller.js');

// inherit from base controller
var Controller = extend({

  register: function(req, res){
    if ( !req.body.host ){
      res.send('Must provide a host to register:', 500);
    } else {
      AGOL.register( req.body.id, req.body.host, function(err, id){
        if (err) {
          res.send( err, 500);
        } else {
          res.json({ 'serviceId': id });
        }
    });
    }
  },

  list: function(req, res){
    AGOL.find(null, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        res.json( data );
      }
    });
  },

  find: function(req, res){
    AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        res.json( data );
      }
    });
  },

  findItem: function(req, res){
    if (req.params.format){
      Controller.findItemData(req, res);
    } else {
      AGOL.find(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          // Get the item
          AGOL.getItem( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              res.json( itemJson );
            }
          });
        }
      });
    }
  },

  findItemData: function(req, res){
    var _get = function(id, item, options, callback){
       AGOL.find( id, function( err, data ){
        if (err) {
          callback(err, null);
        } else {
          // Get the item
          if ( !parseInt(options.layer) ){
            options.layer = 0;
          }
          AGOL.getItemData( data.host, item, options, function(error, itemJson){
            if (error) {
              callback( error, null);
            } else if ( itemJson.koop_status ) {
              // return w/202
              res.json( { status: 'processing' }, 202);
            } else {
              if (itemJson.data[0].features.length > 1000){
                itemJson.data[0].features = itemJson.data[0].features.splice(0,1000);
              }
              callback( null, itemJson );
            }
          });
        }
      });
    };

    // check format for exporting data
    if ( req.params.format ){
      // build the file key as an MD5 hash that's a join on the paams and look for the file
      var toHash = req.params.item + '_' + ( req.params.layer || 0 ) + JSON.stringify( req.query );
      var key = crypto.createHash('md5').update(toHash).digest('hex');
      console.log(toHash, key);

      var fileName = [sails.config.data_dir + 'files', agol+item, key + '.' + req.params.format].join('/');

      // if we have a layer then append it to the query params
      if ( req.params.layer ) {
        req.query.layer = req.params.layer;
      }

      if (fs.existsSync( fileName )){
        res.sendfile( fileName );
      } else {

        // check the koop status table to see if we have a job running
          // if we do then return
          // else proceed
        req.query.format = req.params.format;
        _get(req.params.id, req.params.item, req.query, function( err, itemJson ){
          //console.log(itemJson.data[req.query.layer || 0]);
          if (err){
            res.send(err, 500 );
          } else if ( !itemJson.data[0].features.length ){
            res.send( 'No features exist for the requested FeatureService layer', 500 );
          } else {
            Exporter.exportToFormat( req.params.format, key, itemJson.data[0], function(err, result){
              if (err) {
                res.send( err, 500 );
              } else {
                res.sendfile( result );
              }
            });
          }
        });
      }

    } else {
      // get the esri json data for the service
      _get(req.params.id, req.params.item, req.query, function( err, itemJson ){
          if (err) {
            res.send( err, 500 );
          } else {
            res.send( itemJson );
          }
      });
    }
  },

  del: function(req, res){
    if ( !req.params.id ){
      res.send( 'Must specify a service id', 500 );
    } else {
      AGOL.remove(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          res.json( data );
        }
      });
    }
  },

  featureserver: function( req, res ){
    var callback = req.query.callback;
    delete req.query.callback;

    if (!req.params.layer){
      req.query.layer = 0;
    }

    AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {
        // Get the item
        AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
          if (error) {
            res.send( error, 500);
          } else {
            //GeoJSON.fromEsri( {features: itemJson.data.features}, function(err, geojson){
            //  if ( !geojson.length ) {
            //    geojson = [geojson];
            //  }
              // pass to the shared logic for FeatureService routing
              Controller._processFeatureServer( req, res, err, itemJson.data, callback);
            //});
          }
        });
      }
    });

  },

  thumbnail: function(req, res){

     AGOL.find(req.params.id, function(err, data){
      if (err) {
        res.send( err, 500);
      } else {

        // check the image first and return if exists
        var key = ['agol', req.params.id, req.params.item, (req.params.layer || 0)].join(':');
        var dir = sails.config.data_dir + '/thumbs/';
        req.query.width = parseInt( req.query.width ) || 150;
        req.query.height = parseInt( req.query.height ) || 150;
        req.query.f_base = dir + key + '/' + req.query.width + '::' + req.query.height;
        // var png = req.query.f_base+'.png';

        var fileName = Thumbnail.exists(key, req.query);
        if ( fileName ){
          res.sendfile( fileName );
        } else {

          // if we have a layer then pass it along
          if ( req.params.layer ) {
            req.query.layer = req.params.layer;
          }

          // Get the item
          AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              GeoJSON.fromEsri({features: itemJson.data.features}, function(err, geojson){
                req.query.cache = false;

                if ( itemJson.extent ){
                  req.query.extent = {
                    xmin: itemJson.extent[0][0],
                    ymin: itemJson.extent[0][1],
                    xmax: itemJson.extent[1][0],
                    ymax: itemJson.extent[1][1]
                  };
                }

                // generate a thumbnail
                Thumbnail.generate( geojson, key, req.query, function(err, file){
                  if (err){
                    res.send(err, 500);
                  } else {
                    // send back image
                    res.sendfile( file );
                  }
                });

              });
            }
          });
        }
      }
    });

  },

  preview: function(req, res){
   res.view('demo/agol', { locals:{ host: req.params.id, item: req.params.item } });
  },

  tiles: function( req, res ){
    var key,
      layer = req.params.layer || 0;

    var _send = function( err, data ){
      req.params.key = key + ':' + layer;
        GeoJSON.fromEsri( data, function(err, geojson){
          Tiles.get( req.params, geojson, function(err, tile){
            if ( req.params.format == 'png'){
              res.sendfile( tile );
            } else {
              res.send( tile );
            }
          });
        });
    }

    // build the geometry from z,x,y
    var bounds = merc.bbox( req.params.x, req.params.y, req.params.z );
    req.query.geometry = {
        xmin: bounds[0],
        ymin: bounds[1],
        xmax: bounds[2],
        ymax: bounds[3],
        spatialReference: { wkid: 4326 }
    };

    var _sendImmediate = function( file ){
      if ( req.params.format == 'png'){
        res.sendfile( file );
      } else {
        res.sendfile( file );
      }
    };

    key = ['agol', req.params.id, req.params.item].join(':');
    var file = sails.config.data_dir + 'tiles/';
      file += key + ':' + layer + '/' + req.params.format;
      file += '/' + req.params.z + '/' + req.params.x + '/' + req.params.y + '.' + req.params.format;

    if ( !fs.existsSync( file ) ) {
      sails.config.log.info('NO tile in cache, go get the data', file);
      AGOL.find(req.params.id, function(err, data){
        if (err) {
          res.send( err, 500);
        } else {
          // if we have a layer then pass it along
          if ( req.params.layer ) {
            req.query.layer = req.params.layer;
          }
          // Get the item
          AGOL.getItemData( data.host, req.params.item, req.query, function(error, itemJson){
            if (error) {
              res.send( error, 500);
            } else {
              _send(error, itemJson.data);
            }
          });
        }
      });
    } else {
      _sendImmediate(file);
    }
  }

}, base);

module.exports = Controller;
<|endoftext|>


<commit_before>
$(document).ready(function() {
	update_credits();
	update_shares();
});

/*
	Create a block with given width and background color,
	and append it to the given bar element.
*/
function create_block(username, width, isShare) {
	var block = $(document.createElement('td'));
	block.css({
		'width': width + '%',
		'background-color': '#' + color_generator(username)
	});
	var percent = $(document.createElement('span'));
	percent.text(width + '%');

	if (isShare) {
		block.attr("id", "usage" + username);
		if (username == "Free" && width > 0) {
			var get = $(document.createElement('a'));
			get.attr('id','getresourcesbutton');
			get.attr('href', '../control/myvessels');
			get.text('get');
			// get.click(get_resources_dialog);
			block.append(get);

			var share = $(document.createElement('a'));
			share.attr('id','shareresourcesbutton');
			share.attr('href','#');
			share.text('share');
			share.click(share_resources_dialog);
			block.append(share);

		} else if (username != "Me" && username != "Others" ) {
			var close = $(document.createElement('a'));
			close.attr('href','#');
			close.text('x');
			close.click(function() {
				$.post("../control/ajax_editshare",
						{ username: username, percent: 0 },
						function (data) {
							var json = eval('(' + data + ')');
							if (json.success) {
								update_shares();
							} else {
								alert(json.error);
							}
						});
			});
			block.append(close);
			percent.attr("title", "Click to change percent");
			percent.tooltip({cssClass:"tooltip"});
			percent.css("cursor", "pointer");
			percent.css("text-decoration", "underline");
			percent.click(function() {
				change_percent(username, width);
			});
		}
	}

	if (username == "Others") {
		percent.attr("title", "Click to reveal");
		percent.tooltip({cssClass:"tooltip"});
		percent.css("cursor", "pointer");
		percent.css("text-decoration", "underline");
		percent.click(function () { toggle_table(isShare) });
		// block.attr("id", "creditOthers");
	}
	block.append(percent);
	return block;
}

/*
	Create a name label with given width and append it to
	the given name table.
*/
function create_label(username, width, isShare) {
	var label = $(document.createElement('td'));
	if (isShare) {
		label.attr("id", "labelusage" + username);
	}
	label.text(username);
	label.css({
		'width': width + '%'
	});
	return label;
}

function add_other(type, username, percent) {
	var table;
	if (type == "credits") {
		table = $("#creditotherstable");
	} else if (type == "shares") {
		table = $("#usageotherstable");
	}
	var tr = $(document.createElement("tr"));
	tr.html("" + username + "	" + percent + "	");
	if (type == "shares") {
		var control = $(document.createElement("td"));
		var edit = $(document.createElement("button"));
		edit.text("Edit");
		edit.click(function() {
			change_percent(username, percent);
		});
		var close = $(document.createElement("button"));
		close.text("Delete");
		close.click(function() {
			$.post("../control/ajax_editshare",
					{ username: username, percent: 0 },
					function(data) {
						var json = eval('(' + data + ')');
						if (json.success) {
							update_shares();
						} else {
							alert(json.error);
						}
					});
			tr.remove();
		});
		control.append(edit);
		control.append(close);
		tr.append(control);
	}
	table.append(tr);
}

/*
	Display the change percent dialog box
*/
function change_percent(username, current_percent) {
	var dialog = $(document.createElement("div"));
	dialog.attr("id", "changepercentdialog");
	dialog.html('
Change Percent
');
	var input = $(document.createElement("input"));
	input.attr("type", "text");
	input.val(current_percent);
	input.click(function () { $(this).val("") });
	var symbol = $(document.createElement("span"));
	symbol.html(" %
");
	var cancel = $(document.createElement("button"));
	cancel.text("Cancel");
	var save = $(document.createElement("button"));
	save.text("Save");
	cancel.click(function () {
		close_dialog();
		$(this).parent().remove();
	});
	save.click(function() {
		var new_percent = parseInt(input.val());
		if (validate(current_percent, new_percent)) {
			save_percent(username, new_percent);
		}
	});
	dialog.append(input);
	dialog.append(symbol);
	dialog.append(cancel);
	dialog.append(save);
	$("#dialogframe").append(dialog);
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#changepercentdialog").fadeIn("fast");
}

/*
	Save the percent of the the block when click "save" on the
	change_percent dialog box
*/
function save_percent(username, percent) {
	$.post("../control/ajax_editshare",
			{ username: username, percent: percent },
			function (data) {
				var json = eval(data);
				if (json.success) {
					$("#changepercentdialog").remove();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#changepercentdialog h3"));
				}
	   		},
			"json");
}

/*
	make sure the new percent is within range of validity
*/
function validate(current_percent, new_percent) {
	var free = parseInt($("#usageFree span").text());
	return (new_percent - current_percent < free) && (new_percent >= 0);
}

/*
	Toggle display the tables for small percent users
*/
function toggle_table(isShare) {
	if (isShare) {
		$("#usageotherstable").toggle();
	} else {
		$("#creditotherstable").toggle();
	}
}

function get_resources_dialog() {
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#getresourcesdialog").fadeIn("fast");
	$(".cancel").click(close_dialog);
	$("#getresourcesaction").click(get_resources);
}

function share_resources_dialog() {
	$("#shareresourcesdialog #username").val("");
	$("#shareresourcesdialog #percent").val("");
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#shareresourcesdialog").fadeIn("fast");
	$(".cancel").click(close_dialog);
	if ($("#shareresourcesdialog .warning")) {
	    $("#shareresourcesdialog .warning").remove();
	}
	$("#shareresources").click(function () {
		var username = $("#shareresourcesdialog #username").val();
		var percent = parseInt($("#shareresourcesdialog #percent").val());
		if (validate(0, percent)) {
			share_resources(username, percent);
		}
	});
}

function close_dialog() {
    if ($(this).parent().children(".warning")) {
		$(this).parent().children(".warning").remove();
    }
	$(this).parent().hide();
	$("#dialogframe").hide();
	$("#overlay").hide();
}

function post_ajax(url, args, func) {
	$.post(url, args, function(data) {
		// check data
		// if data ok then:
		func(data)
		// else:
		// display content as raw html in a new popup
	});
}

function share_resources(username, percent) {
	post_ajax("../control/ajax_createshare",
			{ username: username, percent: percent },
			function(data) {
				var json = eval('(' + data + ')');
				if (json.success) {
					$("#shareresourcesdialog").hide();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#shareresourcesdialog h3"));
				}
			});
}

function get_resources() {
	var numvessels = parseInt($("#numvessels").val());
	var env = parseInt($("#environment").val());
	if ($("#getresourcesdialog .warning")) {
	    $("#getresourcesdialog .warning").remove();
	}
	$.post("../control/ajax_getvessels",
			{ num: numvessels, env: env },
			function (data) {
				var json = eval('(' + data + ')');
				// alert(json.mypercent);
				// alert(json.success);
				if (json.success) {
					$("#getresourcesdialog").hide();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#getresourcesdialog h3"));
				}
			});
}

/*
	Create and insert a warning sign after the given position
*/

function create_warning(error, position) {
	var warning = $(document.createElement("p"));
	warning.html(error);
	warning.addClass("warning");
	warning.insertAfter(position);
}

/*
	Generate a color in hex notation for a username, which assigns different
	colors to different usernames.
*/
function color_generator(username) {
	var seeds = ['cc','ff'];
	var color = seeds[username.charCodeAt(0) % 2] +
				seeds[username.charCodeAt(1) % 2] +
				seeds[username.charCodeAt(username.length - 1) % 2];
	if (color == "ffffff") {
		color = "ffffcc";
	} else if (color == "cccccc") {
		color = "ccccff";
	}
	if (username == "Free") {
		color = "ffffff";
	} else if (username == "Me") {
		color = "cccccc";
	}
	return color;
}

function update_credits() {
	var loading = true;
	setTimeout(function () {
		if (loading) {
			$("#credits").html("	");
		}
	}, 2000);
	setTimeout(function () {
		if (loading) {
			$("#credits").html("It seems like it takes too long to load. Please refresh the page.");
		}
	}, 12000);
	$.post("../control/ajax_getcredits",
			function (data) {
				loading = false;
				$("#credits").empty();
				$("#creditnames").empty();
				$("#creditotherstable tr:gt(0)").empty();
				var json = eval('(' + data + ')');
				var total_others = 0;
				for (var i = 0; i < json[0].length; i++) {
					add_cell("credits", json[0][i].username, json[0][i].percent);
				}
				for (var i = 0; i < json[1].length; i++) {
					add_other("credits", json[1][i].username, json[1][i].percent);
					total_others += json[1][i].percent;
				}
				if (total_others > 0) {
					add_cell("credits", "Others", total_others);
				}
				add_cell("credits", json[2][0].username, json[2][0].percent);
				$("#vesselscredits").text(json[3] + " vessels credits");
				$("#creditotherstable tr:odd").addClass("odd");
				$("#creditotherstable tr:even").addClass("even");
			},
			"json");
}

function update_shares() {
	loading = true;
	setTimeout(function () {
		if (loading) {
			$("#usage").html("	");
		}
	}, 2000);
	setTimeout(function () {
		if (loading) {
			$("#usage").html("It seems like it takes too long to load. Please refresh the page.");
		}
	}, 12000);
	$.post("../control/ajax_getshares",
			function (data) {
				loading = false;
				$("#usage").empty();
				$("#usagenames").empty();
				$("#usageotherstable tr:gt(0)").empty();
				var json = eval('(' + data + ')');
				var total_percent = 0;
				var total_others = 0;
				for (var i = 0; i < json[0].length; i++) {
					add_cell("shares", json[0][i].username, json[0][i].percent);
					total_percent += json[0][i].percent;
				}
				for (var i = 0; i < json[1].length; i++) {
					add_other("shares", json[1][i].username, json[1][i].percent);
					total_others += json[1][i].percent;
				}
				total_percent += total_others + json[2][0].percent;
				if (total_others > 0) {
					add_cell("shares", "Others", total_others);
				} else {
					$("#usageotherstable").hide();
				}
				add_cell("shares", json[2][0].username, json[2][0].percent);
				add_cell("shares", "Free", 100 - total_percent);
				$("#vesselsavailable").text(json[3] + " vessels available");
				update_numvessels(json[3]);
				$("#usageotherstable tr:odd").addClass("odd");
				$("#usageotherstable tr:even").addClass("even");
			},
			"json");
}

function add_cell(type, username, percent) {
	if (type == "credits") {
		var block = create_block(username, percent, false);
		var label = create_label(username, percent, false);
		$("#credits").append(block);
		$("#creditnames").append(label);
	} else if (type == "shares") {
		var block = create_block(username, percent, true);
		var label = create_label(username, percent, true);
		$("#usage").append(block);
		$("#usagenames").append(label);
	}
}

function update_numvessels(number) {
	$("#numvessels").empty();
	for (var i = 1; i <= number; i++) {
		var option = $(document.createElement("option"));
		option.val(i);
		option.text(i);
		$("#numvessels").append(option);
	}
}
<commit_msg>debug loading <commit_after>
$(document).ready(function() {
	update_credits();
	update_shares();
});

/*
	Create a block with given width and background color,
	and append it to the given bar element.
*/
function create_block(username, width, isShare) {
	var block = $(document.createElement('td'));
	block.css({
		'width': width + '%',
		'background-color': '#' + color_generator(username)
	});
	var percent = $(document.createElement('span'));
	percent.text(width + '%');

	if (isShare) {
		block.attr("id", "usage" + username);
		if (username == "Free" && width > 0) {
			var get = $(document.createElement('a'));
			get.attr('id','getresourcesbutton');
			get.attr('href', '../control/myvessels');
			get.text('get');
			// get.click(get_resources_dialog);
			block.append(get);

			var share = $(document.createElement('a'));
			share.attr('id','shareresourcesbutton');
			share.attr('href','#');
			share.text('share');
			share.click(share_resources_dialog);
			block.append(share);

		} else if (username != "Me" && username != "Others" ) {
			var close = $(document.createElement('a'));
			close.attr('href','#');
			close.text('x');
			close.click(function() {
				$.post("../control/ajax_editshare",
						{ username: username, percent: 0 },
						function (data) {
							var json = eval('(' + data + ')');
							if (json.success) {
								update_shares();
							} else {
								alert(json.error);
							}
						});
			});
			block.append(close);
			percent.attr("title", "Click to change percent");
			percent.tooltip({cssClass:"tooltip"});
			percent.css("cursor", "pointer");
			percent.css("text-decoration", "underline");
			percent.click(function() {
				change_percent(username, width);
			});
		}
	}

	if (username == "Others") {
		percent.attr("title", "Click to reveal");
		percent.tooltip({cssClass:"tooltip"});
		percent.css("cursor", "pointer");
		percent.css("text-decoration", "underline");
		percent.click(function () { toggle_table(isShare) });
		// block.attr("id", "creditOthers");
	}
	block.append(percent);
	return block;
}

/*
	Create a name label with given width and append it to
	the given name table.
*/
function create_label(username, width, isShare) {
	var label = $(document.createElement('td'));
	if (isShare) {
		label.attr("id", "labelusage" + username);
	}
	label.text(username);
	label.css({
		'width': width + '%'
	});
	return label;
}

function add_other(type, username, percent) {
	var table;
	if (type == "credits") {
		table = $("#creditotherstable");
	} else if (type == "shares") {
		table = $("#usageotherstable");
	}
	var tr = $(document.createElement("tr"));
	tr.html("" + username + "	" + percent + "	");
	if (type == "shares") {
		var control = $(document.createElement("td"));
		var edit = $(document.createElement("button"));
		edit.text("Edit");
		edit.click(function() {
			change_percent(username, percent);
		});
		var close = $(document.createElement("button"));
		close.text("Delete");
		close.click(function() {
			$.post("../control/ajax_editshare",
					{ username: username, percent: 0 },
					function(data) {
						var json = eval('(' + data + ')');
						if (json.success) {
							update_shares();
						} else {
							alert(json.error);
						}
					});
			tr.remove();
		});
		control.append(edit);
		control.append(close);
		tr.append(control);
	}
	table.append(tr);
}

/*
	Display the change percent dialog box
*/
function change_percent(username, current_percent) {
	var dialog = $(document.createElement("div"));
	dialog.attr("id", "changepercentdialog");
	dialog.html('
Change Percent
');
	var input = $(document.createElement("input"));
	input.attr("type", "text");
	input.val(current_percent);
	input.click(function () { $(this).val("") });
	var symbol = $(document.createElement("span"));
	symbol.html(" %
");
	var cancel = $(document.createElement("button"));
	cancel.text("Cancel");
	var save = $(document.createElement("button"));
	save.text("Save");
	cancel.click(function () {
		close_dialog();
		$(this).parent().remove();
	});
	save.click(function() {
		var new_percent = parseInt(input.val());
		if (validate(current_percent, new_percent)) {
			save_percent(username, new_percent);
		}
	});
	dialog.append(input);
	dialog.append(symbol);
	dialog.append(cancel);
	dialog.append(save);
	$("#dialogframe").append(dialog);
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#changepercentdialog").fadeIn("fast");
}

/*
	Save the percent of the the block when click "save" on the
	change_percent dialog box
*/
function save_percent(username, percent) {
	$.post("../control/ajax_editshare",
			{ username: username, percent: percent },
			function (data) {
				var json = eval(data);
				if (json.success) {
					$("#changepercentdialog").remove();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#changepercentdialog h3"));
				}
	   		},
			"json");
}

/*
	make sure the new percent is within range of validity
*/
function validate(current_percent, new_percent) {
	var free = parseInt($("#usageFree span").text());
	return (new_percent - current_percent < free) && (new_percent >= 0);
}

/*
	Toggle display the tables for small percent users
*/
function toggle_table(isShare) {
	if (isShare) {
		$("#usageotherstable").toggle();
	} else {
		$("#creditotherstable").toggle();
	}
}

function get_resources_dialog() {
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#getresourcesdialog").fadeIn("fast");
	$(".cancel").click(close_dialog);
	$("#getresourcesaction").click(get_resources);
}

function share_resources_dialog() {
	$("#shareresourcesdialog #username").val("");
	$("#shareresourcesdialog #percent").val("");
	$("#dialogframe").fadeIn("fast");
	$("#overlay").fadeIn("fast");
	$("#shareresourcesdialog").fadeIn("fast");
	$(".cancel").click(close_dialog);
	if ($("#shareresourcesdialog .warning")) {
	    $("#shareresourcesdialog .warning").remove();
	}
	$("#shareresources").click(function () {
		var username = $("#shareresourcesdialog #username").val();
		var percent = parseInt($("#shareresourcesdialog #percent").val());
		if (validate(0, percent)) {
			share_resources(username, percent);
		}
	});
}

function close_dialog() {
    if ($(this).parent().children(".warning")) {
		$(this).parent().children(".warning").remove();
    }
	$(this).parent().hide();
	$("#dialogframe").hide();
	$("#overlay").hide();
}

function post_ajax(url, args, func) {
	$.post(url, args, function(data) {
		// check data
		// if data ok then:
		func(data)
		// else:
		// display content as raw html in a new popup
	});
}

function share_resources(username, percent) {
	post_ajax("../control/ajax_createshare",
			{ username: username, percent: percent },
			function(data) {
				var json = eval('(' + data + ')');
				if (json.success) {
					$("#shareresourcesdialog").hide();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#shareresourcesdialog h3"));
				}
			});
}

function get_resources() {
	var numvessels = parseInt($("#numvessels").val());
	var env = parseInt($("#environment").val());
	if ($("#getresourcesdialog .warning")) {
	    $("#getresourcesdialog .warning").remove();
	}
	$.post("../control/ajax_getvessels",
			{ num: numvessels, env: env },
			function (data) {
				var json = eval('(' + data + ')');
				// alert(json.mypercent);
				// alert(json.success);
				if (json.success) {
					$("#getresourcesdialog").hide();
					$("#dialogframe").hide();
					$("#overlay").hide();
					update_shares();
				} else {
					create_warning(json.error, $("#getresourcesdialog h3"));
				}
			});
}

/*
	Create and insert a warning sign after the given position
*/

function create_warning(error, position) {
	var warning = $(document.createElement("p"));
	warning.html(error);
	warning.addClass("warning");
	warning.insertAfter(position);
}

/*
	Generate a color in hex notation for a username, which assigns different
	colors to different usernames.
*/
function color_generator(username) {
	var seeds = ['cc','ff'];
	var color = seeds[username.charCodeAt(0) % 2] +
				seeds[username.charCodeAt(1) % 2] +
				seeds[username.charCodeAt(username.length - 1) % 2];
	if (color == "ffffff") {
		color = "ffffcc";
	} else if (color == "cccccc") {
		color = "ccccff";
	}
	if (username == "Free") {
		color = "ffffff";
	} else if (username == "Me") {
		color = "cccccc";
	}
	return color;
}

function update_credits() {
	var loading = true;
	setTimeout(function () {
		if (loading) {
			$("#credits").html("	");
		}
	}, 2000);
	setTimeout(function () {
		if (loading) {
			$("#credits").html("It seems like it takes too long to load. Please refresh the page.");
		}
	}, 12000);
	$.post("../control/ajax_getcreditsx",
			function (data) {
				loading = false;
				$("#credits").empty();
				$("#creditnames").empty();
				$("#creditotherstable tr:gt(0)").empty();
				var json = eval('(' + data + ')');
				var total_others = 0;
				for (var i = 0; i < json[0].length; i++) {
					add_cell("credits", json[0][i].username, json[0][i].percent);
				}
				for (var i = 0; i < json[1].length; i++) {
					add_other("credits", json[1][i].username, json[1][i].percent);
					total_others += json[1][i].percent;
				}
				if (total_others > 0) {
					add_cell("credits", "Others", total_others);
				}
				add_cell("credits", json[2][0].username, json[2][0].percent);
				$("#vesselscredits").text(json[3] + " vessels credits");
				$("#creditotherstable tr:odd").addClass("odd");
				$("#creditotherstable tr:even").addClass("even");
			},
			"json");
}

function update_shares() {
	loading = true;
	setTimeout(function () {
		if (loading) {
			$("#usage").html("	");
		}
	}, 2000);
	setTimeout(function () {
		if (loading) {
			$("#usage").html("It seems like it takes too long to load. Please refresh the page.");
		}
	}, 12000);
	$.post("../control/ajax_getsharesx",
			function (data) {
				loading = false;
				$("#usage").empty();
				$("#usagenames").empty();
				$("#usageotherstable tr:gt(0)").empty();
				var json = eval('(' + data + ')');
				var total_percent = 0;
				var total_others = 0;
				for (var i = 0; i < json[0].length; i++) {
					add_cell("shares", json[0][i].username, json[0][i].percent);
					total_percent += json[0][i].percent;
				}
				for (var i = 0; i < json[1].length; i++) {
					add_other("shares", json[1][i].username, json[1][i].percent);
					total_others += json[1][i].percent;
				}
				total_percent += total_others + json[2][0].percent;
				if (total_others > 0) {
					add_cell("shares", "Others", total_others);
				} else {
					$("#usageotherstable").hide();
				}
				add_cell("shares", json[2][0].username, json[2][0].percent);
				add_cell("shares", "Free", 100 - total_percent);
				$("#vesselsavailable").text(json[3] + " vessels available");
				update_numvessels(json[3]);
				$("#usageotherstable tr:odd").addClass("odd");
				$("#usageotherstable tr:even").addClass("even");
			},
			"json");
}

function add_cell(type, username, percent) {
	if (type == "credits") {
		var block = create_block(username, percent, false);
		var label = create_label(username, percent, false);
		$("#credits").append(block);
		$("#creditnames").append(label);
	} else if (type == "shares") {
		var block = create_block(username, percent, true);
		var label = create_label(username, percent, true);
		$("#usage").append(block);
		$("#usagenames").append(label);
	}
}

function update_numvessels(number) {
	$("#numvessels").empty();
	for (var i = 1; i <= number; i++) {
		var option = $(document.createElement("option"));
		option.val(i);
		option.text(i);
		$("#numvessels").append(option);
	}
}
<|endoftext|>


<commit_before>import Service from '../../db/service';

export default function tags(root, args) {
  const { query = "", geoQuery = null, offset = 0, limit = 18 } = args;

  if (geoQuery) {
    const coords = geoQuery.split(',');
    console.log('search near: ', coords[0], coords[1]);

    return Service.find({
      'location.coords': {
        $near: coords,
        $maxDistance: 5,
      },
      name: { $regex: `^${query}`, $options: 'i' },
    }).skip(offset).limit(limit);
  }

  return Service.find({ name: { $regex: `^${query}`, $options: 'i' } }).skip(offset).limit(limit);
}
<commit_msg>fix search bug<commit_after>import Service from '../../db/service';

export default function tags(root, args) {
  const { query = "", geoQuery = null, offset = 0, limit = 18 } = args;

  if (geoQuery) {
    const coords = geoQuery.split(',');
    console.log('search near: ', coords[0], coords[1]);

    return Service.find({
      'location.coords': {
        $near: coords,
        $maxDistance: 5,
      },
      name: { $regex: `${query}`, $options: 'i' },
    }).skip(offset).limit(limit);
  }

  return Service.find({ name: { $regex: `${query}`, $options: 'i' } }).skip(offset).limit(limit);
}
<|endoftext|>


<commit_before>module.exports = function (config) {

    config.set({
        files: [
            'jspm_packages/npm/zone.js@0.6.12/dist/zone.js',
            'jspm_packages/npm/reflect-metadata@0.1.3/Reflect.js'
        ],

        proxies: {
            '/index.ts': '/base/index.ts',
            '/math.ts': '/base/math.ts',
            '/aggregate.ts': '/base/aggregate.ts',
            '/boolean.ts': '/base/boolean.ts',
            '/array.ts': '/base/array.ts',
            '/string.ts': '/base/string.ts',
            '/object.ts': '/base/object.ts',
            '/test': '/base/test',
            '/src': '/base/src',
            '/jspm_packages': '/base/jspm_packages',
            '/node_modules': '/base/node_modules',
            '/tsconfig.json': '/base/tsconfig.json'
        },

        jspm: {
            serveFiles: [
                '*.ts',
                'src/**/*.ts',
                'test/**/*.ts',
                'tsconfig.json'
            ],
            loadFiles: [
                'test/main.spec.ts'
            ],
            packages: 'jspm_packages',
            config: 'jspm.config.js',
            browser: 'jspm.browser.js',
            paths: {
                "@angular/testing": "jspm_packages/npm/@angular/core@2.0.0-rc.1/testing.js",
            }
        },

        frameworks: ['jspm', 'jasmine'],
        browsers: ['Chrome'],
        plugins: [
            'karma-jspm',
            'karma-jasmine',
            'karma-spec-reporter',
            'karma-chrome-launcher',
            'karma-phantomjs-shim'
        ],

        reporters: ['spec'],
        singleRun: true,
        autoWatch: false,
        colors: true,
        logLevel: config.LOG_DEBUG
    })

}<commit_msg>Remove phantomjs config (for now)<commit_after>module.exports = function (config) {

    config.set({
        files: [
            'jspm_packages/npm/zone.js@0.6.12/dist/zone.js',
            'jspm_packages/npm/reflect-metadata@0.1.3/Reflect.js'
        ],

        proxies: {
            '/index.ts': '/base/index.ts',
            '/math.ts': '/base/math.ts',
            '/aggregate.ts': '/base/aggregate.ts',
            '/boolean.ts': '/base/boolean.ts',
            '/array.ts': '/base/array.ts',
            '/string.ts': '/base/string.ts',
            '/object.ts': '/base/object.ts',
            '/test': '/base/test',
            '/src': '/base/src',
            '/jspm_packages': '/base/jspm_packages',
            '/node_modules': '/base/node_modules',
            '/tsconfig.json': '/base/tsconfig.json'
        },

        jspm: {
            serveFiles: [
                '*.ts',
                'src/**/*.ts',
                'test/**/*.ts',
                'tsconfig.json'
            ],
            loadFiles: [
                'test/main.spec.ts'
            ],
            packages: 'jspm_packages',
            config: 'jspm.config.js',
            browser: 'jspm.browser.js',
            paths: {
                "@angular/testing": "jspm_packages/npm/@angular/core@2.0.0-rc.1/testing.js",
            }
        },

        frameworks: ['jspm', 'jasmine'],
        browsers: ['Chrome'],
        plugins: [
            'karma-jspm',
            'karma-jasmine',
            'karma-spec-reporter',
            'karma-chrome-launcher'
        ],

        reporters: ['spec'],
        singleRun: true,
        autoWatch: false,
        colors: true,
        logLevel: config.LOG_DEBUG
    })

}
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import OsfModel from './osf-model';

import FileItemMixin from 'ember-osf/mixins/file-item';

/**
 * @module ember-osf
 * @submodule models
 */

/**
 * Model for OSF APIv2 nodes. This model may be used with one of several API endpoints. It may be queried directly,
 *  or accessed via relationship fields.
 * For field and usage information, see:
 * * https://api.osf.io/v2/docs/#!/v2/Node_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Detail_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Children_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Linked_Nodes_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Forks_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/User_Nodes_GET
 * @class Node
 */
export default OsfModel.extend(FileItemMixin, {
    isNode: true,

    title: DS.attr('string'),
    description: DS.attr('string'),
    category: DS.attr('string'),

    currentUserPermissions: DS.attr('string'),

    fork: DS.attr('boolean'),
    collection: DS.attr('boolean'),
    registration: DS.attr('boolean'),
    public: DS.attr('boolean'),

    dateCreated: DS.attr('date'),
    dateModified: DS.attr('date'),

    tags: DS.attr(),

    templateFrom: DS.attr('string'),

    parent: DS.belongsTo('node', {
        inverse: 'children'
    }),
    children: DS.hasMany('nodes', {
        inverse: 'parent'
    }),
    preprints: DS.hasMany('preprints', {
        inverse: null
    }),
    affiliatedInstitutions: DS.hasMany('institutions', {
        inverse: 'nodes'
    }),
    comments: DS.hasMany('comments'),
    contributors: DS.hasMany('contributors', {
        allowBulkUpdate: true,
        allowBulkRemove: true,
        inverse: 'node'
    }),

    files: DS.hasMany('file-provider'),
    //forkedFrom: DS.belongsTo('node'),
    nodeLinks: DS.hasMany('node-links', {
        inverse: null
    }),
    registrations: DS.hasMany('registrations', {
        inverse: 'registeredFrom'
    }),

    draftRegistrations: DS.hasMany('draft-registrations', {
        inverse: 'branchedFrom'
    }),

    root: DS.belongsTo('node', {
        inverse: null
    }),
    logs: DS.hasMany('logs'),

    // These are only computeds because maintaining separate flag values on different classes would be a headache TODO: Improve.
    /**
     * Is this a project? Flag can be used to provide template-specific behavior for different resource types.
     * @property isProject
     * @type boolean
     */
    isProject: Ember.computed.equal('constructor.modelName', 'node'),
    /**
     * Is this a registration? Flag can be used to provide template-specific behavior for different resource types.
     * @property isRegistration
     * @type boolean
     */
    isRegistration: Ember.computed.equal('constructor.modelName', 'registration'),

    /**
     * Is this node being viewed through an anonymized, view-only link?
     * @property isAnonymous
     * @type boolean
     */
    isAnonymous: Ember.computed.bool('meta.anonymous'),

    /**
     * Determine whether the specified user ID is a contributor on this node
     * @method isContributor
     * @param {String} userId
     * @returns {boolean} Whether the specified user is a contributor on this node
     */
    isContributor(userId) {
        // Return true if there is at least one matching contributor for this user ID
        if (!userId) {
            return new Ember.RSVP.Promise((resolve) => resolve(false));
        }
        var contribId = `${this.get('id')}-${userId}`;
        return this.store.findRecord('contributor', contribId).then(() => true, () => false);
    },

    save() {
        // Some duplicate logic from osf-model#save needed to support
        // contributor edits being saved through the node
        // Note: order is important here so _dirtyRelationships gets set by the _super call
        var promise = this._super(...arguments);
        if (!this.get('_dirtyRelationships.contributors')) {
            this.set('_dirtyRelationships.contributors', {});
        }

        var contributors = this.hasMany('contributors').hasManyRelationship;
        this.set(
            '_dirtyRelationships.contributors.update',
            contributors.members.list.filter(m => !m.record.get('isNew') && Object.keys(m.record.changedAttributes()).length > 0)
        );
        // Need to included created contributors even in relationship
        // hasLoaded is false
        this.set(
            '_dirtyRelationships.contributors.create',
            contributors.members.list.filter(m => m.record.get('isNew'))
        );
        // Contributors are a 'real' delete, not just a de-reference
        this.set(
            '_dirtyRelationships.contributors.delete',
            this.get('_dirtyRelationships.contributors.remove') || []
        );
        this.set('_dirtyRelationships.contributors.remove', []);
        return promise;
    },

    addChild(title, description, category) {
        let child = this.store.createRecord('node', {
            title: title,
            category: category || 'project',
            description: description || null,
            parent: this
        });

        return child.save();
    },

    addContributor(userId, permission, isBibliographic, sendEmail, fullName, email) {
        let contrib = this.store.createRecord('contributor', {
            permission: permission,
            bibliographic: isBibliographic,
            sendEmail: sendEmail,
            nodeId: this.get('id'),
            userId: userId,
            fullName: fullName,
            email: email
        });

        return contrib.save();
    },

    addContributors(contributors, sendEmail) {
        let payload = contributors.map(contrib => {
            let c = this.store.createRecord('contributor', {
                permission: contrib.permission,
                bibliographic: contrib.bibliographic,
                nodeId: this.get('id'),
                userId: contrib.userId,
                id: this.get('id') + '-' + contrib.userId,
                unregisteredContributor: null
            });
            return c.serialize({
                includeId: true,
                includeUser: true
            }).data;
        });

        var emailQuery = '';
        if (sendEmail === false) {
            emailQuery = '?send_email=false';
        }

        // TODO Get this working properly - should not be an ajax request in the future.
        return this.store.adapterFor('contributor').ajax(this.get('links.relationships.contributors.links.related.href') + emailQuery, 'POST', {
            data: {
                data: payload
            },
            isBulk: true
        }).then(resp => {
            this.store.pushPayload(resp);
            var createdContribs = Ember.A();
            resp.data.map((contrib) => {
                createdContribs.push(this.store.peekRecord('contributor', contrib.id));
            });
            return createdContribs;
        });
    },

    removeContributor(contributor) {
        return contributor.destroyRecord();
    },

    updateContributor(contributor, permissions, bibliographic) {
        if (!Ember.isEmpty(permissions))
            contributor.set('permission', permissions);
        if (!Ember.isEmpty(bibliographic))
            contributor.set('bibliographic', bibliographic);
        return contributor.save();
    },

    updateContributors(contributors, permissionsChanges, bibliographicChanges) {
        let payload = contributors
            .filter(contrib => contrib.id in permissionsChanges || contrib.id in bibliographicChanges)
            .map(contrib => {
                if (contrib.id in permissionsChanges) {
                    contrib.set('permission', permissionsChanges[contrib.id]);
                }

                if (contrib.id in bibliographicChanges) {
                    contrib.set('bibliographic', bibliographicChanges[contrib.id]);
                }

                return contrib.serialize({
                    includeId: true,
                    includeUser: false
                }).data;
            });

        return this.store.adapterFor('contributor').ajax(this.get('links.relationships.contributors.links.related.href'), 'PATCH', {
            data: {
                data: payload
            },
            isBulk: true
        }).then(resp => {
            this.store.pushPayload(resp);
            return contributors;
        });
    }
});
<commit_msg>Define preprints inverse.<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import OsfModel from './osf-model';

import FileItemMixin from 'ember-osf/mixins/file-item';

/**
 * @module ember-osf
 * @submodule models
 */

/**
 * Model for OSF APIv2 nodes. This model may be used with one of several API endpoints. It may be queried directly,
 *  or accessed via relationship fields.
 * For field and usage information, see:
 * * https://api.osf.io/v2/docs/#!/v2/Node_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Detail_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Children_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Linked_Nodes_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/Node_Forks_List_GET
 * * https://api.osf.io/v2/docs/#!/v2/User_Nodes_GET
 * @class Node
 */
export default OsfModel.extend(FileItemMixin, {
    isNode: true,

    title: DS.attr('string'),
    description: DS.attr('string'),
    category: DS.attr('string'),

    currentUserPermissions: DS.attr('string'),

    fork: DS.attr('boolean'),
    collection: DS.attr('boolean'),
    registration: DS.attr('boolean'),
    public: DS.attr('boolean'),

    dateCreated: DS.attr('date'),
    dateModified: DS.attr('date'),

    tags: DS.attr(),

    templateFrom: DS.attr('string'),

    parent: DS.belongsTo('node', {
        inverse: 'children'
    }),
    children: DS.hasMany('nodes', {
        inverse: 'parent'
    }),
    preprints: DS.hasMany('preprints', {
        inverse: 'node'
    }),
    affiliatedInstitutions: DS.hasMany('institutions', {
        inverse: 'nodes'
    }),
    comments: DS.hasMany('comments'),
    contributors: DS.hasMany('contributors', {
        allowBulkUpdate: true,
        allowBulkRemove: true,
        inverse: 'node'
    }),

    files: DS.hasMany('file-provider'),
    //forkedFrom: DS.belongsTo('node'),
    nodeLinks: DS.hasMany('node-links', {
        inverse: null
    }),
    registrations: DS.hasMany('registrations', {
        inverse: 'registeredFrom'
    }),

    draftRegistrations: DS.hasMany('draft-registrations', {
        inverse: 'branchedFrom'
    }),

    root: DS.belongsTo('node', {
        inverse: null
    }),
    logs: DS.hasMany('logs'),

    // These are only computeds because maintaining separate flag values on different classes would be a headache TODO: Improve.
    /**
     * Is this a project? Flag can be used to provide template-specific behavior for different resource types.
     * @property isProject
     * @type boolean
     */
    isProject: Ember.computed.equal('constructor.modelName', 'node'),
    /**
     * Is this a registration? Flag can be used to provide template-specific behavior for different resource types.
     * @property isRegistration
     * @type boolean
     */
    isRegistration: Ember.computed.equal('constructor.modelName', 'registration'),

    /**
     * Is this node being viewed through an anonymized, view-only link?
     * @property isAnonymous
     * @type boolean
     */
    isAnonymous: Ember.computed.bool('meta.anonymous'),

    /**
     * Determine whether the specified user ID is a contributor on this node
     * @method isContributor
     * @param {String} userId
     * @returns {boolean} Whether the specified user is a contributor on this node
     */
    isContributor(userId) {
        // Return true if there is at least one matching contributor for this user ID
        if (!userId) {
            return new Ember.RSVP.Promise((resolve) => resolve(false));
        }
        var contribId = `${this.get('id')}-${userId}`;
        return this.store.findRecord('contributor', contribId).then(() => true, () => false);
    },

    save() {
        // Some duplicate logic from osf-model#save needed to support
        // contributor edits being saved through the node
        // Note: order is important here so _dirtyRelationships gets set by the _super call
        var promise = this._super(...arguments);
        if (!this.get('_dirtyRelationships.contributors')) {
            this.set('_dirtyRelationships.contributors', {});
        }

        var contributors = this.hasMany('contributors').hasManyRelationship;
        this.set(
            '_dirtyRelationships.contributors.update',
            contributors.members.list.filter(m => !m.record.get('isNew') && Object.keys(m.record.changedAttributes()).length > 0)
        );
        // Need to included created contributors even in relationship
        // hasLoaded is false
        this.set(
            '_dirtyRelationships.contributors.create',
            contributors.members.list.filter(m => m.record.get('isNew'))
        );
        // Contributors are a 'real' delete, not just a de-reference
        this.set(
            '_dirtyRelationships.contributors.delete',
            this.get('_dirtyRelationships.contributors.remove') || []
        );
        this.set('_dirtyRelationships.contributors.remove', []);
        return promise;
    },

    addChild(title, description, category) {
        let child = this.store.createRecord('node', {
            title: title,
            category: category || 'project',
            description: description || null,
            parent: this
        });

        return child.save();
    },

    addContributor(userId, permission, isBibliographic, sendEmail, fullName, email) {
        let contrib = this.store.createRecord('contributor', {
            permission: permission,
            bibliographic: isBibliographic,
            sendEmail: sendEmail,
            nodeId: this.get('id'),
            userId: userId,
            fullName: fullName,
            email: email
        });

        return contrib.save();
    },

    addContributors(contributors, sendEmail) {
        let payload = contributors.map(contrib => {
            let c = this.store.createRecord('contributor', {
                permission: contrib.permission,
                bibliographic: contrib.bibliographic,
                nodeId: this.get('id'),
                userId: contrib.userId,
                id: this.get('id') + '-' + contrib.userId,
                unregisteredContributor: null
            });
            return c.serialize({
                includeId: true,
                includeUser: true
            }).data;
        });

        var emailQuery = '';
        if (sendEmail === false) {
            emailQuery = '?send_email=false';
        }

        // TODO Get this working properly - should not be an ajax request in the future.
        return this.store.adapterFor('contributor').ajax(this.get('links.relationships.contributors.links.related.href') + emailQuery, 'POST', {
            data: {
                data: payload
            },
            isBulk: true
        }).then(resp => {
            this.store.pushPayload(resp);
            var createdContribs = Ember.A();
            resp.data.map((contrib) => {
                createdContribs.push(this.store.peekRecord('contributor', contrib.id));
            });
            return createdContribs;
        });
    },

    removeContributor(contributor) {
        return contributor.destroyRecord();
    },

    updateContributor(contributor, permissions, bibliographic) {
        if (!Ember.isEmpty(permissions))
            contributor.set('permission', permissions);
        if (!Ember.isEmpty(bibliographic))
            contributor.set('bibliographic', bibliographic);
        return contributor.save();
    },

    updateContributors(contributors, permissionsChanges, bibliographicChanges) {
        let payload = contributors
            .filter(contrib => contrib.id in permissionsChanges || contrib.id in bibliographicChanges)
            .map(contrib => {
                if (contrib.id in permissionsChanges) {
                    contrib.set('permission', permissionsChanges[contrib.id]);
                }

                if (contrib.id in bibliographicChanges) {
                    contrib.set('bibliographic', bibliographicChanges[contrib.id]);
                }

                return contrib.serialize({
                    includeId: true,
                    includeUser: false
                }).data;
            });

        return this.store.adapterFor('contributor').ajax(this.get('links.relationships.contributors.links.related.href'), 'PATCH', {
            data: {
                data: payload
            },
            isBulk: true
        }).then(resp => {
            this.store.pushPayload(resp);
            return contributors;
        });
    }
});
<|endoftext|>


<commit_before>var Cap = require('../lib/Cap').Cap,
    decoders = require('../lib/Cap').decoders,
    PROTOCOL = decoders.PROTOCOL;

var path = require('path'),
    assert = require('assert'),
    http = require('http');

var t = -1,
    group = path.basename(__filename, '.js') + '/',
    timeout,
    localIP;

var tests = [
  { run: function() {
      var p = new Cap();
      var device = Cap.findDevice(localIP);
      var filter = [
        'tcp',
        'dst port 80',
        '(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) > 0)'
      ].join(' and ');
      var bufSize = 10 * 1024 * 1024;
      var buffer = new Buffer(65535);
      var linkType = p.open(device, filter, bufSize, buffer);
      var evCount = 0;

      p.setMinBytes && p.setMinBytes(0);
      p.once('packet', function(nbytes, trunc) {
        assert.strictEqual(nbytes > 0, true);
        p.close();
        var payload = getTCPPayload(buffer, linkType);
        assert.strictEqual(/^GET \/ HTTP\/1\.1/.test(payload), true);
        checkDone();
      });
      http.get('http://google.com', function(res) {
        res.once('end', checkDone);
        res.resume();
      });
      function checkDone() {
        if (++evCount === 2)
          next();
      }
    },
    what: 'Capture outbound HTTP traffic'
  },
];

function getTCPPayload(buffer, linkType) {
  var payload;
  if (linkType === 'ETHERNET') {
    var ret = decoders.Ethernet(buffer);
    if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
      ret = decoders.IPV4(buffer, ret.offset);
      if (ret.info.protocol === PROTOCOL.IP.TCP) {
        var datalen = ret.info.totallen - ret.hdrlen;
        ret = decoders.TCP(buffer, ret.offset);
        datalen -= ret.hdrlen;
        payload = buffer.toString('binary',
                                  ret.offset,
                                  ret.offset + datalen);
      }
    }
  }
  return payload;
}

function next() {
  clearTimeout(timeout);
  if (t === tests.length - 1)
    return;
  var v = tests[++t];
  timeout = setTimeout(function() {
    throw new Error('Capture timeout');
  }, 20 * 1000);
  v.run.call(v);
}

function makeMsg(msg, what) {
  return '[' + group + (what || tests[t].what) + ']: ' + msg;
}

process.once('uncaughtException', function(err) {
  if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
    console.error(makeMsg('Unexpected Exception:'));

  throw err;
}).once('exit', function() {
  assert(t === tests.length - 1,
         makeMsg('Only finished ' + (t + 1) + '/' + tests.length + ' tests',
                 '_exit'));
});

// Determine "primary" IP address
http.get('http://google.com', function(res) {
  localIP = res.socket.address().address;
  res.on('end', next);
  res.resume();
});
<commit_msg>test: add useful info to stdout<commit_after>var Cap = require('../lib/Cap').Cap,
    decoders = require('../lib/Cap').decoders,
    PROTOCOL = decoders.PROTOCOL;

var path = require('path'),
    assert = require('assert'),
    http = require('http');

var t = -1,
    group = path.basename(__filename, '.js') + '/',
    timeout,
    localIP;

var tests = [
  { run: function() {
      var p = new Cap();
      var device = Cap.findDevice(localIP);
      var filter = [
        'tcp',
        'dst port 80',
        '(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) > 0)'
      ].join(' and ');
      var bufSize = 10 * 1024 * 1024;
      var buffer = new Buffer(65535);
      var linkType = p.open(device, filter, bufSize, buffer);
      var evCount = 0;

      p.setMinBytes && p.setMinBytes(0);
      p.once('packet', function(nbytes, trunc) {
        assert.strictEqual(nbytes > 0, true);
        p.close();
        var payload = getTCPPayload(buffer, linkType);
        assert.strictEqual(/^GET \/ HTTP\/1\.1/.test(payload), true);
        checkDone();
      });
      http.get('http://google.com', function(res) {
        res.once('end', checkDone);
        res.resume();
      });
      function checkDone() {
        if (++evCount === 2)
          next();
      }
    },
    what: 'Capture outbound HTTP traffic'
  },
];

function getTCPPayload(buffer, linkType) {
  var payload;
  if (linkType === 'ETHERNET') {
    var ret = decoders.Ethernet(buffer);
    if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
      ret = decoders.IPV4(buffer, ret.offset);
      if (ret.info.protocol === PROTOCOL.IP.TCP) {
        var datalen = ret.info.totallen - ret.hdrlen;
        ret = decoders.TCP(buffer, ret.offset);
        datalen -= ret.hdrlen;
        payload = buffer.toString('binary',
                                  ret.offset,
                                  ret.offset + datalen);
      }
    }
  }
  return payload;
}

function next() {
  clearTimeout(timeout);
  if (t === tests.length - 1)
    return;
  var v = tests[++t];
  timeout = setTimeout(function() {
    throw new Error('Capture timeout');
  }, 20 * 1000);
  v.run.call(v);
}

function makeMsg(msg, what) {
  return '[' + group + (what || tests[t].what) + ']: ' + msg;
}

process.once('uncaughtException', function(err) {
  if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
    console.error(makeMsg('Unexpected Exception:'));

  throw err;
}).once('exit', function() {
  assert(t === tests.length - 1,
         makeMsg('Only finished ' + (t + 1) + '/' + tests.length + ' tests',
                 '_exit'));
});

// Determine "primary" IP address
http.get('http://google.com', function(res) {
  localIP = res.socket.address().address;
  console.log('localIP = %j', localIP);
  console.log('device list = %s', require('util').inspect(Cap.deviceList()));
  res.on('end', next);
  res.resume();
});
<|endoftext|>


<commit_before>/* @flow */
import React, { Component } from "react";
import defaultFormatter from "./defaultFormatter";
import dateParser from "./dateParser";

export type Unit =
  | "second"
  | "minute"
  | "hour"
  | "day"
  | "week"
  | "month"
  | "year";

export type Suffix = "ago" | "from now";

export type Formatter = (
  value: number,
  unit: Unit,
  suffix: Suffix,
  epochSeconds: number,
  nextFormatter?: Formatter
) => string | React$Element<any>;

export type Props = {
  /** If the component should update itself over time */
  +live: boolean,
  /** minimum amount of time in seceonds between re-renders */
  +minPeriod: number,
  /** Maximum time between re-renders in seconds.
   * The component should update at least once every `x` seconds
   */
  +maxPeriod: number,
  /** The container to render the string into.
   * You could use a string like `span` or a custom component
   */
  +component: string | ReactClass<Object> | Function,
  /**
   * A title used for setting the title attribute if a <time> Element is used.
   */
  +title?: string,
  /** A function to decide how to format the date.
   * If you use this, timeago is basically acting like a glorified
   * setInterval for you.
   */
  +formatter: Formatter,
  /** The Date to display. An actual Date object or something that can be fed
   * to new Date
   */
  +date: string | number | Date,
  /** A function that returns what Date.now would return. Primarily for server
   * rendering.
   */
  +now: () => number
};

type DefaultProps = {
  +live: boolean,
  +minPeriod: number,
  +maxPeriod: number,
  +component: string | ReactClass<Object> | Function,
  +formatter: Formatter,
  +now: () => number
};

export const MINUTE = 60;
export const HOUR = MINUTE * 60;
export const DAY = HOUR * 24;
export const WEEK = DAY * 7;
export const MONTH = DAY * 30;
export const YEAR = DAY * 365;

export default class TimeAgo extends Component<DefaultProps, Props, void> {
  static displayName = "TimeAgo";
  static defaultProps = {
    live: true,
    component: "time",
    minPeriod: 0,
    maxPeriod: Infinity,
    formatter: defaultFormatter,
    now: () => Date.now()
  };

  timeoutId: ?number;
  isStillMounted: boolean = false;

  tick = (refresh: ?boolean): void => {
    if (!this.isStillMounted || !this.props.live) {
      return;
    }

    const then = dateParser(this.props.date).valueOf();
    if (!then) {
      console.warn("[@nteract/timeago] Invalid Date provided");
      return;
    }

    const now = this.props.now();
    const seconds = Math.round(Math.abs(now - then) / 1000);

    const unboundPeriod =
      seconds < MINUTE
        ? 1000
        : seconds < HOUR ? 1000 * MINUTE : seconds < DAY ? 1000 * HOUR : 0;
    const period = Math.min(
      Math.max(unboundPeriod, this.props.minPeriod * 1000),
      this.props.maxPeriod * 1000
    );

    if (period) {
      this.timeoutId = setTimeout(this.tick, period);
    }

    if (!refresh) {
      this.forceUpdate();
    }
  };

  componentDidMount() {
    this.isStillMounted = true;
    if (this.props.live) {
      this.tick(true);
    }
  }

  componentDidUpdate(lastProps: Props) {
    if (
      this.props.live !== lastProps.live ||
      this.props.date !== lastProps.date
    ) {
      if (!this.props.live && this.timeoutId) {
        clearTimeout(this.timeoutId);
      }
      this.tick();
    }
  }

  componentWillUnmount() {
    this.isStillMounted = false;
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = undefined;
    }
  }

  render(): ?React$Element<*> {
    /* eslint-disable no-unused-vars */
    const {
      date,
      formatter,
      component: Komponent,
      live,
      minPeriod,
      maxPeriod,
      title,
      now,
      ...passDownProps
    } = this.props;
    /* eslint-enable no-unused-vars */
    const then = dateParser(date).valueOf();
    if (!then) {
      return null;
    }
    const timeNow = now();
    const seconds = Math.round(Math.abs(timeNow - then) / 1000);
    const suffix = then < timeNow ? "ago" : "from now";

    const [value, unit] =
      seconds < MINUTE
        ? [Math.round(seconds), "second"]
        : seconds < HOUR
          ? [Math.round(seconds / MINUTE), "minute"]
          : seconds < DAY
            ? [Math.round(seconds / HOUR), "hour"]
            : seconds < WEEK
              ? [Math.round(seconds / DAY), "day"]
              : seconds < MONTH
                ? [Math.round(seconds / WEEK), "week"]
                : seconds < YEAR
                  ? [Math.round(seconds / MONTH), "month"]
                  : [Math.round(seconds / YEAR), "year"];

    const passDownTitle =
      typeof title === "undefined"
        ? typeof date === "string"
          ? date
          : dateParser(date)
              .toISOString()
              .substr(0, 16)
              .replace("T", " ")
        : title;

    if (Komponent === "time") {
      passDownProps.dateTime = dateParser(date).toISOString();
    }

    const nextFormatter = defaultFormatter.bind(null, value, unit, suffix);

    return (
      <Komponent {...passDownProps} title={passDownTitle}>
        {this.props.formatter(value, unit, suffix, then, nextFormatter)}
      </Komponent>
    );
  }
}
<commit_msg>fix flowtypes for timeago<commit_after>/* @flow */
import * as React from "react";
import defaultFormatter from "./defaultFormatter";
import dateParser from "./dateParser";

export type Unit =
  | "second"
  | "minute"
  | "hour"
  | "day"
  | "week"
  | "month"
  | "year";

export type Suffix = "ago" | "from now";

export type Formatter = (
  value: number,
  unit: Unit,
  suffix: Suffix,
  epochSeconds: number,
  nextFormatter?: Formatter
) => string | React$Element<any>;

export type Props = {
  /** If the component should update itself over time */
  +live: boolean,
  /** minimum amount of time in seceonds between re-renders */
  +minPeriod: number,
  /** Maximum time between re-renders in seconds.
   * The component should update at least once every `x` seconds
   */
  +maxPeriod: number,
  /** The container to render the string into.
   * You could use a string like `span` or a custom component
   */
  +component: string | React.ComponentType<*> | Function,
  /**
   * A title used for setting the title attribute if a <time> Element is used.
   */
  +title?: string,
  /** A function to decide how to format the date.
   * If you use this, timeago is basically acting like a glorified
   * setInterval for you.
   */
  +formatter: Formatter,
  /** The Date to display. An actual Date object or something that can be fed
   * to new Date
   */
  +date: string | number | Date,
  /** A function that returns what Date.now would return. Primarily for server
   * rendering.
   */
  +now: () => number
};

type DefaultProps = {
  +live: boolean,
  +minPeriod: number,
  +maxPeriod: number,
  +component: string | React.ComponentType<*> | Function,
  +formatter: Formatter,
  +now: () => number
};

export const MINUTE = 60;
export const HOUR = MINUTE * 60;
export const DAY = HOUR * 24;
export const WEEK = DAY * 7;
export const MONTH = DAY * 30;
export const YEAR = DAY * 365;

export default class TimeAgo extends React.Component<Props, void> {
  static displayName = "TimeAgo";
  static defaultProps = {
    live: true,
    component: "time",
    minPeriod: 0,
    maxPeriod: Infinity,
    formatter: defaultFormatter,
    now: () => Date.now()
  };

  timeoutId: ?number;
  isStillMounted: boolean = false;

  tick = (refresh: ?boolean): void => {
    if (!this.isStillMounted || !this.props.live) {
      return;
    }

    const then = dateParser(this.props.date).valueOf();
    if (!then) {
      console.warn("[@nteract/timeago] Invalid Date provided");
      return;
    }

    const now = this.props.now();
    const seconds = Math.round(Math.abs(now - then) / 1000);

    const unboundPeriod =
      seconds < MINUTE
        ? 1000
        : seconds < HOUR ? 1000 * MINUTE : seconds < DAY ? 1000 * HOUR : 0;
    const period = Math.min(
      Math.max(unboundPeriod, this.props.minPeriod * 1000),
      this.props.maxPeriod * 1000
    );

    if (period) {
      this.timeoutId = setTimeout(this.tick, period);
    }

    if (!refresh) {
      this.forceUpdate();
    }
  };

  componentDidMount() {
    this.isStillMounted = true;
    if (this.props.live) {
      this.tick(true);
    }
  }

  componentDidUpdate(lastProps: Props) {
    if (
      this.props.live !== lastProps.live ||
      this.props.date !== lastProps.date
    ) {
      if (!this.props.live && this.timeoutId) {
        clearTimeout(this.timeoutId);
      }
      this.tick();
    }
  }

  componentWillUnmount() {
    this.isStillMounted = false;
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
      this.timeoutId = undefined;
    }
  }

  render(): ?React$Element<*> {
    /* eslint-disable no-unused-vars */
    const {
      date,
      formatter,
      component: Komponent,
      live,
      minPeriod,
      maxPeriod,
      title,
      now,
      ...passDownProps
    } = this.props;
    /* eslint-enable no-unused-vars */
    const then = dateParser(date).valueOf();
    if (!then) {
      return null;
    }
    const timeNow = now();
    const seconds = Math.round(Math.abs(timeNow - then) / 1000);
    const suffix = then < timeNow ? "ago" : "from now";

    const [value, unit] =
      seconds < MINUTE
        ? [Math.round(seconds), "second"]
        : seconds < HOUR
          ? [Math.round(seconds / MINUTE), "minute"]
          : seconds < DAY
            ? [Math.round(seconds / HOUR), "hour"]
            : seconds < WEEK
              ? [Math.round(seconds / DAY), "day"]
              : seconds < MONTH
                ? [Math.round(seconds / WEEK), "week"]
                : seconds < YEAR
                  ? [Math.round(seconds / MONTH), "month"]
                  : [Math.round(seconds / YEAR), "year"];

    const passDownTitle =
      typeof title === "undefined"
        ? typeof date === "string"
          ? date
          : dateParser(date)
              .toISOString()
              .substr(0, 16)
              .replace("T", " ")
        : title;

    let dateTime = null;
    if (Komponent === "time") {
      dateTime = dateParser(date).toISOString();
    }

    const nextFormatter = defaultFormatter.bind(null, value, unit, suffix);

    return (
      <Komponent {...passDownProps} dateTime={dateTime} title={passDownTitle}>
        {this.props.formatter(value, unit, suffix, then, nextFormatter)}
      </Komponent>
    );
  }
}
<|endoftext|>