<commit_before>/**
   Copyright 2011 Couchbase, Inc.

   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.
 **/
Cell.prototype.propagateMeta = function () {
  var metaCell = this.ensureMetaCell();
  var staleMetaCell;
  var sourceCells = this.getSourceCells();

  if (metaCell.cancelRefresh) {
    metaCell.cancelRefresh();
  }

  for (var i = sourceCells.length - 1; i >= 0; i--) {
    var cell = sourceCells[i].metaCell;
    if (!cell || !cell.value) {
      continue;
    }
    var metaValue = cell.value;
    if (metaValue.stale) {
      staleMetaCell = cell;
      break;
    }
  }

  metaCell.setValueAttr(!!staleMetaCell, 'stale');

  var refreshSlave = new Slave($m(this, 'propagateMeta'));

  if (staleMetaCell) {
    staleMetaCell.changedSlot.subscribeWithSlave(refreshSlave);
  } else {
    for (var j = sourceCells.length - 1; j >= 0; j--) {
      var cell = sourceCells[j].metaCell;
      if (!cell) {
        continue;
      }
      cell.changedSlot.subscribeWithSlave(refreshSlave);
    }
  }

  metaCell.cancelRefresh = function () {
    refreshSlave.die();
    for (var i = sourceCells.length - 1; i >= 0; i--) {
      var cell = sourceCells[i].metaCell;
      if (!cell) {
        continue;
      }
      cell.changedSlot.cleanup();
    }
  };
};

Cell.STANDARD_ERROR_MARK = {"this is error marker":true};

Cell.prototype.delegateInvalidationMethods = function (target) {
  var self = this;
  _.each(("recalculate recalculateAt recalculateAfterDelay invalidate").split(' '), function (methodName) {
    self[methodName] = $m(target, methodName);
  });
};

future.get = function (ajaxOptions, valueTransformer, nowValue, futureWrapper) {
  // var aborted = false;
  var options = {
    valueTransformer: valueTransformer,
    cancel: function () {
      // aborted = true;
      operation.cancel();
    },
    nowValue: nowValue
  };
  var operation;
  if (ajaxOptions.url === undefined) {
    throw new Error("url is undefined");
  }

  function initiateXHR(dataCallback) {
    var opts = {dataType: 'json',
                prepareReGet: function (opts) {
                  if (opts.errorMarker) {
                    // if we have error marker pass it to data
                    // callback now to mark error
                    if (!dataCallback.continuing(opts.errorMarker)) {
                      // if we were cancelled, cancel IOCenter
                      // operation
                      operation.cancel();
                    }
                  }
                },
                success: dataCallback};
    if (ajaxOptions.onError) {
      opts.error = function () {
        ajaxOptions.onError.apply(this, [dataCallback].concat(_.toArray(arguments)));
      };
    }
    operation = IOCenter.performGet(_.extend(opts, ajaxOptions));
  }
  return (futureWrapper || future)(initiateXHR, options);
};

future.withEarlyTransformer = function (earlyTransformer) {
  return {
    // we return a function
    get: function (ajaxOptions, valueTransformer, newValue, futureWrapper) {
      // that will delegate to future.get, but...
      return future.get(ajaxOptions, valueTransformer, newValue, futureReplacement);
      // will pass our function instead of future
      function futureReplacement(initXHR) {
        // that function will delegate to real future
        return (future || futureWrapper)(function (dataCallback) {
          // and once future is started
          // will call original future start function replacing dataCallback
          dataCallbackReplacement.cell = dataCallback.cell;
          dataCallbackReplacement.async = dataCallback.async;
          dataCallbackReplacement.continuing = dataCallback.continuing;
          initXHR(dataCallbackReplacement);
          function dataCallbackReplacement(value, status, xhr) {
            // replaced dataCallback will invoke earlyTransformer
            earlyTransformer(value, status, xhr);
            // and will finally pass data to real dataCallback
            dataCallback(value);
          }
        });
      }
    }
  };
}

future.pollingGET = function (ajaxOptions, valueTransformer, nowValue, futureWrapper) {
  var initiator = futureWrapper || future;
  var interval = 2000;
  if (ajaxOptions.interval) {
    interval = ajaxOptions.interval;
    delete ajaxOptions.interval;
  }
  return future.get(ajaxOptions, valueTransformer, nowValue, function (body, options) {
    return initiator(function (dataCallback) {
      var context = this;
      function dataCallbackWrapper(data) {
        if (!dataCallback.continuing(data)) {
          return;
        }
        setTimeout(function () {
          if (dataCallback.continuing(data)) {
            body.call(context, dataCallbackWrapper);
          }
        }, interval);
      }
      body.call(context, dataCallbackWrapper);
    }, options);
  });
};

future.infinite = function () {
  var rv = future(function () {
    rv.active = true;
    rv.cancel = function () {
      rv.active = false;
    };
  });
  return rv;
};

future.getPush = function (ajaxOptions, valueTransformer, nowValue, waitChange) {
  var options = {
    valueTransformer: valueTransformer,
    cancel: function () {
      operation.cancel();
    },
    nowValue: nowValue
  };
  var operation;
  var etag;

  if (!waitChange) {
    waitChange = 20000;
  }

  if (ajaxOptions.url === undefined) {
    throw new Error("url is undefined");
  }

  function sendRequest(dataCallback) {

    function gotData(data) {
      dataCallback.async.weak = false;

      etag = data.etag;
      // pass our data to cell
      if (dataCallback.continuing(data)) {
        // and submit new request if we are not cancelled
        _.defer(_.bind(sendRequest, null, dataCallback));
      }
    }

    var options = _.extend({dataType: 'json',
                            success: gotData},
                           ajaxOptions);

    if (options.url.indexOf("?") < 0) {
      options.url += '?waitChange=';
    } else {
      options.url += '&waitChange=';
    }
    options.url += waitChange;

    var originalUrl = options.url;
    if (etag) {
      options.url += "&etag=" + encodeURIComponent(etag);
      options.pushRequest = true;
      options.timeout = 30000;
    }
    options.prepareReGet = function (opt) {
      if (opt.errorMarker) {
        // if we have error marker pass it to data callback now to
        // mark error
        if (!dataCallback.continuing(opt.errorMarker)) {
          // if we were cancelled, cancel IOCenter operation
          operation.cancel();
          return;
        }
      }
      // make us weak so that cell invalidations will force new
      // network request
      dataCallback.async.weak = true;

      // recovering from error will 'undo' etag asking immediate
      // response, which we need in this case
      opt.url = originalUrl;
      return opt;
    };

    operation = IOCenter.performGet(options);
  }

  return future(sendRequest, options);
};

// this guy holds queue of failed actions and tries to repeat one of
// them every 5 seconds. Once it succeeds it changes status to healthy
// and repeats other actions
var ErrorQueue = mkClass({
  initialize: function () {
    var self = this;

    self.status = new Cell();
    self.status.setValue({
      healthy: true,
      repeating: false
    });
    self.repeatTimeout = null;
    self.queue = [];
    self.status.subscribeValue(function (s) {
      if (s.healthy) {
        self.repeatInterval = undefined;
      }
    });
  },
  repeatIntervalBase: 5000,
  repeatInterval: undefined,
  planRepeat: function () {
    var interval = this.repeatInterval;
    if (interval == null) {
      return this.repeatIntervalBase;
    }
    return Math.min(interval * 1.618, 300000);
  },
  submit: function (action) {
    this.queue.push(action);
    this.status.setValueAttr(false, 'healthy');
    if (this.repeatTimeout || this.status.value.repeating) {
      return;
    }

    var interval = this.repeatInterval = this.planRepeat();
    var at = (new Date()).getTime() + interval;
    this.status.setValueAttr(at, 'repeatAt');
    this.repeatTimeout = setTimeout($m(this, 'onTimeout'),
                                    interval);
  },
  forceAction: function (forcedAction) {
    var self = this;

    if (self.repeatTimeout) {
      clearTimeout(self.repeatTimeout);
      self.repeatTimeout = null;
    }

    if (!forcedAction) {
      if (!self.queue.length) {
        self.status.setValueAttr(true, 'healthy');
        return;
      }
      forcedAction = self.queue.pop();
    }

    self.status.setValueAttr(true, 'repeating');

    forcedAction(function (isOk) {
      self.status.setValueAttr(false, 'repeating');

      if (isOk == false) {
        return self.submit(forcedAction);
      }

      // if result is undefined or true, we issue next action
      if (isOk) {
        self.status.setValueAttr(true, 'healthy');
      }
      self.onTimeout();
    });
  },
  cancel: function (action) {
    this.queue = _.without(this.queue, action);
    if (this.queue.length || this.status.value.repeating) {
      return;
    }

    this.status.setValueAttr(true, 'healthy');
    if (this.repeatTimeout) {
      cancelTimeout(this.repeatTimeout);
      this.repeatTimeout = null;
    }
  },
  onTimeout: function () {
    this.repeatTimeout = null;
    this.forceAction();
  }
});

