<commit_before>/*
  Turn a standard form into an 'ajax' form.

    -- Relies on the action that the form points to to return a 422 with a new,
    'errored' form if there is a (server-side) validation problem.
    Otherwise calls the various callbacks..

    Extra arguments:
      - status_sel: the selector of an element to set the classes 'submitting/success/error'
          on depending on the state of the form. Defaults to the form itself.
      - validators: a hash of input selectors to validation functions (or names)
          for client-side validation.
*/

(function($){
  $.ediface_widgets.ajax_form = function() {
    return this.each(function(){
      var settings = $(this).read_options({
        status_sel: this, // element to set to .submitting/.success/.error as we submit
        //default callbacks
        error: function(x, t, e, form){
          if (x.status >= 400 && x.status < 500) { // a CLIENT ERROR
            //validation failed replace form with new one
            var $new_form = $(x.responseText);
            $(form).replaceWith($new_form);
            // -- not sure this is the best way to pass extra settings through, but it seems to work.
            $new_form.ajax_form(settings);
          } else if (x.status >= 500 && x.status < 600) { // a SERVER ERROR
            // replace the whole page -- FIXME -- do this properly
            $('body').replaceWith(x.responseText);
          } else {
            alert('Form failed. Please try again.');
          }
        },
        success: function(d, s, form){},
        complete: function(x, t, form){},
        submit: function(e){}, // return false to cancel submit
        validators: {}
      });

      // prepare the validators that are references
      for (var selector in settings.validators) {
        var validator = settings.validators[selector];
        if (typeof(validator) === 'string') {
          validator = settings.validators[selector] = $.ediface_widgets.ajax_form.validators[validator];
        }
        $(selector).bind('change.ajax_form focusout.ajax_form', function(event) {
          validate($(this), validator);
        });
      }

      function set_status_class(name) {
        $.each(['form_submitting', 'form_success', 'form_error'], function(i, old_name) {
          $(settings.status_sel).removeClass(old_name);
        });
        $(settings.status_sel).addClass(name);
      };

      $(this).bind("submit.ajax_form", function(ev){
        var form = this;

        ev.preventDefault();

        // do client-side validations
        var valid = true, $first_error;
        for (var selector in settings.validators) {
          if (!validate($(selector), settings.validators[selector])) {
            if (valid) { valid = false; $first_error = $(selector); }
          }
        }
        if (!valid) { $first_error.focus(); return false; }

        // run the user defined submit function
        if (settings.submit(ev) === false) return false;

        // disable the first submit button on the form
        $(form).find('input[type=submit]:first').attr('disabled', true);

        // set the status
        set_status_class('form_submitting');

        // send up the form and process the results
        $.ajax({
          cache: false,
          data: $.param($(form).serializeArray()),
          type: form.method,
          url: form.action,
          error: function (x, t, e) {
            // console.log('error');
            settings.error(x, t, e, form);
            set_status_class('form_error');
          },
          success: function (d, s) {
            // console.log('success');
            settings.success(d, s, form);
            set_status_class('form_success');
          },
          complete: function (x, t) {
            // console.log('complete');
            // enable the first submit button on the form
            $(form).find('input[type=submit]:first').attr('disabled', false);

            settings.complete(x, t, form);
          }
        });

        return false;
      });
    });
  };

  // call this on a div that represents a form element.
  // sets the right classes and enables disables actual form elements
  $.fn.form_element_state = function(command) {
    switch (command) {
      case 'disabled':
        this.addClass('disabled')
          .find('input, select').attr('disabled', true);
        break;
      case 'enabled':
        this.removeClass('disabled')
          .find('input, select').removeAttr('disabled');
        break;
    }
    return this;
  };

  function clearError(id) {
    // run over the input and the label pointing to it
    $('.fieldWithErrors').find('> #' + id + ', > label[for=' + id + ']').unwrap();
    $('label[for=' + id  + ']').next('.formError').remove();
  };

  function addError(id, error) {
    // right now this is causing focus issues and we don't need it, so I won't waste time
    // fixing it.... but it could be used for different form layouts...
    // $('#' + id + ', label[for=' + id + ']').wrap('

');
    $('label[for=' + id  + ']').after('
' + error + '
');
  };

  // calls a validator, and takes care of setting the errors string
  function validate($input, validator) {
    var id = $input.attr('id');
    // clear existing validations
    clearError(id);

    var error = validator($input);
    if (error === true) {
      return true;
    } else {
      addError(id, error);
      return false;
    }
  }

  // validators expect a form element and return true or an error message
  $.ediface_widgets.ajax_form.validators = {
    not_empty: function($input) {
      return $input.val() ? true : 'must not be empty';
    }
  };

  // standardize .focus() for input tags. IE doesn't place the focus at the
  // end of the input box, this fixes it
  $.fn.inputFocus = function() {
    return this.each(function(){
      if ($.browser.msie) {
        if (this.createTextRange) {
          var FieldRange = this.createTextRange();
          FieldRange.moveStart('character', this.value.length);
          FieldRange.collapse();
          FieldRange.select();
        }
      } else {
        this.focus();
      }
    });
  };
}(jQuery));

<commit_msg>A couple of fixes to the ajax_form<commit_after>/*
  Turn a standard form into an 'ajax' form.

    -- Relies on the action that the form points to to return a 422 with a new,
    'errored' form if there is a (server-side) validation problem.
    Otherwise calls the various callbacks..

    Extra arguments:
      - status_sel: the selector of an element to set the classes 'submitting/success/error'
          on depending on the state of the form. Defaults to the form itself.
      - validators: a hash of input selectors to validation functions (or names)
          for client-side validation.
*/

(function($){
  $.ediface_widgets.ajax_form = function() {
    return this.each(function(){
      var settings = $(this).read_options({
        status_sel: this, // element to set to .submitting/.success/.error as we submit
        //default callbacks
        error: function(x, t, e, form){
          if (x.status >= 400 && x.status < 500) { // a CLIENT ERROR
            //validation failed replace form with new one
            var $new_form = $(x.responseText);
            $(form).replaceWith($new_form);
            // -- not sure this is the best way to pass extra settings through, but it seems to work.
            $new_form.create_widget('ajax_form', settings);
          } else if (x.status >= 500 && x.status < 600) { // a SERVER ERROR
            // replace the whole page -- FIXME -- do this properly
            $('body').replaceWith(x.responseText);
          } else {
            alert('Form failed. Please try again.');
          }
        },
        success: function(d, s, form){},
        complete: function(x, t, form){},
        submit: function(e){}, // return false to cancel submit
        validators: {}
      });

      // prepare the validators that are references
      for (var selector in settings.validators) {
        var validator = settings.validators[selector];
        if (typeof(validator) === 'string') {
          validator = settings.validators[selector] = $.ediface_widgets.ajax_form.validators[validator];
        }
        $(selector).bind('change.ajax_form', {validator: validator}, function(event) {
          validate($(this), event.data.validator);
        });
      }

      function set_status_class(name) {
        $.each(['form_submitting', 'form_success', 'form_error'], function(i, old_name) {
          $(settings.status_sel).removeClass(old_name);
        });
        $(settings.status_sel).addClass(name);
      };

      $(this).bind("submit.ajax_form", function(ev){
        var form = this;

        ev.preventDefault();

        // do client-side validations
        var valid = true, $first_error;
        for (var selector in settings.validators) {
          if (!validate($(selector), settings.validators[selector])) {
            if (valid) { valid = false; $first_error = $(selector); }
          }
        }
        if (!valid) { $first_error.focus(); return false; }

        // run the user defined submit function
        if (settings.submit(ev) === false) return false;

        // disable the first submit button on the form
        $(form).find('input[type=submit]:first').attr('disabled', true);

        // set the status
        set_status_class('form_submitting');

        // send up the form and process the results
        $.ajax({
          cache: false,
          data: $.param($(form).serializeArray()),
          type: form.method,
          url: form.action,
          error: function (x, t, e) {
            // console.log('error');
            settings.error(x, t, e, form);
            set_status_class('form_error');
          },
          success: function (d, s) {
            // console.log('success');
            settings.success(d, s, form);
            set_status_class('form_success');
          },
          complete: function (x, t) {
            // console.log('complete');
            // enable the first submit button on the form
            $(form).find('input[type=submit]:first').attr('disabled', false);

            settings.complete(x, t, form);
          }
        });

        return false;
      });
    });
  };

  // call this on a div that represents a form element.
  // sets the right classes and enables disables actual form elements
  $.fn.form_element_state = function(command) {
    switch (command) {
      case 'disabled':
        this.addClass('disabled')
          .find('input, select').attr('disabled', true);
        break;
      case 'enabled':
        this.removeClass('disabled')
          .find('input, select').removeAttr('disabled');
        break;
    }
    return this;
  };

  function clearError(id) {
    // run over the input and the label pointing to it
    $('.fieldWithErrors').find('> #' + id + ', > label[for=' + id + ']').unwrap();
    $('label[for=' + id  + ']').next('.formError').remove();
  };

  function addError(id, error) {
    // right now this is causing focus issues and we don't need it, so I won't waste time
    // fixing it.... but it could be used for different form layouts...
    // $('#' + id + ', label[for=' + id + ']').wrap('

');
    $('label[for=' + id  + ']').after('
' + error + '
');
  };

  // calls a validator, and takes care of setting the errors string
  function validate($input, validator) {
    var id = $input.attr('id');
    // clear existing validations
    clearError(id);

    var error = validator($input);
    if (error === true) {
      return true;
    } else {
      addError(id, error);
      return false;
    }
  }

  // validators expect a form element and return true or an error message
  $.ediface_widgets.ajax_form.validators = {
    not_empty: function($input) {
      return $input.val() ? true : 'must not be empty';
    }
  };

  // standardize .focus() for input tags. IE doesn't place the focus at the
  // end of the input box, this fixes it
  $.fn.inputFocus = function() {
    return this.each(function(){
      if ($.browser.msie) {
        if (this.createTextRange) {
          var FieldRange = this.createTextRange();
          FieldRange.moveStart('character', this.value.length);
          FieldRange.collapse();
          FieldRange.select();
        }
      } else {
        this.focus();
      }
    });
  };
}(jQuery));

<|endoftext|>


<commit_before>var util = require('util'),
  Store = require('../base'),
  _ = require('lodash'),
  debug = require('debug')('denormalizer:revisionGuardStore:mongodb'),
  ConcurrencyError = require('../../errors/concurrencyError'),
  mongo = Store.use('mongodb'),
  mongoVersion = Store.use('mongodb/package.json').version,
  isNew = mongoVersion.indexOf('1.') !== 0,
  ObjectID = isNew ? mongo.ObjectID : mongo.BSONPure.ObjectID;

function Mongo(options) {
  Store.call(this, options);

  var defaults = {
    host: 'localhost',
    port: 27017,
    dbName: 'readmodel',
    collectionName: 'revision'//,
    // heartbeat: 60 * 1000
  };

  _.defaults(options, defaults);

  var defaultOpt = {
    ssl: false
  };

  options.options = options.options || {};

  if (isNew) {
    defaultOpt.autoReconnect = false;
    _.defaults(options.options, defaultOpt);
  } else {
    defaultOpt.auto_reconnect = false;
    _.defaults(options.options, defaultOpt);
  }

  this.options = options;
}

util.inherits(Mongo, Store);

_.extend(Mongo.prototype, {

  connect: function (callback) {
    var self = this;

    var options = this.options;

    var connectionUrl;

    if (options.url) {
      connectionUrl = options.url;
    } else {
      var members = options.servers
        ? options.servers
        : [{host: options.host, port: options.port}];

      var memberString = _(members).map(function(m) { return m.host + ':' + m.port; });
      var authString = options.username && options.password
        ? options.username + ':' + options.password + '@'
        : '';
      var optionsString = options.authSource
        ? '?authSource=' + options.authSource
        : '';

      connectionUrl = 'mongodb://' + authString + memberString + '/' + options.dbName + optionsString;
    }

    var client;

    if (mongo.MongoClient.length === 2) {
      client = new mongo.MongoClient(connectionUrl, options.options);
      client.connect(function(err, cl) {
        if (err) {
          debug(err);
          if (callback) callback(err);
          return;
        }

        self.db = cl.db(cl.s.options.dbName);
        if (!self.db.close) {
          self.db.close = cl.close.bind(cl);
        }
        initDb();
      });
    } else {
      client = new mongo.MongoClient();
      client.connect(connectionUrl, options.options, function(err, db) {
        if (err) {
          debug(err);
          if (callback) callback(err);
          return;
        }

        self.db = db;
        initDb();
      });
    }

    function initDb() {
      self.db.on('close', function() {
        self.emit('disconnect');
        self.stopHeartbeat();
      });

      var finish = function (err) {
        self.store = self.db.collection(options.collectionName);
//        self.store.ensureIndex({ 'aggregateId': 1, date: 1 }, function() {});
        if (!err) {
          self.emit('connect');

          if (self.options.heartbeat) {
            self.startHeartbeat();
          }
        }
        if (callback) callback(err, self);
      };

      finish();
    }
  },

  stopHeartbeat: function () {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      delete this.heartbeatInterval;
    }
  },

  startHeartbeat: function () {
    var self = this;

    var gracePeriod = Math.round(this.options.heartbeat / 2);
    this.heartbeatInterval = setInterval(function () {
      var graceTimer = setTimeout(function () {
        if (self.heartbeatInterval) {
          console.error((new Error ('Heartbeat timeouted after ' + gracePeriod + 'ms (mongodb)')).stack);
          self.disconnect();
        }
      }, gracePeriod);

      self.db.command({ ping: 1 }, function (err) {
        if (graceTimer) clearTimeout(graceTimer);
        if (err) {
          console.error(err.stack || err);
          self.disconnect();
        }
      });
    }, this.options.heartbeat);
  },

  disconnect: function (callback) {
    this.stopHeartbeat();

    if (!this.db) {
      if (callback) callback(null);
      return;
    }

    this.db.close(callback || function () {});
  },

  getNewId: function(callback) {
    callback(null, new ObjectID().toString());
  },

  get: function (id, callback) {
    if (!id || !_.isString(id)) {
      var err = new Error('Please pass a valid id!');
      debug(err);
      return callback(err);
    }

    this.store.findOne({ _id: id }, function (err, entry) {
      if (err) {
        return callback(err);
      }

      if (!entry) {
        return callback(null, null);
      }

      callback(null, entry.revision || null);
    });
  },

  set: function (id, revision, oldRevision, callback) {
    if (!id || !_.isString(id)) {
      var err = new Error('Please pass a valid id!');
      debug(err);
      return callback(err);
    }
    if (!revision || !_.isNumber(revision)) {
      var err = new Error('Please pass a valid revision!');
      debug(err);
      return callback(err);
    }

    this.store.update({ _id: id, revision: oldRevision }, { _id: id, revision: revision }, { safe: true, upsert: true }, function (err, modifiedCount) {
      if (isNew) {
        if (modifiedCount && modifiedCount.result && modifiedCount.result.n === 0) {
          err = new ConcurrencyError();
          debug(err);
          if (callback) {
            callback(err);
          }
          return;
        }
      } else {
        if (modifiedCount === 0) {
          err = new ConcurrencyError();
          debug(err);
          if (callback) {
            callback(err);
          }
          return;
        }
      }
      if (err && err.message && err.message.match(/duplicate key/i)) {
        debug(err);
        err = new ConcurrencyError();
        debug(err);
        if (callback) {
          callback(err);
        }
        return;
      }
      if (callback) { callback(err); }
    });
  },

  saveLastEvent: function (evt, callback) {
    this.store.save({ _id: 'THE_LAST_SEEN_EVENT', event: evt }, { safe: true }, function (err) {
      if (callback) { callback(err); }
    });
  },

  getLastEvent: function (callback) {
    this.store.findOne({ _id: 'THE_LAST_SEEN_EVENT' }, function (err, entry) {
      if (err) {
        return callback(err);
      }

      if (!entry) {
        return callback(null, null);
      }

      callback(null, entry.event || null);
    });
  },

  clear: function (callback) {
    this.store.remove({}, { safe: true }, callback);
  }

});

module.exports = Mongo;
<commit_msg>useNewUrlParser to prevent warning in mongodb<commit_after>var util = require('util'),
  Store = require('../base'),
  _ = require('lodash'),
  debug = require('debug')('denormalizer:revisionGuardStore:mongodb'),
  ConcurrencyError = require('../../errors/concurrencyError'),
  mongo = Store.use('mongodb'),
  mongoVersion = Store.use('mongodb/package.json').version,
  isNew = mongoVersion.indexOf('1.') !== 0,
  ObjectID = isNew ? mongo.ObjectID : mongo.BSONPure.ObjectID;

function Mongo(options) {
  Store.call(this, options);

  var defaults = {
    host: 'localhost',
    port: 27017,
    dbName: 'readmodel',
    collectionName: 'revision'//,
    // heartbeat: 60 * 1000
  };

  _.defaults(options, defaults);

  var defaultOpt = {
    ssl: false
  };

  options.options = options.options || {};

  if (isNew) {
    defaultOpt.autoReconnect = false;
    defaultOpt.useNewUrlParser = true;
    _.defaults(options.options, defaultOpt);
  } else {
    defaultOpt.auto_reconnect = false;
    _.defaults(options.options, defaultOpt);
  }

  this.options = options;
}

util.inherits(Mongo, Store);

_.extend(Mongo.prototype, {

  connect: function (callback) {
    var self = this;

    var options = this.options;

    var connectionUrl;

    if (options.url) {
      connectionUrl = options.url;
    } else {
      var members = options.servers
        ? options.servers
        : [{host: options.host, port: options.port}];

      var memberString = _(members).map(function(m) { return m.host + ':' + m.port; });
      var authString = options.username && options.password
        ? options.username + ':' + options.password + '@'
        : '';
      var optionsString = options.authSource
        ? '?authSource=' + options.authSource
        : '';

      connectionUrl = 'mongodb://' + authString + memberString + '/' + options.dbName + optionsString;
    }

    var client;

    if (mongo.MongoClient.length === 2) {
      client = new mongo.MongoClient(connectionUrl, options.options);
      client.connect(function(err, cl) {
        if (err) {
          debug(err);
          if (callback) callback(err);
          return;
        }

        self.db = cl.db(cl.s.options.dbName);
        if (!self.db.close) {
          self.db.close = cl.close.bind(cl);
        }
        initDb();
      });
    } else {
      client = new mongo.MongoClient();
      client.connect(connectionUrl, options.options, function(err, db) {
        if (err) {
          debug(err);
          if (callback) callback(err);
          return;
        }

        self.db = db;
        initDb();
      });
    }

    function initDb() {
      self.db.on('close', function() {
        self.emit('disconnect');
        self.stopHeartbeat();
      });

      var finish = function (err) {
        self.store = self.db.collection(options.collectionName);
//        self.store.ensureIndex({ 'aggregateId': 1, date: 1 }, function() {});
        if (!err) {
          self.emit('connect');

          if (self.options.heartbeat) {
            self.startHeartbeat();
          }
        }
        if (callback) callback(err, self);
      };

      finish();
    }
  },

  stopHeartbeat: function () {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval);
      delete this.heartbeatInterval;
    }
  },

  startHeartbeat: function () {
    var self = this;

    var gracePeriod = Math.round(this.options.heartbeat / 2);
    this.heartbeatInterval = setInterval(function () {
      var graceTimer = setTimeout(function () {
        if (self.heartbeatInterval) {
          console.error((new Error ('Heartbeat timeouted after ' + gracePeriod + 'ms (mongodb)')).stack);
          self.disconnect();
        }
      }, gracePeriod);

      self.db.command({ ping: 1 }, function (err) {
        if (graceTimer) clearTimeout(graceTimer);
        if (err) {
          console.error(err.stack || err);
          self.disconnect();
        }
      });
    }, this.options.heartbeat);
  },

  disconnect: function (callback) {
    this.stopHeartbeat();

    if (!this.db) {
      if (callback) callback(null);
      return;
    }

    this.db.close(callback || function () {});
  },

  getNewId: function(callback) {
    callback(null, new ObjectID().toString());
  },

  get: function (id, callback) {
    if (!id || !_.isString(id)) {
      var err = new Error('Please pass a valid id!');
      debug(err);
      return callback(err);
    }

    this.store.findOne({ _id: id }, function (err, entry) {
      if (err) {
        return callback(err);
      }

      if (!entry) {
        return callback(null, null);
      }

      callback(null, entry.revision || null);
    });
  },

  set: function (id, revision, oldRevision, callback) {
    if (!id || !_.isString(id)) {
      var err = new Error('Please pass a valid id!');
      debug(err);
      return callback(err);
    }
    if (!revision || !_.isNumber(revision)) {
      var err = new Error('Please pass a valid revision!');
      debug(err);
      return callback(err);
    }

    this.store.update({ _id: id, revision: oldRevision }, { _id: id, revision: revision }, { safe: true, upsert: true }, function (err, modifiedCount) {
      if (isNew) {
        if (modifiedCount && modifiedCount.result && modifiedCount.result.n === 0) {
          err = new ConcurrencyError();
          debug(err);
          if (callback) {
            callback(err);
          }
          return;
        }
      } else {
        if (modifiedCount === 0) {
          err = new ConcurrencyError();
          debug(err);
          if (callback) {
            callback(err);
          }
          return;
        }
      }
      if (err && err.message && err.message.match(/duplicate key/i)) {
        debug(err);
        err = new ConcurrencyError();
        debug(err);
        if (callback) {
          callback(err);
        }
        return;
      }
      if (callback) { callback(err); }
    });
  },

  saveLastEvent: function (evt, callback) {
    this.store.save({ _id: 'THE_LAST_SEEN_EVENT', event: evt }, { safe: true }, function (err) {
      if (callback) { callback(err); }
    });
  },

  getLastEvent: function (callback) {
    this.store.findOne({ _id: 'THE_LAST_SEEN_EVENT' }, function (err, entry) {
      if (err) {
        return callback(err);
      }

      if (!entry) {
        return callback(null, null);
      }

      callback(null, entry.event || null);
    });
  },

  clear: function (callback) {
    this.store.remove({}, { safe: true }, callback);
  }

});

