function loadJSONMergeFile(storage, url, successCallback, errorCallback) {
  loadURL(url, "json", function(json) {
    storage.getAll(null, function(objs) {
      successCallback(objs, json);
    }, errorCallback);
  }, errorCallback);
}

/**
 * Merges the descr from a JSON file into the objs.
 *
 * This allows to keep the DB file small,
 * by keeping the descr in a separate file.
 *
 * @param objs {Array of Detail}   Objects to be enhanced.
 *     They will be modified in-place.
 * @param json {JSON}   Format { ID : descr, ...}, e.g.
 * { "@I00003" : "At the....", "@I0004" : "He was..." }
 *
 */
function mergeDescrFromJSON(objs, json) {
  for (var i = 0; i < objs.length; i++) {
    var obj = objs[i];
    var descr = json[obj.id];
    if (descr && !obj.descr) {
      obj.descr = descr;
    }
  }
}

/**
 * Creates a JSON file populated with the descr of the objs.
 *
 * This effectively filters the original insight book to only those entries
 * that we have in the DB and that we could match.
 *
 * Format { ID : descr, ...}, e.g.
 * { "@I00003" : "At the....", "@I0004" : "He was..." }
 *
 * @param storage {Storage}
 * @param successCallback {Function(result {JSON as String})}
 */
function exportDescrToJSON(storage, successCallback, errorCallback) {
  var json = {};
  storage.iterate(function(obj) {
    if (obj.descr) {
      json[obj.id] = obj.descr;
    }
  }, function() {
    var jsonStr = JSON.stringify(json, null, " ");
    successCallback(jsonStr);
  }, errorCallback);
}


/**
 * Merges the descr from a JSON file into the objs.
 *
 * This allows to keep the DB file small,
 * by keeping the descr in a separate file.
 *
 * @param targetStorage {Storage}   Objects to be enhanced.
 *     They will be modified in-place.
 * @param sourceURL {URL to native JSON}   Objects to be added
 * @param successCallback {Function({Array of Objects})}   Objects that have been added
 *
 */
function mergeAdditionalObjects(targetStorage, sourceURL,
    successCallback, errorCallback) {
  var sourceObjects = [];
  new LoadNativeJSON().loadFromURL(sourceURL, function(sourceStorage) {
    sourceStorage.iterate(function(detail) {
      sourceObjects.push(detail);
      var existingDetail = targetStorage.getID(detail.id);
      if (existingDetail) {
        // add new descr to existing obj
        if (detail.descr && !existingDetail.descr) {
          existingDetail.descr = detail.descr;
        }
      } else {
        targetStorage.add(detail);
      }
      var sourceRelations = detail.relations;
      detail.relations = [];
      sourceRelations.forEach(function(sourceRel) {
        var subj = existingDetail || detail;
        var obj = targetStorage.getID(sourceRel.obj.id) || sourceRel.obj;
        var rel = subj.addRelation(obj, sourceRel.type);
        //rel.opposite().add();
      });
    }, function() {
      successCallback(sourceObjects);
    }, errorCallback);
  }, errorCallback);
}