// this is central place that defines policy of XHR error handling,
// all cells reads (there are no cells writes by definition) are
// tracked and controlled by this guy
var IOCenter = (function () {
  var status = new Cell();
  status.setValue({
    inFlightCount: 0
  });

  var errorQueue = new ErrorQueue();
  errorQueue.status.subscribeValue(function (v) {
    status.setValue(_.extend({}, status.value, v));
  });

  // S is short for self
  var S = {
    status: status,
    forceRepeat: function () {
      errorQueue.forceAction();
    },
    performGet: function (options) {
      var usedOptions = _.clone(options);

      if (usedOptions.stdErrorMarker) {
        usedOptions.errorMarker = Cell.STANDARD_ERROR_MARK;
      }

      usedOptions.error = gotResponse;
      usedOptions.success = gotResponse;

      var op = {
        cancel: function () {
          if (op.xhr) {
            return Abortarium.abortRequest(op.xhr);
          }

          if (op.done) {
            return;
          }

          op.cancelled = true;

          // we're not done yet and we're not in-flight, so we must
          // be on error queue
          errorQueue.cancel(sendXHR);
        }
      };

      var extraCallback;

      // this is our function to send _and_ re-send
      // request. extraCallback parameter is used during re-sends to
      // execute some code when request is complete
      function sendXHR(_extraCallback) {
        extraCallback = _extraCallback;

        status.setValueAttr(status.value.inFlightCount + 1, 'inFlightCount');

        if (S.simulateDisconnect) {
          setTimeout(function () {
            usedOptions.error.call(usedOptions, {status: 500}, 'error');
          }, 200);
          return;
        }
        op.xhr = $.ajax(usedOptions);
      }
      sendXHR(function (isOk) {
        if (isOk != false) {
          return;
        }

        // our first time 'continuation' is if we've got error then we
        // submit us to repeat queue and maybe update options
        if (options.prepareReGet) {
          var newOptions = options.prepareReGet(usedOptions);
          if (newOptions != null)
            usedOptions = newOptions;
        }

        if (!op.cancelled) {
          errorQueue.submit(sendXHR);
        }
      });
      return op;

      function gotResponse(data, xhrStatus) {
        op.xhr = null;
        status.setValueAttr(status.value.inFlightCount - 1, 'inFlightCount');
        var args = arguments;
        var context = this;

        if (xhrStatus == 'success') {
          op.done = true;
          extraCallback(true);
          return options.success.apply(this, arguments);
        }

        var isOk = (function (xhr) {
          if (Abortarium.isAborted(xhr)) {
            return;
          }
          if (S.isNotFound(xhr)) {
            options.success.call(this,
                                 options.missingValue || undefined,
                                 'notfound');
            return;
          }

          // if our caller has it's own error handling strategy let him do it
          if (options.error) {
            options.error.apply(context, args);
            return;
          }

          return false;
        })(data);

        if (isOk != false) {
          op.done = true;
        }

        extraCallback(isOk);
      }
    },
    readStatus: function (xhr) {
      var status = 0;
      try {
        status = xhr.status
      } catch (e) {
        if (!xhr) {
          throw e;
        }
      }
      return status;
    },
    // we treat everything with 4xx status as not found because for
    // GET requests that we send, we don't have ways to re-send them
    // back in case of 4xx response comes.
    //
    // For example, we cannot do anything with sudden (and unexpected
    // anyway) "401-Authenticate" response, so we just interpret is as
    // if no resource exists.
    isNotFound: function (xhr) {
      var status = S.readStatus(xhr);
      return (400 <= status && status < 500);
    }
  };
  return S;
})();

IOCenter.staleness = new Cell(function (status) {
  return !status.healthy;
}, {
  status: IOCenter.status
});

(function () {
  new Cell(function (status) {
    return status.healthy;
  }, {
    status: IOCenter.status
  }).subscribeValue(function (healthy) {
    console.log("IOCenter.status.healthy: ", healthy);
  });
})();
<commit_msg>fixed another lame usage of cancelTimeout<commit_after>/**
   Copyright 2011 Couchbase, Inc.

   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.
 **/
Cell.prototype.propagateMeta = function () {
  var metaCell = this.ensureMetaCell();
  var staleMetaCell;
  var sourceCells = this.getSourceCells();

  if (metaCell.cancelRefresh) {
    metaCell.cancelRefresh();
  }

  for (var i = sourceCells.length - 1; i >= 0; i--) {
    var cell = sourceCells[i].metaCell;
    if (!cell || !cell.value) {
      continue;
    }
    var metaValue = cell.value;
    if (metaValue.stale) {
      staleMetaCell = cell;
      break;
    }
  }

  metaCell.setValueAttr(!!staleMetaCell, 'stale');

  var refreshSlave = new Slave($m(this, 'propagateMeta'));

  if (staleMetaCell) {
    staleMetaCell.changedSlot.subscribeWithSlave(refreshSlave);
  } else {
    for (var j = sourceCells.length - 1; j >= 0; j--) {
      var cell = sourceCells[j].metaCell;
      if (!cell) {
        continue;
      }
      cell.changedSlot.subscribeWithSlave(refreshSlave);
    }
  }

  metaCell.cancelRefresh = function () {
    refreshSlave.die();
    for (var i = sourceCells.length - 1; i >= 0; i--) {
      var cell = sourceCells[i].metaCell;
      if (!cell) {
        continue;
      }
      cell.changedSlot.cleanup();
    }
  };
};

Cell.STANDARD_ERROR_MARK = {"this is error marker":true};

Cell.prototype.delegateInvalidationMethods = function (target) {
  var self = this;
  _.each(("recalculate recalculateAt recalculateAfterDelay invalidate").split(' '), function (methodName) {
    self[methodName] = $m(target, methodName);
  });
};

future.get = function (ajaxOptions, valueTransformer, nowValue, futureWrapper) {
  // var aborted = false;
  var options = {
    valueTransformer: valueTransformer,
    cancel: function () {
      // aborted = true;
      operation.cancel();
    },
    nowValue: nowValue
  };
  var operation;
  if (ajaxOptions.url === undefined) {
    throw new Error("url is undefined");
  }

  function initiateXHR(dataCallback) {
    var opts = {dataType: 'json',
                prepareReGet: function (opts) {
                  if (opts.errorMarker) {
                    // if we have error marker pass it to data
                    // callback now to mark error
                    if (!dataCallback.continuing(opts.errorMarker)) {
                      // if we were cancelled, cancel IOCenter
                      // operation
                      operation.cancel();
                    }
                  }
                },
                success: dataCallback};
    if (ajaxOptions.onError) {
      opts.error = function () {
        ajaxOptions.onError.apply(this, [dataCallback].concat(_.toArray(arguments)));
      };
    }
    operation = IOCenter.performGet(_.extend(opts, ajaxOptions));
  }
  return (futureWrapper || future)(initiateXHR, options);
};

future.withEarlyTransformer = function (earlyTransformer) {
  return {
    // we return a function
    get: function (ajaxOptions, valueTransformer, newValue, futureWrapper) {
      // that will delegate to future.get, but...
      return future.get(ajaxOptions, valueTransformer, newValue, futureReplacement);
      // will pass our function instead of future
      function futureReplacement(initXHR) {
        // that function will delegate to real future
        return (future || futureWrapper)(function (dataCallback) {
          // and once future is started
          // will call original future start function replacing dataCallback
          dataCallbackReplacement.cell = dataCallback.cell;
          dataCallbackReplacement.async = dataCallback.async;
          dataCallbackReplacement.continuing = dataCallback.continuing;
          initXHR(dataCallbackReplacement);
          function dataCallbackReplacement(value, status, xhr) {
            // replaced dataCallback will invoke earlyTransformer
            earlyTransformer(value, status, xhr);
            // and will finally pass data to real dataCallback
            dataCallback(value);
          }
        });
      }
    }
  };
}

future.pollingGET = function (ajaxOptions, valueTransformer, nowValue, futureWrapper) {
  var initiator = futureWrapper || future;
  var interval = 2000;
  if (ajaxOptions.interval) {
    interval = ajaxOptions.interval;
    delete ajaxOptions.interval;
  }
  return future.get(ajaxOptions, valueTransformer, nowValue, function (body, options) {
    return initiator(function (dataCallback) {
      var context = this;
      function dataCallbackWrapper(data) {
        if (!dataCallback.continuing(data)) {
          return;
        }
        setTimeout(function () {
          if (dataCallback.continuing(data)) {
            body.call(context, dataCallbackWrapper);
          }
        }, interval);
      }
      body.call(context, dataCallbackWrapper);
    }, options);
  });
};

future.infinite = function () {
  var rv = future(function () {
    rv.active = true;
    rv.cancel = function () {
      rv.active = false;
    };
  });
  return rv;
};

future.getPush = function (ajaxOptions, valueTransformer, nowValue, waitChange) {
  var options = {
    valueTransformer: valueTransformer,
    cancel: function () {
      operation.cancel();
    },
    nowValue: nowValue
  };
  var operation;
  var etag;

  if (!waitChange) {
    waitChange = 20000;
  }

  if (ajaxOptions.url === undefined) {
    throw new Error("url is undefined");
  }

  function sendRequest(dataCallback) {

    function gotData(data) {
      dataCallback.async.weak = false;

      etag = data.etag;
      // pass our data to cell
      if (dataCallback.continuing(data)) {
        // and submit new request if we are not cancelled
        _.defer(_.bind(sendRequest, null, dataCallback));
      }
    }

    var options = _.extend({dataType: 'json',
                            success: gotData},
                           ajaxOptions);

    if (options.url.indexOf("?") < 0) {
      options.url += '?waitChange=';
    } else {
      options.url += '&waitChange=';
    }
    options.url += waitChange;

    var originalUrl = options.url;
    if (etag) {
      options.url += "&etag=" + encodeURIComponent(etag);
      options.pushRequest = true;
      options.timeout = 30000;
    }
    options.prepareReGet = function (opt) {
      if (opt.errorMarker) {
        // if we have error marker pass it to data callback now to
        // mark error
        if (!dataCallback.continuing(opt.errorMarker)) {
          // if we were cancelled, cancel IOCenter operation
          operation.cancel();
          return;
        }
      }
      // make us weak so that cell invalidations will force new
      // network request
      dataCallback.async.weak = true;

      // recovering from error will 'undo' etag asking immediate
      // response, which we need in this case
      opt.url = originalUrl;
      return opt;
    };

    operation = IOCenter.performGet(options);
  }

  return future(sendRequest, options);
};