module.exports = Mongo;
<|endoftext|>


<commit_before>var cadence = require('cadence')
var createUsage = require('./usage')
var getopt = require('./getopt')
var util = require('util')
var Interrupt = require('interrupt').createInterrupter('bigeasy.arguable')
var rescue = require('rescue')
var coalesce = require('extant')

// This will never be pretty. Let it be ugly. Let it swallow all the sins before
// they enter your program, so that your program can be a garden of pure
// ideology.

//
function Arguable (usage, argv, options) {
    this._usage = usage

    // These are the merged defintion and invocation options provided by the
    // user.
    this.options = options.options

    // The key used to create the `Destructible`.
    this.identifier = options.identifier

    // We'll use this for an exit code if it is set and if we exit normally.
    this.exitCode = null

    // Are we running as a main module?
    this.isMainModule = options.isMainModule

    // Use environment `LANG` or else language of first usage definition.
    this.lang = coalesce(options.lang, this._usage.language)

    // Extract argument patterns from usage.
    var patterns = this._usage.getPattern()

    // Extract the arguments that accept values, TODO maybe call `valuable`.
    this.arguable = patterns.filter(function (pattern) {
        return pattern.arguable
    }).map(function (pattern) {
        return pattern.verbose
    })

    // Parse arguments and save the remaining arguments.
    try {
        var gotopts = getopt(patterns, argv)
    } catch (error) {
        this.abend(error.abend, error.context)
    }

    // Extract an argument end sigil and note that it was there.
    if (this.terminal = argv[0] == '--')  {
        argv.shift()
    }

    // Slice and dice results into convenience structures.
    this._setParameters(gotopts)

    // Remaining arguments.
    this.argv = argv

    // Assign standard I/O provide in `options`.
    this.stdout = options.stdout
    this.stderr = options.stderr
    this.stdin = options.stdin

    // Assign pipes open to our parent.
    this.pipes = options.pipes
}