// this guy holds queue of failed actions and tries to repeat one of
// them every 5 seconds. Once it succeeds it changes status to healthy
// and repeats other actions
var ErrorQueue = mkClass({
  initialize: function () {
    var self = this;

    self.status = new Cell();
    self.status.setValue({
      healthy: true,
      repeating: false
    });
    self.repeatTimeout = null;
    self.queue = [];
    self.status.subscribeValue(function (s) {
      if (s.healthy) {
        self.repeatInterval = undefined;
      }
    });
  },
  repeatIntervalBase: 5000,
  repeatInterval: undefined,
  planRepeat: function () {
    var interval = this.repeatInterval;
    if (interval == null) {
      return this.repeatIntervalBase;
    }
    return Math.min(interval * 1.618, 300000);
  },
  submit: function (action) {
    this.queue.push(action);
    this.status.setValueAttr(false, 'healthy');
    if (this.repeatTimeout || this.status.value.repeating) {
      return;
    }

    var interval = this.repeatInterval = this.planRepeat();
    var at = (new Date()).getTime() + interval;
    this.status.setValueAttr(at, 'repeatAt');
    this.repeatTimeout = setTimeout($m(this, 'onTimeout'),
                                    interval);
  },
  forceAction: function (forcedAction) {
    var self = this;

    if (self.repeatTimeout) {
      clearTimeout(self.repeatTimeout);
      self.repeatTimeout = null;
    }

    if (!forcedAction) {
      if (!self.queue.length) {
        self.status.setValueAttr(true, 'healthy');
        return;
      }
      forcedAction = self.queue.pop();
    }

    self.status.setValueAttr(true, 'repeating');

    forcedAction(function (isOk) {
      self.status.setValueAttr(false, 'repeating');

      if (isOk == false) {
        return self.submit(forcedAction);
      }

      // if result is undefined or true, we issue next action
      if (isOk) {
        self.status.setValueAttr(true, 'healthy');
      }
      self.onTimeout();
    });
  },
  cancel: function (action) {
    this.queue = _.without(this.queue, action);
    if (this.queue.length || this.status.value.repeating) {
      return;
    }

    this.status.setValueAttr(true, 'healthy');
    if (this.repeatTimeout) {
      clearTimeout(this.repeatTimeout);
      this.repeatTimeout = null;
    }
  },
  onTimeout: function () {
    this.repeatTimeout = null;
    this.forceAction();
  }
});

// this is central place that defines policy of XHR error handling,
// all cells reads (there are no cells writes by definition) are
// tracked and controlled by this guy
var IOCenter = (function () {
  var status = new Cell();
  status.setValue({
    inFlightCount: 0
  });

  var errorQueue = new ErrorQueue();
  errorQueue.status.subscribeValue(function (v) {
    status.setValue(_.extend({}, status.value, v));
  });

  // S is short for self
  var S = {
    status: status,
    forceRepeat: function () {
      errorQueue.forceAction();
    },
    performGet: function (options) {
      var usedOptions = _.clone(options);

      if (usedOptions.stdErrorMarker) {
        usedOptions.errorMarker = Cell.STANDARD_ERROR_MARK;
      }

      usedOptions.error = gotResponse;
      usedOptions.success = gotResponse;

      var op = {
        cancel: function () {
          if (op.xhr) {
            return Abortarium.abortRequest(op.xhr);
          }

          if (op.done) {
            return;
          }

          op.cancelled = true;

          // we're not done yet and we're not in-flight, so we must
          // be on error queue
          errorQueue.cancel(sendXHR);
        }
      };

      var extraCallback;

      // this is our function to send _and_ re-send
      // request. extraCallback parameter is used during re-sends to
      // execute some code when request is complete
      function sendXHR(_extraCallback) {
        extraCallback = _extraCallback;

        status.setValueAttr(status.value.inFlightCount + 1, 'inFlightCount');

        if (S.simulateDisconnect) {
          setTimeout(function () {
            usedOptions.error.call(usedOptions, {status: 500}, 'error');
          }, 200);
          return;
        }
        op.xhr = $.ajax(usedOptions);
      }
      sendXHR(function (isOk) {
        if (isOk != false) {
          return;
        }

        // our first time 'continuation' is if we've got error then we
        // submit us to repeat queue and maybe update options
        if (options.prepareReGet) {
          var newOptions = options.prepareReGet(usedOptions);
          if (newOptions != null)
            usedOptions = newOptions;
        }

        if (!op.cancelled) {
          errorQueue.submit(sendXHR);
        }
      });
      return op;

      function gotResponse(data, xhrStatus) {
        op.xhr = null;
        status.setValueAttr(status.value.inFlightCount - 1, 'inFlightCount');
        var args = arguments;
        var context = this;

        if (xhrStatus == 'success') {
          op.done = true;
          extraCallback(true);
          return options.success.apply(this, arguments);
        }

        var isOk = (function (xhr) {
          if (Abortarium.isAborted(xhr)) {
            return;
          }
          if (S.isNotFound(xhr)) {
            options.success.call(this,
                                 options.missingValue || undefined,
                                 'notfound');
            return;
          }

          // if our caller has it's own error handling strategy let him do it
          if (options.error) {
            options.error.apply(context, args);
            return;
          }

          return false;
        })(data);

        if (isOk != false) {
          op.done = true;
        }

        extraCallback(isOk);
      }
    },
    readStatus: function (xhr) {
      var status = 0;
      try {
        status = xhr.status
      } catch (e) {
        if (!xhr) {
          throw e;
        }
      }
      return status;
    },
    // we treat everything with 4xx status as not found because for
    // GET requests that we send, we don't have ways to re-send them
    // back in case of 4xx response comes.
    //
    // For example, we cannot do anything with sudden (and unexpected
    // anyway) "401-Authenticate" response, so we just interpret is as
    // if no resource exists.
    isNotFound: function (xhr) {
      var status = S.readStatus(xhr);
      return (400 <= status && status < 500);
    }
  };
  return S;
})();

IOCenter.staleness = new Cell(function (status) {
  return !status.healthy;
}, {
  status: IOCenter.status
});

(function () {
  new Cell(function (status) {
    return status.healthy;
  }, {
    status: IOCenter.status
  }).subscribeValue(function (healthy) {
    console.log("IOCenter.status.healthy: ", healthy);
  });
})();
<|endoftext|>


<commit_before>import ProcessorBase from '../base/processor.js';

import Logger from '../util/logger.js';

class ImageProcessor extends ProcessorBase {

  constructor() {
    super();

    this.processingDownloads = 0;
    this.concurrencyLimit = 10;
    this.images = [];
    this.strategy = null;
  }

  init(strategy) {
    Logger.instance.info('[ImageProcessor][%s] Init ...', process.pid);

    this.strategy = strategy;
  }

  async start(taskUrl) {
    Logger.instance.info('[ImageProcessor][%s] Start to process task: %s', process.pid, taskUrl);

    try {
      let albums = await this.strategy.parseAlbums(taskUrl);
      Logger.instance.info('[ImageProcessor][%s] Albums to be downloaded:', process.pid, albums);

      for (let albumUrl of albums) {
        let albumImages = await this.strategy.parseImages(albumUrl);
        this.images = [...this.images, ...albumImages];
      }
      Logger.instance.info('[ImageProcessor][%s] Images to be downloaded:', process.pid, this.images);

      if (this.images.length > 0) {
        await this.strategy.ensureOutputDir();
      }
    } catch (err) {
      return new Promise((resolve, reject) => {
        reject(err);
      });
    }

    return new Promise((resolve) => {
      let reporter = setInterval(() => {
        Logger.instance.info('[ImageProcessor][%s] Image task left not assigned: %d, Image task downloading: %d', process.pid, this.images.length, this.processingDownloads);
      }, 3000); // 5s

      let timer = setInterval(() => {
        // process download
        if (this.images.length > 0 && this.processingDownloads < this.concurrencyLimit) {
          this.processingDownloads++;
          this.strategy.downloadImage(this.images.shift()).then(
            () => {
              this.processingDownloads--;
            },
            (err) => {
              this.processingDownloads--;
              Logger.instance.error(err);
            }
          );
        }

        // check status
        if (this.images.length <= 0 && this.processingDownloads <= 0) {
          clearInterval(reporter);
          clearInterval(timer);
          resolve();
        }
      }, 100); // 0.1s
    });
  }

}

export default ImageProcessor;<commit_msg>Reduce log, make easy to see.<commit_after>import ProcessorBase from '../base/processor.js';

import Logger from '../util/logger.js';

class ImageProcessor extends ProcessorBase {