// Use an array of key/value pairs to populate some useful shortcut properties
// for working with parameters.
//
// Note to self; we use `parameters` here and in documentation because
// `arguments` is a JavaScript reserved word.

//
Arguable.prototype._setParameters = function (parameters) {
    this.parameters = parameters
    this.given = parameters.map(function (parameter) {
        return parameter.name
    }).filter(function (value, index, arrayj) {
        return arrayj.indexOf(value) == index
    })
    this.arrayed = {}
    this.arguable.forEach(function (name) {
        this.arrayed[name] = []
    }, this)
    this.parameters.forEach(function (parameter) {
        var group = this.arrayed[parameter.name]
        if (group == null) {
            group = this.arrayed[parameter.name] = []
        }
        group.push(parameter.value)
    }, this)
    this.ultimate = {}
    this.given.forEach(function (key) {
        this.ultimate[key] = this.arrayed[key][this.arrayed[key].length - 1]
    }, this)
}

// Assert that there is a value present for a required argument.
Arguable.prototype.required = function () {
    for (var i = 0, I = arguments.length; i < I; i++) {
        if (!(arguments[i] in this.ultimate)) {
            this.abend(arguments[i] + ' is required')
        }
    }
}

// Variadic nonsense to support handful of different ways to invoke this
// validation function. The validation can change the type and value of the
// parameters in the in the parameters array, so valiation is also
// transmogrifcation of strings into appropriately typed and structured
// parameters for your program.
Arguable.prototype.validate = function () {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var validator = null
    var type = typeof vargs[0]
    if (type == 'string' && ~vargs[0].indexOf('%s')) {
        var format = vargs.shift()
        var test = vargs.pop()
        var valid = test instanceof RegExp ? function (value) {
            return test.test(value)
        } : test
        // The validator will throw a format to use to format the error message.
        // Not sure why I've decied to get so wicked with the throws.
        validator = function (value) {
            if (!valid(value)) {
                throw format
            }
        }
    } else if (type == 'function') {
        validator = vargs.shift()
    } else {
        validator = vargs.pop()
    }
    var parameters = this.parameters.map(function (parameter) {
        try {
            if (!~vargs.indexOf(parameter.name)) {
                return parameter
            }
            var value = validator(parameter.value, parameter.name, this)
            if (value !== (void(0))) {
                parameter.value = value
            }
            return parameter
        } catch (error) {
            if (!(typeof error == 'string' && ~error.indexOf('%s'))) {
                throw error
            }
            this.abend(util.format(error, parameter.name), parameter.value, parameter.name)
        }
    }.bind(this))
    this._setParameters(parameters)
}

// Format a message using the string tables provided in the usage message.
Arguable.prototype.format = function (key) {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var key = vargs.shift()
    return this._usage.format(this.lang, key, vargs)
}

// abend helper stops execution and prints a message
Arguable.prototype.abend = function () {
    console.log('abend')
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var key = vargs.shift(), exitCode = 1
    if (typeof key == 'number') {
        exitCode = key
        key = vargs.shift()
    }
    var message
    if (key) {
        message = this._usage.format(this.lang, key, vargs)
    }
    this._redirect = 'stderr'
    throw new Interrupt('abend', {
        method: 'abend',
        key: key,
        vargs: vargs,
        stderr: message,
        exitCode: exitCode
    })
}

// Stop execution and print help message.
Arguable.prototype.help = function () {
    throw new Interrupt('abend', {
        method: 'help',
        stdout: this._usage.chooseUsage(this.lang),
        exitCode: 0
    })
}

// Assert the given condition is true or else abend with the given abend
// arguments.
Arguable.prototype.assert = function (condition) {
    if (!condition) {
        var vargs = []
        vargs.push.apply(vargs, arguments)
        this.abend.apply(this, vargs.slice(1))
    }
}

// Saves having to write a unit test for every application that checks a branch
// that does exactly this.
Arguable.prototype.helpIf = function (help) {
    if (help) this.help()
}

// Load an arguable module and invoke it using this `Program` as the parent. Not
// at all certain that I want to have all this formatting nonsense. Can't the
// parent simply invoke it with a string?
//
// Look up a delegate module and raise an exception if it is not found.

//
Arguable.prototype.delegate = function (require, format, command) {
    var pkg = util.format(format, command)
    try {
        return require(pkg)
    } catch (error) {
        if (error.code == 'MODULE_NOT_FOUND') {
            this.abend('sub command module not found', command, pkg, format)
        } else {
            throw error
        }
    }
}

// Export `Arugable`.
module.exports = Arguable
<commit_msg>Remove debugging output.<commit_after>var cadence = require('cadence')
var createUsage = require('./usage')
var getopt = require('./getopt')
var util = require('util')
var Interrupt = require('interrupt').createInterrupter('bigeasy.arguable')
var rescue = require('rescue')
var coalesce = require('extant')

// This will never be pretty. Let it be ugly. Let it swallow all the sins before
// they enter your program, so that your program can be a garden of pure
// ideology.

//
function Arguable (usage, argv, options) {
    this._usage = usage

    // These are the merged defintion and invocation options provided by the
    // user.
    this.options = options.options

    // The key used to create the `Destructible`.
    this.identifier = options.identifier

    // We'll use this for an exit code if it is set and if we exit normally.
    this.exitCode = null

    // Are we running as a main module?
    this.isMainModule = options.isMainModule

    // Use environment `LANG` or else language of first usage definition.
    this.lang = coalesce(options.lang, this._usage.language)

    // Extract argument patterns from usage.
    var patterns = this._usage.getPattern()

    // Extract the arguments that accept values, TODO maybe call `valuable`.
    this.arguable = patterns.filter(function (pattern) {
        return pattern.arguable
    }).map(function (pattern) {
        return pattern.verbose
    })

    // Parse arguments and save the remaining arguments.
    try {
        var gotopts = getopt(patterns, argv)
    } catch (error) {
        this.abend(error.abend, error.context)
    }

    // Extract an argument end sigil and note that it was there.
    if (this.terminal = argv[0] == '--')  {
        argv.shift()
    }

    // Slice and dice results into convenience structures.
    this._setParameters(gotopts)

    // Remaining arguments.
    this.argv = argv

    // Assign standard I/O provide in `options`.
    this.stdout = options.stdout
    this.stderr = options.stderr
    this.stdin = options.stdin

    // Assign pipes open to our parent.
    this.pipes = options.pipes
}

// Use an array of key/value pairs to populate some useful shortcut properties
// for working with parameters.
//
// Note to self; we use `parameters` here and in documentation because
// `arguments` is a JavaScript reserved word.

//
Arguable.prototype._setParameters = function (parameters) {
    this.parameters = parameters
    this.given = parameters.map(function (parameter) {
        return parameter.name
    }).filter(function (value, index, arrayj) {
        return arrayj.indexOf(value) == index
    })
    this.arrayed = {}
    this.arguable.forEach(function (name) {
        this.arrayed[name] = []
    }, this)
    this.parameters.forEach(function (parameter) {
        var group = this.arrayed[parameter.name]
        if (group == null) {
            group = this.arrayed[parameter.name] = []
        }
        group.push(parameter.value)
    }, this)
    this.ultimate = {}
    this.given.forEach(function (key) {
        this.ultimate[key] = this.arrayed[key][this.arrayed[key].length - 1]
    }, this)
}

// Assert that there is a value present for a required argument.
Arguable.prototype.required = function () {
    for (var i = 0, I = arguments.length; i < I; i++) {
        if (!(arguments[i] in this.ultimate)) {
            this.abend(arguments[i] + ' is required')
        }
    }
}

// Variadic nonsense to support handful of different ways to invoke this
// validation function. The validation can change the type and value of the
// parameters in the in the parameters array, so valiation is also
// transmogrifcation of strings into appropriately typed and structured
// parameters for your program.
Arguable.prototype.validate = function () {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var validator = null
    var type = typeof vargs[0]
    if (type == 'string' && ~vargs[0].indexOf('%s')) {
        var format = vargs.shift()
        var test = vargs.pop()
        var valid = test instanceof RegExp ? function (value) {
            return test.test(value)
        } : test
        // The validator will throw a format to use to format the error message.
        // Not sure why I've decied to get so wicked with the throws.
        validator = function (value) {
            if (!valid(value)) {
                throw format
            }
        }
    } else if (type == 'function') {
        validator = vargs.shift()
    } else {
        validator = vargs.pop()
    }
    var parameters = this.parameters.map(function (parameter) {
        try {
            if (!~vargs.indexOf(parameter.name)) {
                return parameter
            }
            var value = validator(parameter.value, parameter.name, this)
            if (value !== (void(0))) {
                parameter.value = value
            }
            return parameter
        } catch (error) {
            if (!(typeof error == 'string' && ~error.indexOf('%s'))) {
                throw error
            }
            this.abend(util.format(error, parameter.name), parameter.value, parameter.name)
        }
    }.bind(this))
    this._setParameters(parameters)
}

// Format a message using the string tables provided in the usage message.
Arguable.prototype.format = function (key) {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var key = vargs.shift()
    return this._usage.format(this.lang, key, vargs)
}

// abend helper stops execution and prints a message
Arguable.prototype.abend = function () {
    var vargs = []
    vargs.push.apply(vargs, arguments)
    var key = vargs.shift(), exitCode = 1
    if (typeof key == 'number') {
        exitCode = key
        key = vargs.shift()
    }
    var message
    if (key) {
        message = this._usage.format(this.lang, key, vargs)
    }
    this._redirect = 'stderr'
    throw new Interrupt('abend', {
        method: 'abend',
        key: key,
        vargs: vargs,
        stderr: message,
        exitCode: exitCode
    })
}

// Stop execution and print help message.
Arguable.prototype.help = function () {
    throw new Interrupt('abend', {
        method: 'help',
        stdout: this._usage.chooseUsage(this.lang),
        exitCode: 0
    })
}

// Assert the given condition is true or else abend with the given abend
// arguments.
Arguable.prototype.assert = function (condition) {
    if (!condition) {
        var vargs = []
        vargs.push.apply(vargs, arguments)
        this.abend.apply(this, vargs.slice(1))
    }
}

// Saves having to write a unit test for every application that checks a branch
// that does exactly this.
Arguable.prototype.helpIf = function (help) {
    if (help) this.help()
}

// Load an arguable module and invoke it using this `Program` as the parent. Not
// at all certain that I want to have all this formatting nonsense. Can't the
// parent simply invoke it with a string?
//
// Look up a delegate module and raise an exception if it is not found.

//
Arguable.prototype.delegate = function (require, format, command) {
    var pkg = util.format(format, command)
    try {
        return require(pkg)
    } catch (error) {
        if (error.code == 'MODULE_NOT_FOUND') {
            this.abend('sub command module not found', command, pkg, format)
        } else {
            throw error
        }
    }
}

// Export `Arugable`.
module.exports = Arguable
<|endoftext|>


<commit_before>/* Module dependencies */
var URI = require('URIjs');
var ipaddr = require('ipaddr.js');
var _ = require('lodash');
var _s = require('underscore.string');
var tld = require('tldjs');

function OnlineResource(onlineResource) {
    this.name = onlineResource.name;
    this.sourceLocation = onlineResource.link;
    this.sourceProtocol = onlineResource.protocol;
    this.description = onlineResource.description;

    this.location = this.parseLocation();
    this.query = this.parseAndNormalizeQuery();
}

OnlineResource.prototype.parseLocation = function () {
    if (!this.sourceLocation) throw new Error('location must be defined');

    var uri = new URI(this.sourceLocation);

    if (uri.is('urn')) throw new Error('Location must be an url');
    if (uri.is('relative')) throw new Error('Location must be absolute');
    if (uri.protocol() !== 'http' && uri.protocol() !== 'https') throw new Error('Location protocol must be http ou https');
    if (!uri.is('ip') && !uri.is('name')) throw new Error('Location must contain an IP address or a hostname');

    if (uri.is('ip')) {
        var addr = ipaddr.parse(uri.hostname());
        var range = addr.range();
        var rangesToExclude = [
            'unspecified',
            'broadcast',
            'multicast',
            'linkLocal',
            'loopback',
            'private',
            'reserved'
        ];
        if (range && _.includes(rangesToExclude, range)) throw new Error('IP address must be public');
    }

    if (uri.is('name')) {
        var hostname = uri.hostname();
        if (!tld.tldExists(hostname)) throw new Error('Hostname must be a valid TLD');
        if (_s.endsWith(hostname, 'ader.gouv.fr')) throw new Error('Hostname must be publicly accessible');
    }

    return uri;
};

OnlineResource.prototype.parseAndNormalizeQuery = function () {
    var query = {};

    // Ensure query string param names are lower-cased
    _.forEach(this.location.search(true), function (val, key) {
        query[key.toLowerCase()] = val;
    });

    return query;
};

OnlineResource.prototype.isWfsFeatureType = function () {
    var sourceProtocolContainsWfs = (this.sourceProtocol || '').toLowerCase().indexOf('wfs') >= 0;
    var sourceLocationIsWfsQuery = (this.query.service || '').toLowerCase() === 'wfs';

    var detectWfsProtocol = sourceProtocolContainsWfs || sourceLocationIsWfsQuery;

    if (!detectWfsProtocol) return false;

    var typeNameInQuery = this.query.typename || this.query.typenames || this.query.layers;
    var typeNameFromName = sourceProtocolContainsWfs && this.name;

    this.typeNameFound = typeNameInQuery || typeNameFromName;

    return !!this.typeNameFound;
};

OnlineResource.prototype.getFeatureTypeName = function () {
    return this.typeNameFound;
};

OnlineResource.prototype.getNormalizedWfsServiceLocation = function () {
    var location = this.location.clone();
    location.search(_.pick(this.query, 'map')); // Pick only map param from query string (MapServer)
    location.fragment('').normalize();
    return location.valueOf();
};

OnlineResource.prototype.isWmsLayer = function () {
    var sourceProtocolContainsWms = (this.sourceProtocol || '').toLowerCase().indexOf('wms') >= 0;
    var sourceLocationIsWmsQuery = (this.query.service || '').toLowerCase() === 'wms';

    var detectWmsProtocol = sourceProtocolContainsWms || sourceLocationIsWmsQuery;

    return detectWmsProtocol;
};

OnlineResource.prototype.isWellKnownService = function () {
    return _.includes(['wfs', 'wms'], this.query.service);
};

exports.OnlineResource = OnlineResource;
<commit_msg>Add onlineResources link cleaning<commit_after>/* Module dependencies */
var URI = require('URIjs');
var ipaddr = require('ipaddr.js');
var _ = require('lodash');
var _s = require('underscore.string');
var tld = require('tldjs');

function cleanLocation(location) {
    // Replace tabulations and line breaks by spaces
    location = location.replace(/(\r\n|\n|\r|\t)/gm, ' ');
    // Trim spaces and dashes
    location = _s.trim(location, '- ');
    return location;
}

function OnlineResource(onlineResource) {
    this.name = onlineResource.name;
    this.sourceLocation = onlineResource.link;
    this.sourceProtocol = onlineResource.protocol;
    this.description = onlineResource.description;

    this.location = this.parseLocation();
    this.query = this.parseAndNormalizeQuery();
}

OnlineResource.prototype.parseLocation = function () {
    if (!this.sourceLocation) throw new Error('location must be defined');
    var cleanedLocation = cleanLocation(this.sourceLocation);

    var uri = new URI(cleanedLocation);

    if (!uri.is('url')) throw new Error('Location must be an url');
    if (uri.is('relative')) throw new Error('Location must be absolute');
    if (uri.protocol() !== 'http' && uri.protocol() !== 'https') throw new Error('Location protocol must be http ou https');
    if (!uri.is('ip') && !uri.is('name')) throw new Error('Location must contain an IP address or a hostname');

    if (uri.is('ip')) {
        var addr = ipaddr.parse(uri.hostname());
        var range = addr.range();
        var rangesToExclude = [
            'unspecified',
            'broadcast',
            'multicast',
            'linkLocal',
            'loopback',
            'private',
            'reserved'
        ];
        if (range && _.includes(rangesToExclude, range)) throw new Error('IP address must be public');
    }

    if (uri.is('name')) {
        var hostname = uri.hostname();
        if (!tld.tldExists(hostname)) throw new Error('Hostname must be a valid TLD');
        if (_s.endsWith(hostname, 'ader.gouv.fr')) throw new Error('Hostname must be publicly accessible');
    }

    return uri;
};