  constructor() {
    super();

    this.processingDownloads = 0;
    this.concurrencyLimit = 10;
    this.images = [];
    this.strategy = null;
  }

  init(strategy) {
    Logger.instance.info('[ImageProcessor][%s] Init ...', process.pid);

    this.strategy = strategy;
  }

  async start(taskUrl) {
    Logger.instance.info('[ImageProcessor][%s] Start to process task: %s', process.pid, taskUrl);

    try {
      let albums = await this.strategy.parseAlbums(taskUrl);
      Logger.instance.info('[ImageProcessor][%s] Albums to be downloaded: %d', process.pid, albums.length);

      for (let albumUrl of albums) {
        let albumImages = await this.strategy.parseImages(albumUrl);
        this.images = [...this.images, ...albumImages];
      }
      Logger.instance.info('[ImageProcessor][%s] Images to be downloaded: %d', process.pid, this.images.length);

      if (this.images.length > 0) {
        await this.strategy.ensureOutputDir();
      }
    } catch (err) {
      return new Promise((resolve, reject) => {
        reject(err);
      });
    }

    return new Promise((resolve) => {
      let reporter = setInterval(() => {
        Logger.instance.info('[ImageProcessor][%s] Image task left not assigned: %d, Image task downloading: %d', process.pid, this.images.length, this.processingDownloads);
      }, 3000); // 5s

      let timer = setInterval(() => {
        // process download
        if (this.images.length > 0 && this.processingDownloads < this.concurrencyLimit) {
          this.processingDownloads++;
          this.strategy.downloadImage(this.images.shift()).then(
            () => {
              this.processingDownloads--;
            },
            (err) => {
              this.processingDownloads--;
              Logger.instance.error(err);
            }
          );
        }

        // check status
        if (this.images.length <= 0 && this.processingDownloads <= 0) {
          clearInterval(reporter);
          clearInterval(timer);
          resolve();
        }
      }, 100); // 0.1s
    });
  }

}

export default ImageProcessor;<|endoftext|>


<commit_before>
import omit from 'object.omit'
import { deepMerge } from "../lib/helpers"
import generateName from 'sillyname'
import uuid from 'node-uuid';

import { actionTypes } from 'redux-localstorage'

import { OPERATION_INITIALSTATE } from './operation'

import CommandHistory from '../components/command-history'

export const MATERIALDB_INITIALSTATE = require("../data/lw.materials/material-database.json");
export const MATERIALDB_SCHEMA = require("../data/lw.materials/material-database.spec.json");

import Ajv from 'ajv';
const ajv = new Ajv();
      ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
const validate = ajv.compile(MATERIALDB_SCHEMA);

function generateInteger(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

const GROUP_TEMPLATE = () => {
    return {
        id: uuid.v4(),
        name: generateName(),
        notes: "",
        template: PRESET_TEMPLATE('Laser Cut'),
        presets: []
    }
}

const PRESET_TEMPLATE = (type, machineProfile = null) => {
    return {
        id: uuid.v4(),
        name: "** " + generateName() + " **",
        notes: "",
        type: type,
        machine_profile: machineProfile,
        params: OPERATION_INITIALSTATE
    }
}

export const DEFAULT_GROUPING_NAME = '__SAVES__';

const togglePresetAttribute = (state, id, attribute, processPreset = null) => {
    return state.map((group) => {
        if (!group.presets || !group.presets.find((preset) => { return preset.id === id }))
            return group;

        group.presets = group.presets.map((preset, i) => {
            if (preset.id !== id)
                return preset;

            if (typeof preset[attribute] == "undefined")
                preset[attribute] = false;

            preset[attribute] = !preset[attribute];

            if (processPreset)
                preset = processPreset(preset)

            return preset;

        })

        return group;
    })
}

const toggleGroupAttribute = (state, id, attribute, processGroup = null) => {
    return state.map((group) => {
        if (group.id !== id)
            return group;
        if (typeof group[attribute] == "undefined")
            group[attribute] = false;

        group[attribute] = !group[attribute];

        if (processGroup)
            group = processGroup(group)

        return group;
    })
}

export const materialDatabase = (state = MATERIALDB_INITIALSTATE, action) => {

    switch (action.type) {

        case "MATERIALDB_UPLOAD":
            return action.payload.database;

        case "MATERIALDB_DOWNLOAD":
            return state;

        case "MATERIALDB_GROUP_ADD":
            state = [...state, GROUP_TEMPLATE()];
            return state;

        case "MATERIALDB_GROUP_DELETE":
            return state.filter((group) => {
                return (group.id !== action.payload);
            })

        case "MATERIALDB_GROUP_SET_ATTRS":
            return state.map((group) => {
                if (group.id !== action.payload.groupId)
                    return group;

                let attrs = omit(action.payload.attrs, ['id', 'presets']); // dont overwrite id,presets
                group = deepMerge(group, attrs)
                return group;
            })

        case "MATERIALDB_GROUP_TOGGLE_VIEW":
            return toggleGroupAttribute(state, action.payload, 'isOpened');

        case "MATERIALDB_GROUP_TOGGLE_EDIT":

            //disable children edit.
            const processGroup = (group) => {
                if (group.isEditable) {
                    group.presets = group.presets.map((preset, i) => {
                        preset.isEditable = false;
                        return preset;
                    })
                }
                return group;
            }

            return toggleGroupAttribute(state, action.payload, 'isEditable', processGroup)

        case "MATERIALDB_PRESET_ADD":
            return state.map((group) => {
                if (group.id !== action.payload.groupId)
                    return group;

                let template = group.template || {};
                let attrs = action.payload.attrs || {};
                group.presets = [...group.presets, Object.assign(PRESET_TEMPLATE(), omit(template, ['id', 'name']), omit(attrs, ['id']))]

                return group;
            });

        case "MATERIALDB_PRESET_SET_ATTRS":
            return state.map((group) => {
                if (!group.presets || !group.presets.find((preset) => { return preset.id === action.payload.presetId }))
                    return group;

                group.presets = group.presets.map((preset) => {
                    if (preset.id !== action.payload.presetId)
                        return preset;
                    return deepMerge(preset, action.payload.attrs)

                })

                return group;
            })

        case "MATERIALDB_PRESET_DELETE":
            return state.map((group) => {
                if (!group.presets || !group.presets.find((preset) => { return preset.id === action.payload }))
                    return group;

                group.presets = group.presets.filter((preset, i) => { return (preset.id !== action.payload) })

                return group;
            })

        case "MATERIALDB_PRESET_TOGGLE_EDIT":
            return togglePresetAttribute(state, action.payload, 'isEditable')

        case "MATERIALDB_PRESET_NEW":
            const grouping_name = action.payload.grouping || DEFAULT_GROUPING_NAME;
            let groupings = state.slice();
            let grouping = groupings.find((grouping) => (grouping.name === grouping_name))

            if (!grouping) {
                grouping = Object.assign(GROUP_TEMPLATE(), { name: grouping_name })
                groupings.push(grouping);
            }
            let preset_name = action.payload.preset.name || action.payload.name || ("** " + generateName() + " **");
            let existingPreset = grouping.presets.find((preset) => preset.name === preset_name);
            if (!existingPreset) {

                let attrs = Object.assign(
                                PRESET_TEMPLATE(action.payload.preset.type),
                                { name:  preset_name, params: omit(action.payload.preset,['id','documents'])}
                             );
                CommandHistory.write(`Creating preset "${preset_name}" into grouping "${grouping.name}"`,CommandHistory.SUCCESS)
                return materialDatabase(groupings, { type: 'MATERIALDB_PRESET_ADD', payload: { groupId: grouping.id, attrs } })
            } else {
                CommandHistory.warn(`Updating preset "${existingPreset.name}" of grouping "${grouping.name}"`)
                return materialDatabase(groupings, { type: 'MATERIALDB_PRESET_SET_ATTRS', payload: { presetId: existingPreset.id, attrs: { params: action.payload.preset} } })
            }

        case actionTypes.INIT:
            if (action.payload) {
                let lockedState = MATERIALDB_INITIALSTATE.slice().map((vendor) => {
                    return  (vendor._locked!==false) ? { ...vendor, _locked: true } : vendor
                });
                let currentState = action.payload.materialDatabase || []

                if (validate(currentState)) {
                    if (currentState.length) {
                        lockedState.forEach((l,i)=>{ if (l._locked && !currentState.find((f)=>{ return f.id == l.id })) currentState=[l, ...currentState];})
                    } else {
                        currentState=lockedState;
                    }
                    return currentState;
                } else {
                    CommandHistory.error("Material Database corrupt/obsolete. Restoring.")
                    console.error(validate.errors);
                    return lockedState;
                }
            }
            return state;

        default:
            return state;
    }

}
<commit_msg>Allow mDB backup<commit_after>
import omit from 'object.omit'
import { deepMerge, sendAsFile } from "../lib/helpers"
import generateName from 'sillyname'
import uuid from 'node-uuid';

import { actionTypes } from 'redux-localstorage'

import { OPERATION_INITIALSTATE } from './operation'

import CommandHistory from '../components/command-history'

export const MATERIALDB_INITIALSTATE = require("../data/lw.materials/material-database.json");
export const MATERIALDB_SCHEMA = require("../data/lw.materials/material-database.spec.json");

import { confirm } from '../components/laserweb'
import stringify from 'json-stringify-pretty-compact';

import Ajv from 'ajv';
const ajv = new Ajv();
      ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
const validate = ajv.compile(MATERIALDB_SCHEMA);

function generateInteger(min, max) {
    return Math.floor(Math.random() * (max - min)) + min;
}

const GROUP_TEMPLATE = () => {
    return {
        id: uuid.v4(),
        name: generateName(),
        notes: "",
        template: PRESET_TEMPLATE('Laser Cut'),
        presets: []
    }
}

const PRESET_TEMPLATE = (type, machineProfile = null) => {
    return {
        id: uuid.v4(),
        name: "** " + generateName() + " **",
        notes: "",
        type: type,
        machine_profile: machineProfile,
        params: OPERATION_INITIALSTATE
    }
}

export const DEFAULT_GROUPING_NAME = '__SAVES__';

const togglePresetAttribute = (state, id, attribute, processPreset = null) => {
    return state.map((group) => {
        if (!group.presets || !group.presets.find((preset) => { return preset.id === id }))
            return group;

        group.presets = group.presets.map((preset, i) => {
            if (preset.id !== id)
                return preset;

            if (typeof preset[attribute] == "undefined")
                preset[attribute] = false;

            preset[attribute] = !preset[attribute];

            if (processPreset)
                preset = processPreset(preset)

            return preset;

        })

        return group;
    })
}

const toggleGroupAttribute = (state, id, attribute, processGroup = null) => {
    return state.map((group) => {
        if (group.id !== id)
            return group;
        if (typeof group[attribute] == "undefined")
            group[attribute] = false;

        group[attribute] = !group[attribute];

        if (processGroup)
            group = processGroup(group)

        return group;
    })
}

export const materialDatabase = (state = MATERIALDB_INITIALSTATE, action) => {

    switch (action.type) {

        case "MATERIALDB_UPLOAD":
            return action.payload.database;

        case "MATERIALDB_DOWNLOAD":
            return state;

        case "MATERIALDB_GROUP_ADD":
            state = [...state, GROUP_TEMPLATE()];
            return state;

        case "MATERIALDB_GROUP_DELETE":
            return state.filter((group) => {
                return (group.id !== action.payload);
            })

        case "MATERIALDB_GROUP_SET_ATTRS":
            return state.map((group) => {
                if (group.id !== action.payload.groupId)
                    return group;

                let attrs = omit(action.payload.attrs, ['id', 'presets']); // dont overwrite id,presets
                group = deepMerge(group, attrs)
                return group;
            })

        case "MATERIALDB_GROUP_TOGGLE_VIEW":
            return toggleGroupAttribute(state, action.payload, 'isOpened');

        case "MATERIALDB_GROUP_TOGGLE_EDIT":

            //disable children edit.
            const processGroup = (group) => {
                if (group.isEditable) {
                    group.presets = group.presets.map((preset, i) => {
                        preset.isEditable = false;
                        return preset;
                    })
                }
                return group;
            }

            return toggleGroupAttribute(state, action.payload, 'isEditable', processGroup)

        case "MATERIALDB_PRESET_ADD":
            return state.map((group) => {
                if (group.id !== action.payload.groupId)
                    return group;

                let template = group.template || {};
                let attrs = action.payload.attrs || {};
                group.presets = [...group.presets, Object.assign(PRESET_TEMPLATE(), omit(template, ['id', 'name']), omit(attrs, ['id']))]

                return group;
            });

        case "MATERIALDB_PRESET_SET_ATTRS":
            return state.map((group) => {
                if (!group.presets || !group.presets.find((preset) => { return preset.id === action.payload.presetId }))
                    return group;

                group.presets = group.presets.map((preset) => {
                    if (preset.id !== action.payload.presetId)
                        return preset;
                    return deepMerge(preset, action.payload.attrs)

                })

                return group;
            })

        case "MATERIALDB_PRESET_DELETE":
            return state.map((group) => {
                if (!group.presets || !group.presets.find((preset) => { return preset.id === action.payload }))
                    return group;

                group.presets = group.presets.filter((preset, i) => { return (preset.id !== action.payload) })

                return group;
            })

        case "MATERIALDB_PRESET_TOGGLE_EDIT":
            return togglePresetAttribute(state, action.payload, 'isEditable')

        case "MATERIALDB_PRESET_NEW":
            const grouping_name = action.payload.grouping || DEFAULT_GROUPING_NAME;
            let groupings = state.slice();
            let grouping = groupings.find((grouping) => (grouping.name === grouping_name))

            if (!grouping) {
                grouping = Object.assign(GROUP_TEMPLATE(), { name: grouping_name })
                groupings.push(grouping);
            }
            let preset_name = action.payload.preset.name || action.payload.name || ("** " + generateName() + " **");
            let existingPreset = grouping.presets.find((preset) => preset.name === preset_name);
            if (!existingPreset) {

                let attrs = Object.assign(
                                PRESET_TEMPLATE(action.payload.preset.type),
                                { name:  preset_name, params: omit(action.payload.preset,['id','documents'])}
                             );
                CommandHistory.write(`Creating preset "${preset_name}" into grouping "${grouping.name}"`,CommandHistory.SUCCESS)
                return materialDatabase(groupings, { type: 'MATERIALDB_PRESET_ADD', payload: { groupId: grouping.id, attrs } })
            } else {
                CommandHistory.warn(`Updating preset "${existingPreset.name}" of grouping "${grouping.name}"`)
                return materialDatabase(groupings, { type: 'MATERIALDB_PRESET_SET_ATTRS', payload: { presetId: existingPreset.id, attrs: { params: action.payload.preset} } })
            }

        case actionTypes.INIT:
            if (action.payload) {
                let lockedState = MATERIALDB_INITIALSTATE.slice().map((vendor) => {
                    return  (vendor._locked!==false) ? { ...vendor, _locked: true } : vendor
                });
                let currentState = action.payload.materialDatabase || []
                if (validate(currentState)) {
                    if (currentState.length) {
                        lockedState.forEach((l,i)=>{ if (l._locked && !currentState.find((f)=>{ return f.id == l.id })) currentState=[l, ...currentState];})
                    } else {
                        currentState=lockedState;
                    }
                    return currentState;
                } else {
                    let backup = stringify(currentState);
                    confirm("Material Database corrupt/obsolete. Restoring. Ok download a backup, Cancel to continue.",function(data){
                        if (data) sendAsFile('LaserWeb-MaterialDatabase-Backup.json',backup,'application/json')
                    })
                    CommandHistory.error("Material Database corrupt/obsolete. Restoring.")
                    console.error(validate.errors);
                    return lockedState;
                }
            }

        default:
            return state;
    }

}
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of perseo-fe
 *
 * perseo-fe is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * perseo-fe is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with perseo-fe.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var request = require('request'),
    util = require('util'),
    logger = require('logops'),
    constants = require('./constants'),
    metrics = require('./models/metrics');

function logErrorIf(err, message, context) {
    var level = 'error';
    if (context === undefined) {
        context = process.domain && process.domain.context;
    }
    if (err) {
        message = message || '';
        if (context) {
            context.op =
                context.op ||
                new Error().stack
                    .split('\n')[2]
                    .trim()
                    .substr(3);
            context.comp = context.comp || constants.COMPONENT_NAME;
            logger[level](context, message, err.message || JSON.stringify(err));
        } else {
            logger[level](message, err.message || JSON.stringify(err));
        }
    }
}

function expandVar(val, mapping) {
    if (typeof val === 'string') {
        Object.keys(mapping).forEach(function(p) {
            val = val.replace(new RegExp('\\$\\{' + p + '\\}', 'g'), mapping[p]);
        });
        val = val.replace(/\$\{\w*\}/g, '[?]');
    }
    return val;
}

function expandObject(templateObj, dictionary) {
    var res = {};
    if (templateObj && typeof templateObj === 'object') {
        Object.keys(templateObj).forEach(function(key) {
            if (typeof templateObj[key] === 'string') {
                res[expandVar(key, dictionary)] = expandVar(templateObj[key], dictionary);
            } else if (typeof templateObj[key] === 'object') {
                res[expandVar(key, dictionary)] = expandObject(templateObj[key], dictionary);
            }
        });
    }
    return res;
}

// Think better if this is the best way
function flattenMap(key, targetMap) {
    var newMap = {},
        v,
        flattened,
        fv;
    Object.keys(targetMap).forEach(function(k) {
        v = targetMap[k];
        if (v && typeof v === 'object') {
            flattened = flattenMap(k + '__', v);
            Object.keys(flattened).forEach(function(fk) {
                fv = flattened[fk];
                newMap[key + fk] = fv;
            });
        } else {
            newMap[key + k] = v;
        }
    });
    return newMap;
}

function requestHelperAux(method, options, withMetrics, callback) {
    var localError,
        respObj,
        headers,
        domain = process.domain;
    logger.info('making %s to %s', method, options.url);
    if (withMetrics && domain.context) {
        metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactions);
    }
    headers = options.headers || {};
    if (domain && domain.context) {
        headers[constants.CORRELATOR_HEADER] = domain.context.corr;
        if (domain.context.srv && headers[constants.SERVICE_HEADER] === undefined) {
            headers[constants.SERVICE_HEADER] = domain.context.srv;
        }
        if (domain.context.subsrv && headers[constants.SUBSERVICE_HEADER] === undefined) {
            headers[constants.SUBSERVICE_HEADER] = domain.context.subsrv;
        }
        if (domain.context.from && headers[constants.REALIP_HEADER] === undefined) {
            headers[constants.REALIP_HEADER] = domain.context.from;
        }
    }
    options.headers = headers;
    if (withMetrics && options.json && domain.context) {
        try {
            metrics.IncMetrics(
                domain.context.srv,
                domain.context.subsrv,
                metrics.outgoingTransactionsRequestSize,
                Buffer.byteLength(JSON.stringify(options.body), 'utf-8')
            );
        } catch (exception) {
            logger.warn(exception);
        }
    }
    request[method](options, function cbRequest2core(err, response, body) {
        var bodySz = 0;
        if (withMetrics && domain.context) {
            if (body) {
                if (typeof body === 'string') {
                    bodySz = Buffer.byteLength(body);
                } else {
                    try {
                        bodySz = Buffer.byteLength(JSON.stringify(body));
                    } catch (ex) {
                        logger.warn(ex);
                    }
                }
                metrics.IncMetrics(
                    domain.context.srv,
                    domain.context.subsrv,
                    metrics.outgoingTransactionsResponseSize,
                    bodySz
                );
            }
        }
        if (err) {
            logErrorIf(err, util.format('error %s to %s', method, options.url));
            if (withMetrics && domain.context) {
                metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors);
            }
            return callback(err, null);
        }
        respObj = { code: response.statusCode, body: body };
        logger.debug('%s to %s returns %j', method, options.url, respObj);
        if (response.statusCode < 200 || response.statusCode >= 300) {
            localError = new Error(
                util.format(
                    'error %s to %s (%s)',
                    method,
                    options.url,
                    (body && body.error) || (body && JSON.stringify(body)) || response.statusCode
                )
            );
            localError.httpCode = 500;
            logErrorIf(localError, domain && domain.context);
            if (withMetrics && domain.context) {
                metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors);
            }
            return callback(localError, respObj);
        }
        logger.info('done %s to %s', method, options.url);
        return callback(err, respObj);
    });
}