OnlineResource.prototype.parseAndNormalizeQuery = function () {
    var query = {};

    // Ensure query string param names are lower-cased
    _.forEach(this.location.search(true), function (val, key) {
        query[key.toLowerCase()] = val;
    });

    return query;
};

OnlineResource.prototype.isWfsFeatureType = function () {
    var sourceProtocolContainsWfs = (this.sourceProtocol || '').toLowerCase().indexOf('wfs') >= 0;
    var sourceLocationIsWfsQuery = (this.query.service || '').toLowerCase() === 'wfs';

    var detectWfsProtocol = sourceProtocolContainsWfs || sourceLocationIsWfsQuery;

    if (!detectWfsProtocol) return false;

    var typeNameInQuery = this.query.typename || this.query.typenames || this.query.layers;
    var typeNameFromName = sourceProtocolContainsWfs && this.name;

    this.typeNameFound = typeNameInQuery || typeNameFromName;

    return !!this.typeNameFound;
};

OnlineResource.prototype.getFeatureTypeName = function () {
    return this.typeNameFound;
};

OnlineResource.prototype.getNormalizedWfsServiceLocation = function () {
    var location = this.location.clone();
    location.search(_.pick(this.query, 'map')); // Pick only map param from query string (MapServer)
    location.fragment('').normalize();
    return location.valueOf();
};

OnlineResource.prototype.isWmsLayer = function () {
    var sourceProtocolContainsWms = (this.sourceProtocol || '').toLowerCase().indexOf('wms') >= 0;
    var sourceLocationIsWmsQuery = (this.query.service || '').toLowerCase() === 'wms';

    var detectWmsProtocol = sourceProtocolContainsWms || sourceLocationIsWmsQuery;

    return detectWmsProtocol;
};

OnlineResource.prototype.isWellKnownService = function () {
    return _.includes(['wfs', 'wms'], this.query.service);
};

exports.OnlineResource = OnlineResource;
<|endoftext|>


<commit_before>module( 'plugins.anchor' );