function requestHelper(method, options, callback) {
    return requestHelperAux(method, options, true, callback);
}

function requestHelperWOMetrics(method, options, callback) {
    return requestHelperAux(method, options, false, callback);
}

function respondAux(resp, err, data, withCount, raw, withMetrics) {
    var statusCode = 200,
        errMsg = null,
        respObj,
        respStr,
        domain = process.domain;
    if (err) {
        errMsg = err.message;
        statusCode = err.httpCode || 500;
        data = null;
    }

    if (raw === true) {
        if (err) {
            respObj = err;
            delete respObj.httpCode;
        } else {
            respObj = data;
        }
    } else {
        // non-raw
        respObj = { error: errMsg, data: data };
        if (withCount === true && data && util.isArray(data)) {
            respObj.count = data.length;
        }
    }
    logger.info('sending response: %s %j', statusCode, respObj);
    respStr = JSON.stringify(respObj);
    if (withMetrics && domain && domain.context) {
        metrics.IncMetrics(
            domain.context.srv,
            domain.context.subsrv,
            metrics.incomingTransactionsResponseSize,
            Buffer.byteLength(respStr, 'utf-8')
        );
        if (err) {
            metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.incomingTransactionsErrors);
        }
    }
    resp.set('Content-Type', 'application/json');
    resp.status(statusCode);
    resp.send(respStr);
}

function respond(resp, err, data, withCount, raw) {
    return respondAux(resp, err, data, withCount, raw, true);
}

function respondWOMetrics(resp, err, data, withCount, raw) {
    return respondAux(resp, err, data, withCount, raw, false);
}

function firstChars(result) {
    var text;
    if (result === undefined) {
        result = 'undefined';
    }
    text = JSON.stringify(result);
    if (text.length > 125) {
        text = text.substr(0, 125) + ' [...]';
    }
    return text;
}

function purgeName(name) {
    return name ? name.replace(/\//g, '$') : name;
}

function contextName(rule) {
    return util.format('ctxt$%s%s', purgeName(rule.service), purgeName(rule.subservice));
}

function ruleUniqueName(rule) {
    return util.format('%s@%s%s', rule.name, rule.service, rule.subservice);
}

function contextEPL(rule) {
    return util.format(
        'create context %s partition by service from iotEvent(service="%s" and subservice="%s")',
        contextName(rule),
        rule.service,
        rule.subservice
    );
}

function ruleWithContext(rule) {
    return util.format('context %s %s', contextName(rule), rule.text);
}

/**
 * expandVar substitutes every variable in val (denoted as $(var}) with the value
 * in mappings (as dictionary), getting the key 'var' from the object
 *
 * @param  {string}   'template' in which to replace variables for values
 * @param  {Object}   variable name to substitute
 */
module.exports.expandVar = expandVar;

module.exports.expandObject = expandObject;

/**
 * flattenMap flatten a map recursively using '__' to nested maps
 *
 * @param  {string} key to append to every nested key
 * @param  {Object} object to put the flattened keys
 */
module.exports.flattenMap = flattenMap;

/**
 * requestHelper makes an HTTP request.
 *
 * @param {string}       method  ('GET', 'POST', 'PUT', 'DELETE')
 * @param {Object}       options, as accepted by 'request' library
 * @param {function}     callback function(error, response)
 */
module.exports.requestHelper = requestHelper;
module.exports.requestHelperWOMetrics = requestHelperWOMetrics;

/**
 * respond sends an HTTP response with the proper code for the error passed in, if any.
 * Also it can add the length if the object to send is in Array and it is asked for.
 *
 * @param {Object}       response object form Express
 * @param {Object}       error object or null
 * @param {Object}       data to send as JSON
 * @param {boolean}      if data is an Array, add a count field with its length
 */
module.exports.respond = respond;
module.exports.respondWOMetrics = respondWOMetrics;

/**
 * logErrorIf writes an error if passed in. Optionally a message can be add and the level for log can be set
 *
 * @param {Object}       error, if null, nothing will be logged
 * @param {string}       message to add, optional
 * @param {string}       level to log to, optional. 'error' by default
 */
module.exports.logErrorIf = logErrorIf;
/**
 * firstChars returns first characters of a string
 *
 * @param  {string} string to trim, if necessary
 */
module.exports.firstChars = firstChars;
/**
 * ruleUniqueName returns a unique name for a rule, including subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.ruleUniqueName = ruleUniqueName;

/**
 * ruleWithContext returns the text of a rule with the context information add (currently the text)
 *
 * @param  {Object} Object rule
 */
module.exports.ruleWithContext = ruleWithContext;
/**
 * contextRuleText returns the EPL text for creating a context for subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.contextEPL = contextEPL;

/**
 * contextRuleName returns the name of a context for subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.contextName = contextName;
<commit_msg>try check num<commit_after>/*
 * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of perseo-fe
 *
 * perseo-fe is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * perseo-fe is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with perseo-fe.
 * If not, see http://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var request = require('request'),
    util = require('util'),
    logger = require('logops'),
    constants = require('./constants'),
    metrics = require('./models/metrics');

function logErrorIf(err, message, context) {
    var level = 'error';
    if (context === undefined) {
        context = process.domain && process.domain.context;
    }
    if (err) {
        message = message || '';
        if (context) {
            context.op =
                context.op ||
                new Error().stack
                    .split('\n')[2]
                    .trim()
                    .substr(3);
            context.comp = context.comp || constants.COMPONENT_NAME;
            logger[level](context, message, err.message || JSON.stringify(err));
        } else {
            logger[level](message, err.message || JSON.stringify(err));
        }
    }
}

function expandVar(val, mapping) {
    if (typeof val === 'string') {
        Object.keys(mapping).forEach(function(p) {
            val = val.replace(new RegExp('\\$\\{' + p + '\\}', 'g'), mapping[p]);
        });
        val = val.replace(/\$\{\w*\}/g, '[?]');
        try {
            // Check if "${num}" and expand it as real value, non string
            var n = JSON.parse(val);
            if (typeof n == 'number') {
                val = n;
            }
        } catch (e) {
            // keep val value
        }
    }
    return val;
}

function expandObject(templateObj, dictionary) {
    var res = {};
    if (templateObj && typeof templateObj === 'object') {
        Object.keys(templateObj).forEach(function(key) {
            if (typeof templateObj[key] === 'string') {
                res[expandVar(key, dictionary)] = expandVar(templateObj[key], dictionary);
            } else if (typeof templateObj[key] === 'object') {
                res[expandVar(key, dictionary)] = expandObject(templateObj[key], dictionary);
            }
        });
    }
    return res;
}

// Think better if this is the best way
function flattenMap(key, targetMap) {
    var newMap = {},
        v,
        flattened,
        fv;
    Object.keys(targetMap).forEach(function(k) {
        v = targetMap[k];
        if (v && typeof v === 'object') {
            flattened = flattenMap(k + '__', v);
            Object.keys(flattened).forEach(function(fk) {
                fv = flattened[fk];
                newMap[key + fk] = fv;
            });
        } else {
            newMap[key + k] = v;
        }
    });
    return newMap;
}

function requestHelperAux(method, options, withMetrics, callback) {
    var localError,
        respObj,
        headers,
        domain = process.domain;
    logger.info('making %s to %s', method, options.url);
    if (withMetrics && domain.context) {
        metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactions);
    }
    headers = options.headers || {};
    if (domain && domain.context) {
        headers[constants.CORRELATOR_HEADER] = domain.context.corr;
        if (domain.context.srv && headers[constants.SERVICE_HEADER] === undefined) {
            headers[constants.SERVICE_HEADER] = domain.context.srv;
        }
        if (domain.context.subsrv && headers[constants.SUBSERVICE_HEADER] === undefined) {
            headers[constants.SUBSERVICE_HEADER] = domain.context.subsrv;
        }
        if (domain.context.from && headers[constants.REALIP_HEADER] === undefined) {
            headers[constants.REALIP_HEADER] = domain.context.from;
        }
    }
    options.headers = headers;
    if (withMetrics && options.json && domain.context) {
        try {
            metrics.IncMetrics(
                domain.context.srv,
                domain.context.subsrv,
                metrics.outgoingTransactionsRequestSize,
                Buffer.byteLength(JSON.stringify(options.body), 'utf-8')
            );
        } catch (exception) {
            logger.warn(exception);
        }
    }
    request[method](options, function cbRequest2core(err, response, body) {
        var bodySz = 0;
        if (withMetrics && domain.context) {
            if (body) {
                if (typeof body === 'string') {
                    bodySz = Buffer.byteLength(body);
                } else {
                    try {
                        bodySz = Buffer.byteLength(JSON.stringify(body));
                    } catch (ex) {
                        logger.warn(ex);
                    }
                }
                metrics.IncMetrics(
                    domain.context.srv,
                    domain.context.subsrv,
                    metrics.outgoingTransactionsResponseSize,
                    bodySz
                );
            }
        }
        if (err) {
            logErrorIf(err, util.format('error %s to %s', method, options.url));
            if (withMetrics && domain.context) {
                metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors);
            }
            return callback(err, null);
        }
        respObj = { code: response.statusCode, body: body };
        logger.debug('%s to %s returns %j', method, options.url, respObj);
        if (response.statusCode < 200 || response.statusCode >= 300) {
            localError = new Error(
                util.format(
                    'error %s to %s (%s)',
                    method,
                    options.url,
                    (body && body.error) || (body && JSON.stringify(body)) || response.statusCode
                )
            );
            localError.httpCode = 500;
            logErrorIf(localError, domain && domain.context);
            if (withMetrics && domain.context) {
                metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.outgoingTransactionsErrors);
            }
            return callback(localError, respObj);
        }
        logger.info('done %s to %s', method, options.url);
        return callback(err, respObj);
    });
}

function requestHelper(method, options, callback) {
    return requestHelperAux(method, options, true, callback);
}

function requestHelperWOMetrics(method, options, callback) {
    return requestHelperAux(method, options, false, callback);
}

function respondAux(resp, err, data, withCount, raw, withMetrics) {
    var statusCode = 200,
        errMsg = null,
        respObj,
        respStr,
        domain = process.domain;
    if (err) {
        errMsg = err.message;
        statusCode = err.httpCode || 500;
        data = null;
    }

    if (raw === true) {
        if (err) {
            respObj = err;
            delete respObj.httpCode;
        } else {
            respObj = data;
        }
    } else {
        // non-raw
        respObj = { error: errMsg, data: data };
        if (withCount === true && data && util.isArray(data)) {
            respObj.count = data.length;
        }
    }
    logger.info('sending response: %s %j', statusCode, respObj);
    respStr = JSON.stringify(respObj);
    if (withMetrics && domain && domain.context) {
        metrics.IncMetrics(
            domain.context.srv,
            domain.context.subsrv,
            metrics.incomingTransactionsResponseSize,
            Buffer.byteLength(respStr, 'utf-8')
        );
        if (err) {
            metrics.IncMetrics(domain.context.srv, domain.context.subsrv, metrics.incomingTransactionsErrors);
        }
    }
    resp.set('Content-Type', 'application/json');
    resp.status(statusCode);
    resp.send(respStr);
}

function respond(resp, err, data, withCount, raw) {
    return respondAux(resp, err, data, withCount, raw, true);
}

function respondWOMetrics(resp, err, data, withCount, raw) {
    return respondAux(resp, err, data, withCount, raw, false);
}

function firstChars(result) {
    var text;
    if (result === undefined) {
        result = 'undefined';
    }
    text = JSON.stringify(result);
    if (text.length > 125) {
        text = text.substr(0, 125) + ' [...]';
    }
    return text;
}

function purgeName(name) {
    return name ? name.replace(/\//g, '$') : name;
}

function contextName(rule) {
    return util.format('ctxt$%s%s', purgeName(rule.service), purgeName(rule.subservice));
}

function ruleUniqueName(rule) {
    return util.format('%s@%s%s', rule.name, rule.service, rule.subservice);
}

function contextEPL(rule) {
    return util.format(
        'create context %s partition by service from iotEvent(service="%s" and subservice="%s")',
        contextName(rule),
        rule.service,
        rule.subservice
    );
}

function ruleWithContext(rule) {
    return util.format('context %s %s', contextName(rule), rule.text);
}

/**
 * expandVar substitutes every variable in val (denoted as $(var}) with the value
 * in mappings (as dictionary), getting the key 'var' from the object
 *
 * @param  {string}   'template' in which to replace variables for values
 * @param  {Object}   variable name to substitute
 */
module.exports.expandVar = expandVar;

module.exports.expandObject = expandObject;

/**
 * flattenMap flatten a map recursively using '__' to nested maps
 *
 * @param  {string} key to append to every nested key
 * @param  {Object} object to put the flattened keys
 */
module.exports.flattenMap = flattenMap;

/**
 * requestHelper makes an HTTP request.
 *
 * @param {string}       method  ('GET', 'POST', 'PUT', 'DELETE')
 * @param {Object}       options, as accepted by 'request' library
 * @param {function}     callback function(error, response)
 */
module.exports.requestHelper = requestHelper;
module.exports.requestHelperWOMetrics = requestHelperWOMetrics;

/**
 * respond sends an HTTP response with the proper code for the error passed in, if any.
 * Also it can add the length if the object to send is in Array and it is asked for.
 *
 * @param {Object}       response object form Express
 * @param {Object}       error object or null
 * @param {Object}       data to send as JSON
 * @param {boolean}      if data is an Array, add a count field with its length
 */
module.exports.respond = respond;
module.exports.respondWOMetrics = respondWOMetrics;

/**
 * logErrorIf writes an error if passed in. Optionally a message can be add and the level for log can be set
 *
 * @param {Object}       error, if null, nothing will be logged
 * @param {string}       message to add, optional
 * @param {string}       level to log to, optional. 'error' by default
 */
module.exports.logErrorIf = logErrorIf;
/**
 * firstChars returns first characters of a string
 *
 * @param  {string} string to trim, if necessary
 */
module.exports.firstChars = firstChars;
/**
 * ruleUniqueName returns a unique name for a rule, including subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.ruleUniqueName = ruleUniqueName;

/**
 * ruleWithContext returns the text of a rule with the context information add (currently the text)
 *
 * @param  {Object} Object rule
 */
module.exports.ruleWithContext = ruleWithContext;
/**
 * contextRuleText returns the EPL text for creating a context for subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.contextEPL = contextEPL;

/**
 * contextRuleName returns the name of a context for subservice and service names
 *
 * @param  {Object} Object rule
 */
module.exports.contextName = contextName;
<|endoftext|>


<commit_before>"use strict";

require("es6-shim");

var fs =  require("fs");
var shapefile = require('shapefile');
var Map = require('es6-map');
var proj4 = require('proj4');

// Metropolitan France
proj4.defs["EPSG:2154"] = "+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs";
var epsg2154 = proj4.defs['EPSG:2154'];
var projector = proj4(epsg2154, proj4.WGS84);

// for Guadeloupe, Saint-Barthélemy, Saint-Martin, Martinique
proj4.defs["EPSG:32620"] = "+proj=utm +zone=20 +ellps=WGS84 +datum=WGS84 +units=m +no_defs ";
var UTM20 = proj4.defs["EPSG:32620"];
var projectorUTM20 = proj4(UTM20, proj4.WGS84);

// for Guyane
proj4.defs["EPSG:2972"] = "+proj=utm +zone=22 +ellps=GRS80 +towgs84=2,2,-2,0,0,0,0 +units=m +no_defs ";
var UTM22 = proj4.defs["EPSG:2972"];
var projectorUTM22 = proj4(UTM22, proj4.WGS84);

// for Réunion
proj4.defs["EPSG:2975"] = "+proj=utm +zone=40 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ";
var UTM40 = proj4.defs["EPSG:2975"];
var projectorUTM40 = proj4(UTM40, proj4.WGS84);

var output = fs.createWriteStream("data/iris.json");
output.write('{"type": "FeatureCollection", "features": [\n');

// unzip files
var Zip = require('node-7z'); // Name the class as you want!
var unzipTask = new Zip();

var shapefiles = [];

unzipTask.extractFull('data/iris-france.7z', 'data/iris')
   .progress(function (file) {
      file.forEach(function(fileName) {
         if (/\.shp$/.test(fileName)) {
            shapefiles.push("data/iris/" + fileName);
         }
      })
   })
   .then(function () {
      console.log('Done extracting shapefiles: ', shapefiles.length);

      var nbShapeFileProcessed = shapefiles.length;

      // convert shapefiles to geojson
      var promises = shapefiles.map(function(file){

         return new Promise(function(resolve, reject){

            parseShapeFile(file)
               .then(function(iris){
                  console.log("Nb iris parsed: ", iris.length);

                  // write to file
                  output.write(iris.join(",\n"));
                  if (nbShapeFileProcessed !== 1)
                     output.write(",\n");
                  nbShapeFileProcessed--;
                  resolve();
               })
               .catch(function(error){
                  console.log("Error in parseShapeFile. : ", error);
                  reject();
               })
         });
      });

      var allFilesProcessed = Promise.all(promises);

      // close the file
      allFilesProcessed
         .then(function(){
            console.log("Finishing")
            output.write("]}");
         })
         .catch(function(error){
            console.log("Could not finish correctly.", error)
         })

   })
  .catch(function (err) {
      console.error(err);
   })

var parseShapeFile = function(file){

   return new Promise(function(resolve, reject){

      var output = [];

      var reader = shapefile.reader(file)
      reader.readHeader(function(error, header) {
         if (error) reject(error);
         readNextRecord();
      });

      function readNextRecord() {
         reader.readRecord(function(error, record) {
            if (error)  reject(error);
            if (record === shapefile.end) {
               console.log("end of file : ", file);
               resolve(output);
               return reader.close();
            } else {
               // coordinates to convert
               if(record){

                  var adaptedProjection = switch(record.properties.DEPCOM.substring(0, 3)) {
                     // for Guadeloupe, Saint-Barthélemy, Saint-Martin, Martinique
                     case '971': // Guadeloupe
                     case '977': // Saint-Barthélemy
                     case '978': // Saint-Martin
                     case '972': // Martinique
                        var newCoord = coord.map(function(c) {
                           return projectorUTM20;
                        });
                     break;

                     // for Guyane
                     case '973':
                        var newCoord = coord.map(function(c) {
                           return projectorUTM22;
                        });
                     break;

                     // for Réunion
                     case '974':
                        var newCoord = coord.map(function(c) {
                           return projectorUTM40;
                        });
                        break;

                     default:
                        var newCoord = coord.map(function(c) {
                           return projector;
                        });
                  }

                  if(record.geometry.type === 'Polygon') {
                     var newCoord = coord.map(function(c){return adaptedProjection.forward(c)});
                  } else { // for MultiPolygon
                     var newCoord = coord.map(function(p){return p.map(function(c){return adaptedProjection.forward(c)})});
                  }
                  record.geometry.coordinates = [newCoord];
                  output.push(JSON.stringify(record));
               }
            };
            setImmediate(readNextRecord);
         });
      }
  });
};

<commit_msg>Correction switch<commit_after>"use strict";

require("es6-shim");

var fs =  require("fs");
var shapefile = require('shapefile');
var Map = require('es6-map');
var proj4 = require('proj4');

// Metropolitan France
proj4.defs["EPSG:2154"] = "+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs";
var epsg2154 = proj4.defs['EPSG:2154'];
var projector = proj4(epsg2154, proj4.WGS84);

// for Guadeloupe, Saint-Barthélemy, Saint-Martin, Martinique
proj4.defs["EPSG:32620"] = "+proj=utm +zone=20 +ellps=WGS84 +datum=WGS84 +units=m +no_defs ";
var UTM20 = proj4.defs["EPSG:32620"];
var projectorUTM20 = proj4(UTM20, proj4.WGS84);

// for Guyane
proj4.defs["EPSG:2972"] = "+proj=utm +zone=22 +ellps=GRS80 +towgs84=2,2,-2,0,0,0,0 +units=m +no_defs ";
var UTM22 = proj4.defs["EPSG:2972"];
var projectorUTM22 = proj4(UTM22, proj4.WGS84);

// for Réunion
proj4.defs["EPSG:2975"] = "+proj=utm +zone=40 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ";
var UTM40 = proj4.defs["EPSG:2975"];
var projectorUTM40 = proj4(UTM40, proj4.WGS84);

var output = fs.createWriteStream("data/iris.json");
output.write('{"type": "FeatureCollection", "features": [\n');

// unzip files
var Zip = require('node-7z'); // Name the class as you want!
var unzipTask = new Zip();

var shapefiles = [];

unzipTask.extractFull('data/iris-france.7z', 'data/iris')
   .progress(function (file) {
      file.forEach(function(fileName) {
         if (/\.shp$/.test(fileName)) {
            shapefiles.push("data/iris/" + fileName);
         }
      })
   })
   .then(function () {
      console.log('Done extracting shapefiles: ', shapefiles.length);

      var nbShapeFileProcessed = shapefiles.length;

      // convert shapefiles to geojson
      var promises = shapefiles.map(function(file){

         return new Promise(function(resolve, reject){

            parseShapeFile(file)
               .then(function(iris){
                  console.log("Nb iris parsed: ", iris.length);

                  // write to file
                  output.write(iris.join(",\n"));
                  if (nbShapeFileProcessed !== 1)
                     output.write(",\n");
                  nbShapeFileProcessed--;
                  resolve();
               })
               .catch(function(error){
                  console.log("Error in parseShapeFile. : ", error);
                  reject();
               })
         });
      });

      var allFilesProcessed = Promise.all(promises);

      // close the file
      allFilesProcessed
         .then(function(){
            console.log("Finishing")
            output.write("]}");
         })
         .catch(function(error){
            console.log("Could not finish correctly.", error)
         })

   })
  .catch(function (err) {
      console.error(err);
   })

var parseShapeFile = function(file){

   return new Promise(function(resolve, reject){

      var output = [];

      var reader = shapefile.reader(file)
      reader.readHeader(function(error, header) {
         if (error) reject(error);
         readNextRecord();
      });

      function readNextRecord() {
         reader.readRecord(function(error, record) {
            if (error)  reject(error);
            if (record === shapefile.end) {
               console.log("end of file : ", file);
               resolve(output);
               return reader.close();
            } else {
               // coordinates to convert
               if(record){

                  var adaptedProjection;
                  switch(record.properties.DEPCOM.substring(0, 3)) {
                     // for Guadeloupe, Saint-Barthélemy, Saint-Martin, Martinique
                     case '971': // Guadeloupe
                     case '977': // Saint-Barthélemy
                     case '978': // Saint-Martin
                     case '972': // Martinique
                           adaptedProjection = projectorUTM20;
                     break;

                     // for Guyane
                     case '973':
                           adaptedProjection = projectorUTM22;
                     break;

                     // for Réunion
                     case '974':
                           adaptedProjection = projectorUTM40;
                        break;

                     default:
                           adaptedProjection = projector;
                  }

                  var coord = record.geometry.coordinates[0];

                  if(record.geometry.type === 'Polygon') {
                     var newCoord = coord.map(function(c){return adaptedProjection.forward(c)});
                  } else { // for MultiPolygon
                     var newCoord = coord.map(function(p){return p.map(function(c){return adaptedProjection.forward(c)})});
                  }
                  record.geometry.coordinates = [newCoord];
                  output.push(JSON.stringify(record));
               }
            };
            setImmediate(readNextRecord);
         });
      }
  });
};

<|endoftext|>


<commit_before>'use strict';

describe('userCampaignsCtrl', function() {
  var userCampaignsCtrl, scope, userid, $httpBackend;
  //Loading the main Application module
  beforeEach(module(ApplicationConfiguration.applicationModuleName));
  beforeEach(inject(function($controller, $rootScope, _$httpBackend_, Authentication) {
    scope = $rootScope.$new();
    Authentication.user = {
        _id: '54da2257568f9cfd6d2dba2f'
    };
    userid = '54da2257568f9cfd6d2dba2f';
    $httpBackend = _$httpBackend_;
    userCampaignsCtrl = $controller('userCampaignsCtrl', {
      $scope: scope,
    });
  }));
  it('should return all campaigns created by a user', function() {
    $httpBackend.when('GET', '/campaigns/' + userid).respond(200, {
        '_id' : '54e4b7eba241567dbbec7122',
        'lastModifiedBy' : '54da2257568f9cfd6d2dba2f',
      }
    );
    console.log(scope.myCampaigns);
    $httpBackend.flush();
    console.log(scope.myCampaigns);
    expect(typeof scope.myCampaigns).toBe('object');
  });
});<commit_msg>removed all console.log lines in test file<commit_after>'use strict';

describe('userCampaignsCtrl', function() {
  var userCampaignsCtrl, scope, userid, $httpBackend;
  //Loading the main Application module
  beforeEach(module(ApplicationConfiguration.applicationModuleName));
  beforeEach(inject(function($controller, $rootScope, _$httpBackend_, Authentication) {
    scope = $rootScope.$new();
    Authentication.user = {
        _id: '54da2257568f9cfd6d2dba2f'
    };
    userid = '54da2257568f9cfd6d2dba2f';
    $httpBackend = _$httpBackend_;
    userCampaignsCtrl = $controller('userCampaignsCtrl', {
      $scope: scope,
    });
  }));
  it('should return all campaigns created by a user', function() {
    $httpBackend.when('GET', '/campaigns/' + userid).respond(200, {
        '_id' : '54e4b7eba241567dbbec7122',
        'lastModifiedBy' : '54da2257568f9cfd6d2dba2f',
      }
    );
    $httpBackend.flush();
    expect(typeof scope.myCampaigns).toBe('object');
  });
});<|endoftext|>