test( '插入锚点后切换源码', function() {
    var editor = te.obj[0];
    var range = te.obj[1];
    var body = editor.body;
    stop();
    var br = baidu.editor.browser.ie ? ' ' : '
';     //1.2版本,ie中‘’-〉' '
    setTimeout( function() {
        editor.setContent( '
' + br + '
' );
        range.setStart( body.firstChild, 0 ).collapse( 1 ).select();
        editor.execCommand( 'anchor', 'hello' );
        ua.checkHTMLSameStyle( '' + br, editor.document, body.firstChild, '检查锚点html' );     //1.2版本后，在img前有的不可见字符没有删去，这里改成之比较img内的内容
        ok(body.getElementsByTagName('img')[0].attributes['anchorname'].nodeValue=="hello"&&body.getElementsByTagName('img')[0].attributes['class'].nodeValue=="anchorclass",'检查锚点');
        editor.execCommand( 'source' );     /*切到源码模式下会有一个超时*/
        setTimeout( function() {
            var tas = editor.iframe.parentNode.getElementsByTagName( 'textarea' );
            if(ua.browser.chrome){
                ok( editor.iframe.nextSibling.textContent.indexOf( '<a name="hello"' ) !=-1, '查看是否转换成功' );
            }
            else{
                ok( tas[0].value.indexOf( '<a name="hello"' ) != -1 || tas[0].value.indexOf( '<a anchorname="1"' ) != -1, '查看是否转换成功' );
            }
          /*没办法比，看上去一样，但是一个42个字符，一个48个字符
             * ok((tas[0].value=='

')||(tas[0].value=='

'),'检查源码');*/
            editor.execCommand( 'source' );
            ua.checkHTMLSameStyle( '' + br, editor.document, body.firstChild, '检查锚点html' );
            setTimeout( function() {
                start();
            }, 50 );
        }, 100);
    }, 20 );
} );

test( '在源码模式设置超链接的name属性，切换到编辑器模式检查超链接是否变为锚点', function() {
    var editor = te.obj[0];
    var body = editor.body;
    stop();
    setTimeout(function(){
        editor.setContent( '' );
        setTimeout( function() {
            editor.execCommand( 'source' );
            setTimeout( function() {
                var ta = editor.iframe.parentNode.getElementsByTagName( 'textarea' )[0];
                ta.value = '

';       /*这种情况认为是锚点*/
                setTimeout( function() {
                    editor.execCommand( 'source' );
                    ua.checkHTMLSameStyle( '', editor.document, body.firstChild, '检查锚点html' );
                    start();
                }, 100 );
            }, 100 );
        }, 100 );
    },100);
} );

test( '在源码模式设置超链接没有name属性，切换到编辑器模式检查超链接不变为锚点', function() {
    var editor = te.obj[0];
    editor.setContent( '' );
    var body = editor.body;
    stop();
    setTimeout( function() {
        editor.execCommand( 'source' );
        setTimeout( function() {
            var ta = editor.iframe.parentNode.getElementsByTagName( 'textarea' )[0];
            ta.value = '
你好
';
            setTimeout( function() {
                editor.execCommand( 'source' );
                ua.manualDeleteFillData(editor.body);
//                equal( body.firstChild.firstChild.tagName.toLowerCase(), 'a', 'a标签不会转化' );
                equal( body.firstChild.lastChild.tagName.toLowerCase(), 'a', 'a标签不会转化' );   //兼容opera
                start();
                }, 50 );
        }, 10 );
    }, 20 );
} );

test( '已存在锚点', function() {
    var editor = te.obj[0];
    var range = te.obj[1];
    var body = editor.body;
    var br = baidu.editor.browser.ie ? ' ' : '
';
    editor.setContent( '

' );
    range.selectNode(body.firstChild).select();
    editor.execCommand( 'anchor', 'hello' );
    var name=body.firstChild.firstChild.getAttribute('anchorname');
    equal(name, 'hello', '更改name');
    editor.setContent( '

' );
    range.selectNode(body.firstChild).select();
    editor.execCommand( 'anchor');
    equal(ua.getChildHTML(editor.body),'

','去掉锚点');
} );<commit_msg>[test]用例微调<commit_after>module( 'plugins.anchor' );

test( '插入锚点后切换源码', function() {
    var editor = te.obj[0];
    var range = te.obj[1];
    var body = editor.body;
    stop();
    var br = baidu.editor.browser.ie ? ' ' : '
';     //1.2版本,ie中‘’-〉' '
    setTimeout( function() {
        editor.setContent( '
' + br + '
' );
        range.setStart( body.firstChild, 0 ).collapse( 1 ).select();
        editor.execCommand( 'anchor', 'hello' );
        ua.checkHTMLSameStyle( '' + br, editor.document, body.firstChild, '检查锚点html' );     //1.2版本后，在img前有的不可见字符没有删去，这里改成之比较img内的内容
        ok(body.getElementsByTagName('img')[0].attributes['anchorname'].nodeValue=="hello"&&body.getElementsByTagName('img')[0].attributes['class'].nodeValue=="anchorclass",'检查锚点');
        editor.execCommand( 'source' );     /*切到源码模式下会有一个超时*/
        setTimeout( function() {
            var tas = editor.iframe.parentNode.getElementsByTagName( 'textarea' );
            if(ua.browser.webkit){
                ok( editor.iframe.nextSibling.textContent.indexOf( '<a name="hello"' ) !=-1, '查看是否转换成功' );
            }
            else{
                ok( tas[0].value.indexOf( '<a name="hello"' ) != -1 || tas[0].value.indexOf( '<a anchorname="1"' ) != -1, '查看是否转换成功' );
            }
          /*没办法比，看上去一样，但是一个42个字符，一个48个字符
             * ok((tas[0].value=='

')||(tas[0].value=='

'),'检查源码');*/
            editor.execCommand( 'source' );
            ua.checkHTMLSameStyle( '' + br, editor.document, body.firstChild, '检查锚点html' );
            setTimeout( function() {
                start();
            }, 50 );
        }, 100);
    }, 20 );
} );

test( '在源码模式设置超链接的name属性，切换到编辑器模式检查超链接是否变为锚点', function() {
    var editor = te.obj[0];
    var body = editor.body;
    stop();
    setTimeout(function(){
        editor.setContent( '' );
        setTimeout( function() {
            editor.execCommand( 'source' );
            setTimeout( function() {
                var ta = editor.iframe.parentNode.getElementsByTagName( 'textarea' )[0];
                ta.value = '

';       /*这种情况认为是锚点*/
                setTimeout( function() {
                    editor.execCommand( 'source' );
                    ua.checkHTMLSameStyle( '', editor.document, body.firstChild, '检查锚点html' );
                    start();
                }, 100 );
            }, 100 );
        }, 100 );
    },100);
} );

test( '在源码模式设置超链接没有name属性，切换到编辑器模式检查超链接不变为锚点', function() {
    var editor = te.obj[0];
    editor.setContent( '' );
    var body = editor.body;
    stop();
    setTimeout( function() {
        editor.execCommand( 'source' );
        setTimeout( function() {
            var ta = editor.iframe.parentNode.getElementsByTagName( 'textarea' )[0];
            ta.value = '
你好
';
            setTimeout( function() {
                editor.execCommand( 'source' );
                ua.manualDeleteFillData(editor.body);
//                equal( body.firstChild.firstChild.tagName.toLowerCase(), 'a', 'a标签不会转化' );
                equal( body.firstChild.lastChild.tagName.toLowerCase(), 'a', 'a标签不会转化' );   //兼容opera
                start();
                }, 50 );
        }, 10 );
    }, 20 );
} );

test( '已存在锚点', function() {
    var editor = te.obj[0];
    var range = te.obj[1];
    var body = editor.body;
    var br = baidu.editor.browser.ie ? ' ' : '
';
    editor.setContent( '

' );
    range.selectNode(body.firstChild).select();
    editor.execCommand( 'anchor', 'hello' );
    var name=body.firstChild.firstChild.getAttribute('anchorname');
    equal(name, 'hello', '更改name');
    editor.setContent( '

' );
    range.selectNode(body.firstChild).select();
    editor.execCommand( 'anchor');
    equal(ua.getChildHTML(editor.body),'

','去掉锚点');
} );<|endoftext|>


<commit_before>const DigitTicker = (() => {
    let barrier,
        el_container,
        el_peek,
        el_peek_box,
        el_mask,
        total_tick_count,
        contract_status,
        type,
        current_spot;
    let style_offset_correction = 5;

    const array_of_digits         = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    const init = (container_id, contract_type, shortcode, tick_count, status = 'open') => {
        contract_status      = status;
        total_tick_count     = tick_count;
        type                 = contract_type;
        current_spot         = '-';
        el_container         = document.querySelector(`#${container_id}`);

        setBarrierFromShortcode(type, shortcode);
        populateContainer(el_container);
        highlightWinningNumbers(getWinningNumbers(contract_type, barrier));
        observeResize();
    };

    const populateContainer = (container_element) => {
        // remove previous elements and start fresh.
        while (container_element && container_element.firstChild) {
            container_element.removeChild(container_element.firstChild);
        }

        const temp_epoch_el = document.createElement('div');
        temp_epoch_el.classList.add('epoch');
        const temp_peek_box_el = document.createElement('div');
        temp_peek_box_el.classList.add('peek-box');
        if (contract_status === 'won') {
            temp_peek_box_el.classList.add('digit-winning');
        }
        if (contract_status === 'lost') {
            temp_peek_box_el.classList.add('digit-losing');
        }

        const temp_digits_el = document.createElement('div');
        temp_digits_el.classList.add('digits');
        array_of_digits.forEach(digit => {
            const digit_el = document.createElement('div');
            digit_el.classList.add('digit', `digit-${digit}`);
            digit_el.appendChild(document.createTextNode(digit));
            temp_digits_el.appendChild(digit_el);
        });

        const temp_mask_el = document.createElement('div');
        temp_mask_el.classList.add('mask');
        temp_mask_el.append(document.createTextNode('0 / 0'));

        const temp_peek_el = document.createElement('div');
        temp_peek_el.classList.add('peek');
        // grid peek-box element definition
        const topleft_el = document.createElement('div');
        topleft_el.classList.add('topleft');
        const top_el = document.createElement('div');
        top_el.classList.add('top');
        const topright_el = document.createElement('div');
        topright_el.classList.add('topright');
        const left_el = document.createElement('div');
        left_el.classList.add('left');
        const right_el = document.createElement('div');
        right_el.classList.add('right');
        const bottomleft_el = document.createElement('div');
        bottomleft_el.classList.add('bottomleft');
        const bottom_el = document.createElement('div');
        bottom_el.classList.add('bottom');
        const bottomright_el = document.createElement('div');
        bottomright_el.classList.add('bottomright');
        temp_peek_box_el.appendChild(topleft_el);
        temp_peek_box_el.appendChild(top_el);
        temp_peek_box_el.appendChild(topright_el);
        temp_peek_box_el.appendChild(left_el);
        temp_peek_box_el.appendChild(right_el);
        temp_peek_box_el.appendChild(bottomright_el);
        temp_peek_box_el.appendChild(bottomleft_el);
        temp_peek_box_el.appendChild(bottom_el);
        temp_peek_box_el.appendChild(temp_mask_el);
        temp_peek_box_el.appendChild(temp_peek_el);

        const fragment = document.createDocumentFragment();
        fragment.appendChild(temp_epoch_el);
        fragment.appendChild(temp_peek_box_el);
        fragment.appendChild(temp_digits_el);
        container_element.appendChild(fragment);
        container_element.classList.add('invisible');
    };

    // adjust box sizes for mobile
    const adjustBoxSizes = () => {
        if (el_container.offsetWidth < 360) {
            style_offset_correction = 6;
        }
    };

    // Detect winning numbers against the barrier with the given contract type.
    const getWinningNumbers = (contract_type, spot) => {
        switch (contract_type) {
            case 'DIGITOVER':
                return array_of_digits.filter(digit => +digit > +spot);
            case 'DIGITUNDER':
                return array_of_digits.filter(digit => +digit < +spot);
            case 'DIGITMATCH':
                return array_of_digits.filter(digit => +digit === +spot);
            case 'DIGITDIFF':
                return array_of_digits.filter(digit => +digit !== +spot);
            case 'DIGITODD':
                return array_of_digits.filter(digit => +digit % 2 !== 0);
            case 'DIGITEVEN':
                return array_of_digits.filter(digit => +digit % 2 === 0);
            default:
                throw new Error('Cannot Determine Winning numbers.');
        }
    };

    const highlightWinningNumbers = (winning_numbers) => {
        winning_numbers.forEach(digit => {
            const element = el_container.querySelector(`.digit-${digit}`);
            element.classList.remove('digit-losing');
            element.classList.add('digit-winning');
        });
    };

    const observeResize = () => {
        window.onresize = () => {
            if (el_peek_box) {
                adjustBoxSizes();
                el_peek_box.setAttribute('style', `transform: translateX(${calculateOffset()}px)`);
            }
        };
    };

    // Calculate peek-box left offset.
    const calculateOffset = () => {
        const current_spot_digit = document.querySelector(`.digit-${current_spot}`);
        const left_offset =  current_spot_digit ? current_spot_digit.offsetLeft : 0;
        return left_offset - style_offset_correction;
    };

    const markAsLost = () => {
        if (!el_peek_box || !el_peek) {
            setElements();
        }
        el_peek.classList.remove('digit-winning', 'digit-running');
        el_peek_box.classList.remove('digit-winning', 'digit-running');
        el_peek.classList.add('digit-losing');
        el_peek_box.classList.add('digit-losing');
    };

    const markDigitAsWon = (digit) => {
        if (el_container && el_container.querySelector(`.digit-${digit}`)) {
            el_container.querySelector(`.digit-${digit}`).classList.add('digit-won');
        }
    };

    const markDigitAsLost = (digit) => {
        if (el_container && el_container.querySelector(`.digit-${digit}`)) {
            el_container.querySelector(`.digit-${digit}`).classList.add('digit-lost');
        }
    };

    const markAsWon = () => {
        if (!el_peek_box || !el_peek) {
            setElements();
        }
        el_peek_box.classList.remove('digit-losing', 'digit-running');
        el_peek_box.classList.add('digit-winning');
        el_peek.classList.remove('digit-losing', 'digit-running');
        el_peek.classList.add('digit-winning');
    };

    const setElements = () => {
        el_peek     = el_container ? el_container.querySelector('.peek') : null;
        el_peek_box = el_peek ? el_container.querySelector('.peek-box') : null;
        el_mask     = el_peek_box ? el_peek_box.querySelector('.peek-box > .mask') : null;
    };

    const isBarrierMissing = (contract_type, bar) => !/digit(even|odd)/i.test(type) && !bar;

    const setBarrierFromShortcode = (contract_type, shortcode) => {
        barrier = '';
        if (!/^(digiteven|digitodd)_/i.test(shortcode)) {
            const arr_shortcode = shortcode.split('_');
            barrier = arr_shortcode[arr_shortcode.length - 2];
        }
    };

    const update = (current_tick_count, { quote, epoch }) => {
        if (current_tick_count > total_tick_count) {
            return;
        }
        setElements(epoch);
        el_container.classList.remove('invisible');
        adjustBoxSizes();
        current_spot = quote.substr(-1);

        el_mask.innerText = `${current_tick_count} / ${total_tick_count}`;

        el_peek_box.classList.add('digit-running');
        el_peek.classList.add('digit-running');

        el_peek_box.setAttribute('style', `transform: translateX(${calculateOffset()}px)`);
    };

    const remove = () => {
        window.onresize = null;
        while (el_container && el_container.firstChild) {
            el_container.removeChild(el_container.firstChild);
        }
        if (el_container) el_container.classList.add('invisible');
    };

    const countDecimals = (value) => {
        if (Math.floor(value) !== value) return value.toString().split('.')[1].length || 0;
        return 0;
    };

    const calculateDistance = (old_digit, new_digit) => Math.abs(old_digit - new_digit);

    const countUp = (start, end, duration, element, render) => {
        const decimal_points = countDecimals(start.replace('/,+/', ''));
        const f_start = parseFloat(start.replace(/,+/, ''));
        const range = calculateDistance(f_start, end);
        const step = Math.abs(range / 60);
        const increment = end > f_start ? (1 * step) : (-1 * step);
        let current = f_start;
        let i = 0;

        const renderTick = () => {
            i++;
            current += increment;

            element.innerHTML = render(current.toFixed(decimal_points));

            if (i <= 15) {
                requestAnimationFrame(renderTick);
            } else {
                current = end;
                element.innerHTML = render(current.toFixed(decimal_points));
            }
        };

        requestAnimationFrame(renderTick);
    };

    return {
        init,
        update,
        countUp,
        isBarrierMissing,
        markAsWon,
        markAsLost,
        markDigitAsLost,
        markDigitAsWon,
        remove,
    };
})();

module.exports = DigitTicker;
<commit_msg>Improve query selector<commit_after>const DigitTicker = (() => {
    let barrier,
        el_container,
        el_peek,
        el_peek_box,
        el_mask,
        total_tick_count,
        contract_status,
        type,
        current_spot;
    let style_offset_correction = 5;

    const array_of_digits         = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    const init = (container_id, contract_type, shortcode, tick_count, status = 'open') => {
        contract_status      = status;
        total_tick_count     = tick_count;
        type                 = contract_type;
        current_spot         = '-';
        el_container         = document.querySelector(`#${container_id}`);

        setBarrierFromShortcode(type, shortcode);
        populateContainer(el_container);
        highlightWinningNumbers(getWinningNumbers(contract_type, barrier));
        observeResize();
    };

    const populateContainer = (container_element) => {
        // remove previous elements and start fresh.
        while (container_element && container_element.firstChild) {
            container_element.removeChild(container_element.firstChild);
        }

        const temp_epoch_el = document.createElement('div');
        temp_epoch_el.classList.add('epoch');
        const temp_peek_box_el = document.createElement('div');
        temp_peek_box_el.classList.add('peek-box');
        if (contract_status === 'won') {
            temp_peek_box_el.classList.add('digit-winning');
        }
        if (contract_status === 'lost') {
            temp_peek_box_el.classList.add('digit-losing');
        }

        const temp_digits_el = document.createElement('div');
        temp_digits_el.classList.add('digits');
        array_of_digits.forEach(digit => {
            const digit_el = document.createElement('div');
            digit_el.classList.add('digit', `digit-${digit}`);
            digit_el.appendChild(document.createTextNode(digit));
            temp_digits_el.appendChild(digit_el);
        });

        const temp_mask_el = document.createElement('div');
        temp_mask_el.classList.add('mask');
        temp_mask_el.append(document.createTextNode('0 / 0'));

        const temp_peek_el = document.createElement('div');
        temp_peek_el.classList.add('peek');
        // grid peek-box element definition
        const topleft_el = document.createElement('div');
        topleft_el.classList.add('topleft');
        const top_el = document.createElement('div');
        top_el.classList.add('top');
        const topright_el = document.createElement('div');
        topright_el.classList.add('topright');
        const left_el = document.createElement('div');
        left_el.classList.add('left');
        const right_el = document.createElement('div');
        right_el.classList.add('right');
        const bottomleft_el = document.createElement('div');
        bottomleft_el.classList.add('bottomleft');
        const bottom_el = document.createElement('div');
        bottom_el.classList.add('bottom');
        const bottomright_el = document.createElement('div');
        bottomright_el.classList.add('bottomright');
        temp_peek_box_el.appendChild(topleft_el);
        temp_peek_box_el.appendChild(top_el);
        temp_peek_box_el.appendChild(topright_el);
        temp_peek_box_el.appendChild(left_el);
        temp_peek_box_el.appendChild(right_el);
        temp_peek_box_el.appendChild(bottomright_el);
        temp_peek_box_el.appendChild(bottomleft_el);
        temp_peek_box_el.appendChild(bottom_el);
        temp_peek_box_el.appendChild(temp_mask_el);
        temp_peek_box_el.appendChild(temp_peek_el);

        const fragment = document.createDocumentFragment();
        fragment.appendChild(temp_epoch_el);
        fragment.appendChild(temp_peek_box_el);
        fragment.appendChild(temp_digits_el);
        container_element.appendChild(fragment);
        container_element.classList.add('invisible');
    };

    // adjust box sizes for mobile
    const adjustBoxSizes = () => {
        if (el_container.offsetWidth < 360) {
            style_offset_correction = 6;
        }
    };

    // Detect winning numbers against the barrier with the given contract type.
    const getWinningNumbers = (contract_type, spot) => {
        switch (contract_type) {
            case 'DIGITOVER':
                return array_of_digits.filter(digit => +digit > +spot);
            case 'DIGITUNDER':
                return array_of_digits.filter(digit => +digit < +spot);
            case 'DIGITMATCH':
                return array_of_digits.filter(digit => +digit === +spot);
            case 'DIGITDIFF':
                return array_of_digits.filter(digit => +digit !== +spot);
            case 'DIGITODD':
                return array_of_digits.filter(digit => +digit % 2 !== 0);
            case 'DIGITEVEN':
                return array_of_digits.filter(digit => +digit % 2 === 0);
            default:
                throw new Error('Cannot Determine Winning numbers.');
        }
    };

    const highlightWinningNumbers = (winning_numbers) => {
        winning_numbers.forEach(digit => {
            const element = el_container.querySelector(`.digit-${digit}`);
            element.classList.remove('digit-losing');
            element.classList.add('digit-winning');
        });
    };

    const observeResize = () => {
        window.onresize = () => {
            if (el_peek_box) {
                adjustBoxSizes();
                el_peek_box.setAttribute('style', `transform: translateX(${calculateOffset()}px)`);
            }
        };
    };

    // Calculate peek-box left offset.
    const calculateOffset = () => {
        const current_spot_digit = document.querySelector(`.digit-${current_spot}`);
        const left_offset =  current_spot_digit ? current_spot_digit.offsetLeft : 0;
        return left_offset - style_offset_correction;
    };

    const markAsLost = () => {
        if (!el_peek_box || !el_peek) {
            setElements();
        }
        el_peek.classList.remove('digit-winning', 'digit-running');
        el_peek_box.classList.remove('digit-winning', 'digit-running');
        el_peek.classList.add('digit-losing');
        el_peek_box.classList.add('digit-losing');
    };

    const markDigitAsWon = (digit) => {
        if (el_container && el_container.querySelector(`.digit-${digit}`)) {
            el_container.querySelector(`.digit-${digit}`).classList.add('digit-won');
        }
    };

    const markDigitAsLost = (digit) => {
        if (el_container && el_container.querySelector(`.digit-${digit}`)) {
            el_container.querySelector(`.digit-${digit}`).classList.add('digit-lost');
        }
    };

    const markAsWon = () => {
        if (!el_peek_box || !el_peek) {
            setElements();
        }
        el_peek_box.classList.remove('digit-losing', 'digit-running');
        el_peek_box.classList.add('digit-winning');
        el_peek.classList.remove('digit-losing', 'digit-running');
        el_peek.classList.add('digit-winning');
    };

    const setElements = () => {
        el_peek     = el_container ? el_container.querySelector('.peek') : null;
        el_peek_box = el_peek ? el_container.querySelector('.peek-box') : null;
        el_mask     = el_peek_box ? el_peek_box.querySelector('> .mask') : null;
    };

    const isBarrierMissing = (contract_type, bar) => !/digit(even|odd)/i.test(type) && !bar;

    const setBarrierFromShortcode = (contract_type, shortcode) => {
        barrier = '';
        if (!/^(digiteven|digitodd)_/i.test(shortcode)) {
            const arr_shortcode = shortcode.split('_');
            barrier = arr_shortcode[arr_shortcode.length - 2];
        }
    };

    const update = (current_tick_count, { quote, epoch }) => {
        if (current_tick_count > total_tick_count) {
            return;
        }
        setElements(epoch);
        el_container.classList.remove('invisible');
        adjustBoxSizes();
        current_spot = quote.substr(-1);

        el_mask.innerText = `${current_tick_count} / ${total_tick_count}`;

        el_peek_box.classList.add('digit-running');
        el_peek.classList.add('digit-running');

        el_peek_box.setAttribute('style', `transform: translateX(${calculateOffset()}px)`);
    };

    const remove = () => {
        window.onresize = null;
        while (el_container && el_container.firstChild) {
            el_container.removeChild(el_container.firstChild);
        }
        if (el_container) el_container.classList.add('invisible');
    };

    const countDecimals = (value) => {
        if (Math.floor(value) !== value) return value.toString().split('.')[1].length || 0;
        return 0;
    };

    const calculateDistance = (old_digit, new_digit) => Math.abs(old_digit - new_digit);

    const countUp = (start, end, duration, element, render) => {
        const decimal_points = countDecimals(start.replace('/,+/', ''));
        const f_start = parseFloat(start.replace(/,+/, ''));
        const range = calculateDistance(f_start, end);
        const step = Math.abs(range / 60);
        const increment = end > f_start ? (1 * step) : (-1 * step);
        let current = f_start;
        let i = 0;

        const renderTick = () => {
            i++;
            current += increment;

            element.innerHTML = render(current.toFixed(decimal_points));

            if (i <= 15) {
                requestAnimationFrame(renderTick);
            } else {
                current = end;
                element.innerHTML = render(current.toFixed(decimal_points));
            }
        };

        requestAnimationFrame(renderTick);
    };

    return {
        init,
        update,
        countUp,
        isBarrierMissing,
        markAsWon,
        markAsLost,
        markDigitAsLost,
        markDigitAsWon,
        remove,
    };
})();

module.exports = DigitTicker;
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 IBM Corporation
 *
 * 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.
 */

var request = require('request');

/**
 * Openwhisk action to cerate a topic in bluemix message hub
 * @param      {string}  restUrl                    (required)  MessageHub url instance
 * @param      {string}  restPort                   (required)  MessageHub port, default 443
 * @param      {string}  apikey                     (required)  MessageHub Api key
 * @param      {string}  topic                      (required)  MessageHub topic for creation
 * @return     {Object}                                         Done with the result of invocation
 **/

function main(params) {
	var requiredParams = ["restUrl", "restPort", 'apikey', 'topic'];

	checkParameters(params, requiredParams, function(missingParams) {
		if (missingParams != "") {
			console.error("Missing required parameters: " + missingParams);
			return whisk.error("Missing required parameters: " + missingParams);
		} else {
			var body = {name: params.topic};

			var url = 'https://'+params.restUrl+':'+params.restPort+'/admin/topics';

			var options = {
					method: 'POST',
					url: url,
					body: JSON.stringify(body),
					headers: {
						'X-Auth-Token': params.apikey,
						'Content-Type': 'application/json'
					}
			};

			request(options, function(err, res, body) {
				if (!err && res.statusCode === 202) {
					return whisk.done({result: "topic created successfully "});

				} else {
					return whisk.error({
						statusCode: (res || {}).statusCode,
						error: err,
						body: body
					});
				}
			});
		}
	});

	return whisk.async();

}

/**
 *  A function that check whether the parameters passed are required or not
 *
 * @param      {object}    params    An object contains the parameter required
 *                                   in order to check it and generate a string
 *                                   that contains a list of missing parameters
 * @param      {Function}  callback  the callback function has the generated
 *                                   array or an empty one if the params is
 *                                   empty or nothing is missing
 */
function checkParameters(params, requiredParams, callback) {
	console.log("Checking Existence of Required Parameters");
	var missingParams = [];
	for (var i = requiredParams.length - 1; i >= 0; i--) {
		console.log(requiredParams[i]);
		if (!params.hasOwnProperty(requiredParams[i])) {
			missingParams.push(requiredParams[i]);
		}
		if (i == 0)
			return callback(missingParams);
	}
}
<commit_msg>Delete createTopics.js<commit_after><|endoftext|>


<commit_before>var isAlreadyRunning = false;
var myMaxHealth = 0;

function doTheThing() {
	if (isAlreadyRunning || g_Minigame === undefined) {
		return;
	}
	isAlreadyRunning = true;

	goToLaneWithLowest();

	useMedicsIfRelevant();

	// TODO use abilities if available and a suitable target exists
	// - Tactical Nuke on a Spawner if below 50% and above 25% of its health
	// - Cluster Bomb and Napalm if the current lane has a spawner and 2+ creeps
	// - Good Luck if available
	// - Metal Detector if a spawner death is imminent (predicted in > 2 and < 7 seconds)
	// - Morale Booster if available and lane has > 2 live enemies
	// - Decrease Cooldowns if another player used a long-cooldown ability < 10 seconds ago

	// TODO purchase abilities and upgrades intelligently

	// TODO click the current target a few times (<= 10/sec, so as not to cheat)

	// TODO respawn if dead and respawn button is available

	isAlreadyRunning = false;
}

function goToLaneWithLowest() {
	// TODO check if it's a boss level, handle boss levels differently

	// TODO prefer lane with a dying creep as long as all living spawners have >50% health

	// determine which living spawner has lowest hp
	var spawners = [];
	for (var i = 0; i < 3; i++) {
		for (var j = 0; j < 4; j++) {
			var enemy = g_Minigame.CurrentScene().GetEnemy(i, j);
			if (enemy && enemy.GetName() == 'Spawner') {
				spawners[spawners.length] = g_Minigame.CurrentScene().GetEnemy(i, j);
			}
		}
	}
	var lowHP = 0;
	var lowLane = 0;
	var lowTarget = 0;
	for (var i = 0; i < spawners.length; i++) {
		if(spawners[i] && !spawners[i].m_bIsDestroyed) {
			if(lowHP < 1 || spawners[i].m_flDisplayedHP < lowHP) {
				lowHP = spawners[i].m_flDisplayedHP;
				lowLane = spawners[i].m_nLane;
				lowTarget = spawners[i].m_nID;
			}
		}
	}

	// if no spawners remain, determine which lane has a creep with the lowest health
	if (lowHP < 1) {
		// determine which living creep has the lowest hp
		var creeps = [];
		for (var i = 0; i < 3; i++) {
			for (var j = 0; j < 4; j++) {
				var enemy = g_Minigame.CurrentScene().GetEnemy(i, j);
				if (enemy && enemy.m_data.type == 1) {
					creeps[creeps.length] = g_Minigame.CurrentScene().GetEnemy(i, j);
				}
			}
		}

		for (var i = 0; i < creeps.length; i++) {
			if (creeps[i] && !creeps[i].m_bIsDestroyed) {
				if (lowHP < 1 || creeps[i].m_flDisplayedHP < lowHP) {
					lowHP = creeps[i];
					lowLane = creeps[i].m_nLane;
					lowTarget = creeps[i].m_nID;
				}
			}
		}
	}

	// go to the chosen lane
	if (g_Minigame.CurrentScene().m_nExpectedLane != lowLane) {
		//console.log('switching langes');
		g_Minigame.CurrentScene().TryChangeLane(lowLane);
	}

	// target the chosen enemy
	if (g_Minigame.CurrentScene().m_nTarget != lowTarget) {
		//console.log('switching targets');
		g_Minigame.CurrentScene().TryChangeTarget(lowTarget);
	}
}

function useMedicsIfRelevant() {
	// regularly check HP to try to determine max health (I haven't found the variable for it yet)
	if (g_Minigame.CurrentScene().m_rgPlayerData.hp > myMaxHealth) {
		myMaxHealth = g_Minigame.CurrentScene().m_rgPlayerData.hp
	}

	// check if health is below 50%
	var hpPercent = g_Minigame.CurrentScene().m_rgPlayerData.hp / myMaxHealth;
	if (hpPercent > 0.5) {
		return; // no need to heal - HP is above 50%
	}

	// check if Medics is purchased and cooled down
	if ((1 << 7) & g_Minigame.CurrentScene().m_rgPlayerTechTree.unlocked_abilities_bitfield) {
		// each bit in unlocked_abilities_bitfield corresponds to an ability. Medics is ability 7.
		// the above condition checks if the Medics bit is set or cleared. I.e. it checks if
		// the player has the Medics ability.

		var abilitiesInCooldown = g_Minigame.CurrentScene().m_rgPlayerData.active_abilities;
		for (var i = 1; i < abilitiesInCooldown.length; i++) {
			if (abilitiesInCooldown[i].ability == 7) {
				return; // Medics is in cooldown, can't use it.
			}
		}

		// Medics is purchased, cooled down, and needed. Trigger it.
		console.log('Medics is purchased, cooled down, and needed. Trigger it.');
		if (document.getElementById('ability_7')) {
			g_Minigame.CurrentScene().TryAbility(document.getElementById('ability_7').childElements()[0]);
		}
	}
}

var thingTimer = window.setInterval(doTheThing, 1000);<commit_msg>Added condition where medic is not used if you're already dead<commit_after>var isAlreadyRunning = false;
var myMaxHealth = 0;

function doTheThing() {
	if (isAlreadyRunning || g_Minigame === undefined) {
		return;
	}
	isAlreadyRunning = true;

	goToLaneWithLowest();

	useMedicsIfRelevant();

	// TODO use abilities if available and a suitable target exists
	// - Tactical Nuke on a Spawner if below 50% and above 25% of its health
	// - Cluster Bomb and Napalm if the current lane has a spawner and 2+ creeps
	// - Good Luck if available
	// - Metal Detector if a spawner death is imminent (predicted in > 2 and < 7 seconds)
	// - Morale Booster if available and lane has > 2 live enemies
	// - Decrease Cooldowns if another player used a long-cooldown ability < 10 seconds ago

	// TODO purchase abilities and upgrades intelligently

	// TODO click the current target a few times (<= 10/sec, so as not to cheat)

	// TODO respawn if dead and respawn button is available

	isAlreadyRunning = false;
}

function goToLaneWithLowest() {
	// TODO check if it's a boss level, handle boss levels differently

	// TODO prefer lane with a dying creep as long as all living spawners have >50% health

	// determine which living spawner has lowest hp
	var spawners = [];
	for (var i = 0; i < 3; i++) {
		for (var j = 0; j < 4; j++) {
			var enemy = g_Minigame.CurrentScene().GetEnemy(i, j);
			if (enemy && enemy.GetName() == 'Spawner') {
				spawners[spawners.length] = g_Minigame.CurrentScene().GetEnemy(i, j);
			}
		}
	}
	var lowHP = 0;
	var lowLane = 0;
	var lowTarget = 0;
	for (var i = 0; i < spawners.length; i++) {
		if(spawners[i] && !spawners[i].m_bIsDestroyed) {
			if(lowHP < 1 || spawners[i].m_flDisplayedHP < lowHP) {
				lowHP = spawners[i].m_flDisplayedHP;
				lowLane = spawners[i].m_nLane;
				lowTarget = spawners[i].m_nID;
			}
		}
	}

	// if no spawners remain, determine which lane has a creep with the lowest health
	if (lowHP < 1) {
		// determine which living creep has the lowest hp
		var creeps = [];
		for (var i = 0; i < 3; i++) {
			for (var j = 0; j < 4; j++) {
				var enemy = g_Minigame.CurrentScene().GetEnemy(i, j);
				if (enemy && enemy.m_data.type == 1) {
					creeps[creeps.length] = g_Minigame.CurrentScene().GetEnemy(i, j);
				}
			}
		}

		for (var i = 0; i < creeps.length; i++) {
			if (creeps[i] && !creeps[i].m_bIsDestroyed) {
				if (lowHP < 1 || creeps[i].m_flDisplayedHP < lowHP) {
					lowHP = creeps[i];
					lowLane = creeps[i].m_nLane;
					lowTarget = creeps[i].m_nID;
				}
			}
		}
	}

	// go to the chosen lane
	if (g_Minigame.CurrentScene().m_nExpectedLane != lowLane) {
		//console.log('switching langes');
		g_Minigame.CurrentScene().TryChangeLane(lowLane);
	}

	// target the chosen enemy
	if (g_Minigame.CurrentScene().m_nTarget != lowTarget) {
		//console.log('switching targets');
		g_Minigame.CurrentScene().TryChangeTarget(lowTarget);
	}
}

function useMedicsIfRelevant() {
	// regularly check HP to try to determine max health (I haven't found the variable for it yet)
	if (g_Minigame.CurrentScene().m_rgPlayerData.hp > myMaxHealth) {
		myMaxHealth = g_Minigame.CurrentScene().m_rgPlayerData.hp
	}

	// check if health is below 50%
	var hpPercent = g_Minigame.CurrentScene().m_rgPlayerData.hp / myMaxHealth;
	if (hpPercent > 0.5 || g_Minigame.CurrentScene().m_rgPlayerData.hp < 1) {
		return; // no need to heal - HP is above 50% or already dead
	}

	// check if Medics is purchased and cooled down
	if ((1 << 7) & g_Minigame.CurrentScene().m_rgPlayerTechTree.unlocked_abilities_bitfield) {
		// each bit in unlocked_abilities_bitfield corresponds to an ability. Medics is ability 7.
		// the above condition checks if the Medics bit is set or cleared. I.e. it checks if
		// the player has the Medics ability.

		var abilitiesInCooldown = g_Minigame.CurrentScene().m_rgPlayerData.active_abilities;
		for (var i = 1; i < abilitiesInCooldown.length; i++) {
			if (abilitiesInCooldown[i].ability == 7) {
				return; // Medics is in cooldown, can't use it.
			}
		}

		// Medics is purchased, cooled down, and needed. Trigger it.
		console.log('Medics is purchased, cooled down, and needed. Trigger it.');
		if (document.getElementById('ability_7')) {
			g_Minigame.CurrentScene().TryAbility(document.getElementById('ability_7').childElements()[0]);
		}
	}
}

var thingTimer = window.setInterval(doTheThing, 1000);<|endoftext|>