<commit_before>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa:application');
var Emitter = require('events').EventEmitter;
var compose = require('koa-compose');
var context = require('./context');
var request = require('./request');
var response = require('./response');
var Cookies = require('cookies');
var Keygrip = require('keygrip');
var Stream = require('stream');
var http = require('http');
var co = require('co');

/**
 * Application prototype.
 */

var app = Application.prototype;

/**
 * Expose `Application`.
 */

exports = module.exports = Application;

/**
 * Initialize a new `Application`.
 *
 * @api public
 */

function Application() {
  if (!(this instanceof Application)) return new Application;
  this.env = process.env.NODE_ENV || 'development';
  this.on('error', this.onerror);
  this.outputErrors = 'test' != this.env;
  this.subdomainOffset = 2;
  this.poweredBy = true;
  this.jsonSpaces = 2;
  this.middleware = [];
  this.context = Object.create(context);
  this.request = Object.create(request);
  this.response = Object.create(response);
}

/**
 * Inherit from `Emitter.prototype`.
 */

Application.prototype.__proto__ = Emitter.prototype;

/**
 * Shorthand for:
 *
 *    http.createServer(app.callback()).listen(...)
 *
 * @param {Mixed} ...
 * @return {Server}
 * @api public
 */

app.listen = function(){
  var server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
};

/**
 * Use the given middleware `fn`.
 *
 * @param {Function} fn
 * @return {Application} self
 * @api public
 */

app.use = function(fn){
  debug('use %s', fn.name || '-');
  this.middleware.push(fn);
  return this;
};

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */

app.callback = function(){
  var mw = [respond].concat(this.middleware);
  var gen = compose(mw);
  var self = this;

  return function(req, res, next){
    var ctx = self.createContext(req, res);
    co.call(ctx, gen)(next || ctx.onerror);
  }
};

/**
 * Set signed cookie keys.
 *
 * These are passed to [KeyGrip](https://github.com/jed/keygrip),
 * however you may also pass your own `KeyGrip` instance. For
 * example the following are acceptable:
 *
 *   app.keys = ['im a newer secret', 'i like turtle'];
 *   app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
 *
 * @param {Array|KeyGrip} keys
 * @api public
 */

app.__defineSetter__('keys', function(keys){
  var ok = Array.isArray(keys) || keys instanceof Keygrip;
  if (!ok) throw new TypeError('app.keys must be an array or Keygrip');
  if (!(keys instanceof Keygrip)) keys = new Keygrip(keys);
  this._keys = keys;
});

/**
 * Get `Keygrip` instance.
 *
 * @return {Keygrip}
 * @api public
 */

app.__defineGetter__('keys', function(){
  return this._keys;
});

/**
 * Initialize a new context.
 *
 * @api private
 */

app.createContext = function(req, res){
  var context = Object.create(this.context);
  var request = context.request = Object.create(this.request);
  var response = context.response = Object.create(this.response);
  context.app = request.app = response.app = this;
  context.req = request.req = response.req = req;
  context.res = request.res = response.res = res;
  request.ctx = response.ctx = context;
  context.onerror = context.onerror.bind(context);
  context.originalUrl = request.originalUrl = req.url;
  context.cookies = new Cookies(req, res, this.keys);
  return context;
};

/**
 * Default error handler.
 *
 * @param {Error} err
 * @api private
 */

app.onerror = function(err){
  if (!this.outputErrors) return;
  if (404 == err.status) return;
  console.error(err.stack);
};

/**
 * Response middleware.
 */

function *respond(next){
  this.status = 200;
  if (this.app.poweredBy) this.set('X-Powered-By', 'koa');

  yield next;

  var res = this.res;
  var body = this.body;
  var head = 'HEAD' == this.method;
  var noContent = 204 == this.status || 304 == this.status;

  // 404
  if (null == body && 200 == this.status) {
    this.status = 404;
  }

  // ignore body
  if (noContent) return res.end();

  // status body
  if (null == body) {
    this.type = 'text';
    body = http.STATUS_CODES[this.status];
  }

  // Buffer body
  if (Buffer.isBuffer(body)) {
    if (head) return res.end();
    return res.end(body);
  }

  // string body
  if ('string' == typeof body) {
    if (head) return res.end();
    return res.end(body);
  }

  // Stream body
  if (body instanceof Stream) {
    if (!~body.listeners('error').indexOf(this.onerror)) body.on('error', this.onerror);
    if (head) return body.close(), res.end();
    return body.pipe(res);
  }

  // body: json
  body = JSON.stringify(body, null, this.app.jsonSpaces);
  this.length = Buffer.byteLength(body);
  if (head) return res.end();
  res.end(body);
}
<commit_msg>add body.close check for HEAD requests<commit_after>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa:application');
var Emitter = require('events').EventEmitter;
var compose = require('koa-compose');
var context = require('./context');
var request = require('./request');
var response = require('./response');
var Cookies = require('cookies');
var Keygrip = require('keygrip');
var Stream = require('stream');
var http = require('http');
var co = require('co');

/**
 * Application prototype.
 */

var app = Application.prototype;

/**
 * Expose `Application`.
 */

exports = module.exports = Application;

/**
 * Initialize a new `Application`.
 *
 * @api public
 */

function Application() {
  if (!(this instanceof Application)) return new Application;
  this.env = process.env.NODE_ENV || 'development';
  this.on('error', this.onerror);
  this.outputErrors = 'test' != this.env;
  this.subdomainOffset = 2;
  this.poweredBy = true;
  this.jsonSpaces = 2;
  this.middleware = [];
  this.context = Object.create(context);
  this.request = Object.create(request);
  this.response = Object.create(response);
}

/**
 * Inherit from `Emitter.prototype`.
 */

Application.prototype.__proto__ = Emitter.prototype;

/**
 * Shorthand for:
 *
 *    http.createServer(app.callback()).listen(...)
 *
 * @param {Mixed} ...
 * @return {Server}
 * @api public
 */

app.listen = function(){
  var server = http.createServer(this.callback());
  return server.listen.apply(server, arguments);
};

/**
 * Use the given middleware `fn`.
 *
 * @param {Function} fn
 * @return {Application} self
 * @api public
 */

app.use = function(fn){
  debug('use %s', fn.name || '-');
  this.middleware.push(fn);
  return this;
};

/**
 * Return a request handler callback
 * for node's native http server.
 *
 * @return {Function}
 * @api public
 */

app.callback = function(){
  var mw = [respond].concat(this.middleware);
  var gen = compose(mw);
  var self = this;

  return function(req, res, next){
    var ctx = self.createContext(req, res);
    co.call(ctx, gen)(next || ctx.onerror);
  }
};

/**
 * Set signed cookie keys.
 *
 * These are passed to [KeyGrip](https://github.com/jed/keygrip),
 * however you may also pass your own `KeyGrip` instance. For
 * example the following are acceptable:
 *
 *   app.keys = ['im a newer secret', 'i like turtle'];
 *   app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
 *
 * @param {Array|KeyGrip} keys
 * @api public
 */

app.__defineSetter__('keys', function(keys){
  var ok = Array.isArray(keys) || keys instanceof Keygrip;
  if (!ok) throw new TypeError('app.keys must be an array or Keygrip');
  if (!(keys instanceof Keygrip)) keys = new Keygrip(keys);
  this._keys = keys;
});

/**
 * Get `Keygrip` instance.
 *
 * @return {Keygrip}
 * @api public
 */

app.__defineGetter__('keys', function(){
  return this._keys;
});

/**
 * Initialize a new context.
 *
 * @api private
 */

app.createContext = function(req, res){
  var context = Object.create(this.context);
  var request = context.request = Object.create(this.request);
  var response = context.response = Object.create(this.response);
  context.app = request.app = response.app = this;
  context.req = request.req = response.req = req;
  context.res = request.res = response.res = res;
  request.ctx = response.ctx = context;
  context.onerror = context.onerror.bind(context);
  context.originalUrl = request.originalUrl = req.url;
  context.cookies = new Cookies(req, res, this.keys);
  return context;
};

/**
 * Default error handler.
 *
 * @param {Error} err
 * @api private
 */

app.onerror = function(err){
  if (!this.outputErrors) return;
  if (404 == err.status) return;
  console.error(err.stack);
};

/**
 * Response middleware.
 */

function *respond(next){
  this.status = 200;
  if (this.app.poweredBy) this.set('X-Powered-By', 'koa');

  yield next;

  var res = this.res;
  var body = this.body;
  var head = 'HEAD' == this.method;
  var noContent = 204 == this.status || 304 == this.status;

  // 404
  if (null == body && 200 == this.status) {
    this.status = 404;
  }

  // ignore body
  if (noContent) return res.end();

  // status body
  if (null == body) {
    this.type = 'text';
    body = http.STATUS_CODES[this.status];
  }

  // Buffer body
  if (Buffer.isBuffer(body)) {
    if (head) return res.end();
    return res.end(body);
  }

  // string body
  if ('string' == typeof body) {
    if (head) return res.end();
    return res.end(body);
  }

  // Stream body
  if (body instanceof Stream) {
    if (!~body.listeners('error').indexOf(this.onerror)) body.on('error', this.onerror);

    if (head) {
      if (body.close) body.close();
      return res.end();
    }

    return body.pipe(res);
  }

  // body: json
  body = JSON.stringify(body, null, this.app.jsonSpaces);
  this.length = Buffer.byteLength(body);
  if (head) return res.end();
  res.end(body);
}
<|endoftext|>


<commit_before>/**
* Plugin for CodeX.Editor
* Implements RAW-data block
*/
var rawPlugin = function (plugin) {

    var editor = codex.editor;

    plugin.render = function (data) {

        var input   = editor.draw.node('TEXTAREA', 'raw-plugin__input', {});

        input.placeholder = 'Вставьте HTML код';

        if (data && data.html) {
            input.value = data.html;
        }

        return input;

    };

    plugin.save = function (block) {

        return {
            html: block.value
        };

    };

    plugin.validate = function (data) {

        if (data.html.trim() === '') {

            return;

        }

        return true;

    };

    plugin.destroy = function () {

        rawPlugin = null;

    };

    return plugin;

}({});<commit_msg>new raw data type (#171)<commit_after>/**
* Plugin for CodeX.Editor
* Implements RAW-data block
*/
var rawPlugin = function (plugin) {

    var editor = codex.editor;

    plugin.render = function (data) {

        var input   = editor.draw.node('TEXTAREA', 'raw-plugin__input', {});

        input.placeholder = 'Вставьте HTML код';

        if (data && data.raw) {
            input.value = data.raw;
        }

        return input;

    };

    plugin.save = function (block) {

        return {
            raw: block.value
        };

    };

    plugin.validate = function (data) {

        if (data.html.trim() === '') {

            return;

        }

        return true;

    };

    plugin.destroy = function () {

        rawPlugin = null;

    };

    return plugin;

}({});<|endoftext|>


<commit_before>var async = require('async');
var wowhead = require('wowhead')();
var sprintf = require('sprintf').sprintf;
var c = require('irc-colors');

var Wowhead = function() {
  this.hooks = ['hearthstone'];
};

var itemRegEx = /\[([\w\s]+)\]/g;

function coloredNameForQuality(name, quality) {
  var color = function(name) {
    return name;
  };

  switch (quality) {
    case 5:
      color = c.yellow
      break;
    case 4:
      color = c.purple;
      break;
    case 3:
      color = c.navy;
      break;
    case 2:
      color = c.white;
      break;
  }

  return color(name);
};

Wowhead.prototype.hearthstone = function(bot, to, from, msg, callback) {
  var cards = msg.match(itemRegEx);

  if (!cards) {
    callback();
    return;
  }

  function generateOutput(card) {
    var msg = '';

    msg = sprintf('%s %s', coloredNameForQuality('[' + card.name + ']', card.quality), c.cyan(card.cost));

    if (card.attack && card.health) {
      msg += sprintf('/%s/%s', c.green(card.attack), c.red(card.health));
    }

    msg += sprintf(' - %s - %s', card.description, card.getLink());

    return msg;
  };

  async.map(cards, function(card, callback) {
    wowhead.getCard(card.slice(1, card.length - 1), callback);
  }, function(err, results) {
    if (results) {
      results.forEach(function(card) {
        if (card) {
          bot.say(to, generateOutput(card));
        }
      });
    }

    callback();
  });
};

exports.Plugin = Wowhead;
<commit_msg>Fix formatting for hearthstone plugin.<commit_after>var async = require('async');
var wowhead = require('wowhead')();
var sprintf = require('sprintf').sprintf;
var c = require('irc-colors');

var Wowhead = function() {
  this.hooks = ['hearthstone'];
};

var itemRegEx = /\[([\w\s]+)\]/g;

function coloredNameForQuality(name, quality) {
  var color = function(name) {
    return name;
  };

  switch (quality) {
    case 5:
      color = c.yellow
      break;
    case 4:
      color = c.purple;
      break;
    case 3:
      color = c.blue;
      break;
    case 2:
      color = c.white.bold;
      break;
  }

  return color(name);
};

Wowhead.prototype.hearthstone = function(bot, to, from, msg, callback) {
  var cards = msg.match(itemRegEx);

  if (!cards) {
    callback();
    return;
  }

  function generateOutput(card) {
    var msg = '';

    msg = sprintf('%s (%s)', coloredNameForQuality('[' + card.name + ']', card.quality), c.cyan(card.cost));

    if (card.attack && card.health) {
      msg += sprintf(' %s/%s', c.green(card.attack), c.red(card.health));
    }

    msg += sprintf(' - %s - %s', card.description, card.getLink());

    return msg;
  };

  async.map(cards, function(card, callback) {
    wowhead.getCard(card.slice(1, card.length - 1), callback);
  }, function(err, results) {
    if (results) {
      results.forEach(function(card) {
        if (card) {
          bot.say(to, generateOutput(card));
        }
      });
    }

    callback();
  });
};

exports.Plugin = Wowhead;
<|endoftext|>


<commit_before>/**
 * @class Text
 * @extends Asset
 */

var util = require('util'),
    _ = require('underscore'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    Asset = require('./Asset'),
    iconv;

try {
    iconv = require('iconv');
} catch (e) {}

/**
 * new Text(options)
 * =================
 *
 * Create a new Text asset instance.
 *
 * Adds text encoding and decoding support to Asset. Serves as a
 * superclass for `Html`, `Xml`, `Css`, `JavaScript`, `CoffeeScript`,
 * `Json`, and `CacheManifest`.
 *
 * In addition to the options already supported by the Asset base
 * class, these options are supported:
 *
 * - `text` (String) The decoded source of the asset. Can be used
 *              instead of `rawSrc` and `rawSrcProxy`.  - `encoding`
 *              (String) Used to decode and reencode the `rawSrc`. Can
 *              be any encoding supported by the `iconv` module. Can
 *              be changed later using the `encoding`
 *              getter/setter. Defaults to "utf-8" (see the docs for
 *              `defaultEncoding` below). If the asset is loaded via
 *              http, the encoding will be read from the
 *              `Content-Type`, and likewise for `data:` urls.
 *
 * Example:
 *
 *     var textAsset = new Text({
 *         // "æøå" in iso-8859-1:
 *         rawSrc: new Buffer([0xe6, 0xf8, 0xe5]),
 *         encoding: "iso-8859-1"
 *     });
 *     textAsset.text; // "æøå" (decoded JavaScript string)
 *     textAsset.encoding = 'utf-8';
 *     textAsset.rawSrc; // <Buffer c3 a6 c3 b8 c3 a5>
 */
function Text(config) {
    if ('text' in config) {
        this._text = config.text;
        delete config.text;
    }
    if (config.encoding) {
        this._encoding = config.encoding;
        delete config.encoding;
    }
    Asset.call(this, config);
}

util.inherits(Text, Asset);

extendWithGettersAndSetters(Text.prototype, {
    /**
     * text.isText
     * ===========
     *
     * Property that's true for all Text instances. Avoids reliance on
     * the `instanceof` operator.
     */

    /**
     * text.defaultEncoding
     * ====================
     *
     * The default encoding for the Text (sub)class. Used for decoding
     * the raw source when the encoding cannot be determined by other
     * means, such as a `Content-Type` header (when the asset was
     * fetched via http), or another indicator specific to the given
     * asset type (`@charset` for Css, `<meta
     * http-equiv="Content-Type" ...>` for Html).
     *
     * Factory setting is "utf-8", but you can override it by setting
     * `Text.prototype.defaultEncoding` to another value supported by
     * the `iconv` module.
     */
    defaultEncoding: 'utf-8',

    defaultExtension: '.txt',

    alternativeExtensions: ['.xtemplate'], // For Ext.XTemplate + one.getText

    contentType: 'text/plain',

    /**
     * Text.encoding (getter/setter)
     * =============================
     *
     * Get or set the encoding (charset) used for re-encoding the raw
     * source of the asset. To affect the initial decoding of the
     * `rawSrc` option, provide the `encoding` option to the
     * constructor.
     */
    get encoding() {
        if (!this._encoding) {
            this._encoding = this.defaultEncoding;
        }
        return this._encoding;
    },

    set encoding(encoding) {
        if (encoding !== this.encoding) {
            var text = this.text; // Make sure this._text exists so the rawSrc is decoded before the original encoding is thrown away
            delete this._rawSrc;
            this._encoding = encoding;
            this.markDirty();
        }
    },

    get rawSrc() {
        if (!this._rawSrc) {
            if ('_text' in this || this._parseTree) {
                if (/^utf-?8$/i.test(this.encoding)) {
                    this._rawSrc = new Buffer(this.text, 'utf-8');
                } else if (/^(?:us-?)?ascii$/i.test(this.encoding)) {
                    this._rawSrc = new Buffer(this.text, 'ascii');
                } else if (iconv) {
                    try {
                        this._rawSrc = new iconv.Iconv('utf-8', this.encoding).convert(this.text);
                    } catch (err) {
                        if (err.code === 'EILSEQ') {
                            this.assetgraph.emit('error', 'iconv: Illegal character sequence in ' + this.url + '\n       Conversion: utf-8 --> ' + this.encoding + '\n       Trying to transliterate, ignoring character on failure.');
                            this._rawSrc = new iconv.Iconv('utf-8', this.encoding + '//TRANSLIT//IGNORE').convert(this.text);
                        } else {
                            err.message = 'iconv: Converting ' + this.url + ' failed:\n' + err.message;
                            this.assetgraph.emit('error', err);
                        }
                    }
                } else {
                    throw new Error("node-iconv not found. Cannot encode " + this + " as " + this.encoding + ". " +
                                    "Please run 'npm install iconv' and try again");
                }
            } else {
                throw new Error("assets.Text.rawSrc getter: No _rawSrc or _text property found, asset not loaded?");
            }
        }
        return this._rawSrc;
    },

    set rawSrc(rawSrc) {
        var assetGraph = this.assetGraph;
        if (assetGraph) {
            assetGraph.removeAsset(this);
        }
        this._rawSrc = rawSrc;
        delete this._parseTree;
        delete this._text;
        if (assetGraph) {
            assetGraph.addAsset(this);
        }
        this.markDirty();
    },

    /**
     * text.text (getter/setter)
     * =========================
     *
     * Get or set the decoded text contents of the of the asset as a
     * JavaScript string. Unlike browsers AssetGraph doesn't try to
     * sniff the charset of your text-based assets. It will fall back
     * to assuming utf-8 if it's unable to determine the
     * encoding/charset from HTTP headers, `<meta
     * http-equiv='Content-Type'>` tags (Html), `@charset` (Css), so
     * if for some reason you're not using utf-8 for all your
     * text-based assets, make sure to provide those hints. Other
     * asset types provide no standard way to specify the charset
     * within the file itself, so presently there's no way to load
     * eg. JavaScript from disc if it's not utf-8 or ASCII, except by
     * overriding `Text.prototype.defaultEncoding` globally.
     *
     * If the internal state has been changed since the asset was
     * initialized, it will automatically be reserialized when the
     * `text` property is retrieved, for example:
     *
     *     var htmlAsset = new Html({
     *         rawSrc: new Buffer("hello");
     *     });
     *     htmlAsset.text; // "hello"
     *     htmlAsset.parseTree.body.innerHTML = "bye";
     *     htmlAsset.markDirty();
     *     htmlAsset.text; // "bye"
     *
     * Setting this property after the outgoing relations have been
     * accessed currently leads to undefined behavior.
     */
    get text() {
        if (!('_text' in this)) {
            this._text = this._getTextFromRawSrc();
        }
        return this._text;
    },

    set text(text) {
        this.unload();
        this._text = text;
        if (this.assetGraph) {
            this.populate();
        }
        this.markDirty();
    },

    markDirty: function () {
        if (this._parseTree) {
            delete this._text;
        }
        return Asset.prototype.markDirty.call(this);
    },

    _getTextFromRawSrc: function () {
        if (!this._rawSrc) {
            throw new Error("assets.Text._getTextFromRawSrc(): No _rawSrc property found.");
        }
        if (/^utf-?8$/i.test(this.encoding)) {
            return this._rawSrc.toString('utf-8');
        } else if (/^(?:us-?)?ascii$/i.test(this.encoding)) {
            return this._rawSrc.toString('ascii');
        } else if (iconv) {
            return new iconv.Iconv(this.encoding, 'utf-8').convert(this._rawSrc).toString('utf-8');
        } else {
            throw new Error("node-iconv not found. Cannot decode " + this + " (encoding is " + this.encoding + "). " +
                            "Please run 'npm install iconv' and try again");
        }
    }
});

module.exports = Text;
<commit_msg>assets.Text: Further improved iconv error handling<commit_after>/**
 * @class Text
 * @extends Asset
 */

var util = require('util'),
    _ = require('underscore'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    Asset = require('./Asset'),
    iconv;

try {
    iconv = require('iconv');
} catch (e) {}

/**
 * new Text(options)
 * =================
 *
 * Create a new Text asset instance.
 *
 * Adds text encoding and decoding support to Asset. Serves as a
 * superclass for `Html`, `Xml`, `Css`, `JavaScript`, `CoffeeScript`,
 * `Json`, and `CacheManifest`.
 *
 * In addition to the options already supported by the Asset base
 * class, these options are supported:
 *
 * - `text` (String) The decoded source of the asset. Can be used
 *              instead of `rawSrc` and `rawSrcProxy`.  - `encoding`
 *              (String) Used to decode and reencode the `rawSrc`. Can
 *              be any encoding supported by the `iconv` module. Can
 *              be changed later using the `encoding`
 *              getter/setter. Defaults to "utf-8" (see the docs for
 *              `defaultEncoding` below). If the asset is loaded via
 *              http, the encoding will be read from the
 *              `Content-Type`, and likewise for `data:` urls.
 *
 * Example:
 *
 *     var textAsset = new Text({
 *         // "æøå" in iso-8859-1:
 *         rawSrc: new Buffer([0xe6, 0xf8, 0xe5]),
 *         encoding: "iso-8859-1"
 *     });
 *     textAsset.text; // "æøå" (decoded JavaScript string)
 *     textAsset.encoding = 'utf-8';
 *     textAsset.rawSrc; // <Buffer c3 a6 c3 b8 c3 a5>
 */
function Text(config) {
    if ('text' in config) {
        this._text = config.text;
        delete config.text;
    }
    if (config.encoding) {
        this._encoding = config.encoding;
        delete config.encoding;
    }
    Asset.call(this, config);
}

util.inherits(Text, Asset);

extendWithGettersAndSetters(Text.prototype, {
    /**
     * text.isText
     * ===========
     *
     * Property that's true for all Text instances. Avoids reliance on
     * the `instanceof` operator.
     */

    /**
     * text.defaultEncoding
     * ====================
     *
     * The default encoding for the Text (sub)class. Used for decoding
     * the raw source when the encoding cannot be determined by other
     * means, such as a `Content-Type` header (when the asset was
     * fetched via http), or another indicator specific to the given
     * asset type (`@charset` for Css, `<meta
     * http-equiv="Content-Type" ...>` for Html).
     *
     * Factory setting is "utf-8", but you can override it by setting
     * `Text.prototype.defaultEncoding` to another value supported by
     * the `iconv` module.
     */
    defaultEncoding: 'utf-8',

    defaultExtension: '.txt',

    alternativeExtensions: ['.xtemplate'], // For Ext.XTemplate + one.getText

    contentType: 'text/plain',

    /**
     * Text.encoding (getter/setter)
     * =============================
     *
     * Get or set the encoding (charset) used for re-encoding the raw
     * source of the asset. To affect the initial decoding of the
     * `rawSrc` option, provide the `encoding` option to the
     * constructor.
     */
    get encoding() {
        if (!this._encoding) {
            this._encoding = this.defaultEncoding;
        }
        return this._encoding;
    },

    set encoding(encoding) {
        if (encoding !== this.encoding) {
            var text = this.text; // Make sure this._text exists so the rawSrc is decoded before the original encoding is thrown away
            delete this._rawSrc;
            this._encoding = encoding;
            this.markDirty();
        }
    },

    get rawSrc() {
        if (!this._rawSrc) {
            if ('_text' in this || this._parseTree) {
                if (/^utf-?8$/i.test(this.encoding)) {
                    this._rawSrc = new Buffer(this.text, 'utf-8');
                } else if (/^(?:us-?)?ascii$/i.test(this.encoding)) {
                    this._rawSrc = new Buffer(this.text, 'ascii');
                } else if (iconv) {
                    try {
                        this._rawSrc = new iconv.Iconv('utf-8', this.encoding).convert(this.text);
                    } catch (err) {
                        err.message = 'iconv: Converting ' + this.url + ' from UTF-8 to ' + this.encoding + ' failed:\n' + err.message;
                        if (this.assetGraph) {
                            if (err.code === 'EILSEQ') {
                                err.message += '\nTransliterating and ignoring further failures. Data corruption may occur.'
                                this._rawSrc = new iconv.Iconv('utf-8', this.encoding + '//TRANSLIT//IGNORE').convert(this.text);
                            }
                            this.assetGraph.emit('error', err);
                        } else {
                            throw err;
                        }
                    }
                } else {
                    throw new Error("node-iconv not found. Cannot encode " + this + " as " + this.encoding + ". " +
                                    "Please run 'npm install iconv' and try again");
                }
            } else {
                throw new Error("assets.Text.rawSrc getter: No _rawSrc or _text property found, asset not loaded?");
            }
        }
        return this._rawSrc;
    },

    set rawSrc(rawSrc) {
        var assetGraph = this.assetGraph;
        if (assetGraph) {
            assetGraph.removeAsset(this);
        }
        this._rawSrc = rawSrc;
        delete this._parseTree;
        delete this._text;
        if (assetGraph) {
            assetGraph.addAsset(this);
        }
        this.markDirty();
    },

    /**
     * text.text (getter/setter)
     * =========================
     *
     * Get or set the decoded text contents of the of the asset as a
     * JavaScript string. Unlike browsers AssetGraph doesn't try to
     * sniff the charset of your text-based assets. It will fall back
     * to assuming utf-8 if it's unable to determine the
     * encoding/charset from HTTP headers, `<meta
     * http-equiv='Content-Type'>` tags (Html), `@charset` (Css), so
     * if for some reason you're not using utf-8 for all your
     * text-based assets, make sure to provide those hints. Other
     * asset types provide no standard way to specify the charset
     * within the file itself, so presently there's no way to load
     * eg. JavaScript from disc if it's not utf-8 or ASCII, except by
     * overriding `Text.prototype.defaultEncoding` globally.
     *
     * If the internal state has been changed since the asset was
     * initialized, it will automatically be reserialized when the
     * `text` property is retrieved, for example:
     *
     *     var htmlAsset = new Html({
     *         rawSrc: new Buffer("hello");
     *     });
     *     htmlAsset.text; // "hello"
     *     htmlAsset.parseTree.body.innerHTML = "bye";
     *     htmlAsset.markDirty();
     *     htmlAsset.text; // "bye"
     *
     * Setting this property after the outgoing relations have been
     * accessed currently leads to undefined behavior.
     */
    get text() {
        if (!('_text' in this)) {
            this._text = this._getTextFromRawSrc();
        }
        return this._text;
    },

    set text(text) {
        this.unload();
        this._text = text;
        if (this.assetGraph) {
            this.populate();
        }
        this.markDirty();
    },

    markDirty: function () {
        if (this._parseTree) {
            delete this._text;
        }
        return Asset.prototype.markDirty.call(this);
    },

    _getTextFromRawSrc: function () {
        if (!this._rawSrc) {
            throw new Error("assets.Text._getTextFromRawSrc(): No _rawSrc property found.");
        }
        if (/^utf-?8$/i.test(this.encoding)) {
            return this._rawSrc.toString('utf-8');
        } else if (/^(?:us-?)?ascii$/i.test(this.encoding)) {
            return this._rawSrc.toString('ascii');
        } else if (iconv) {
            return new iconv.Iconv(this.encoding, 'utf-8').convert(this._rawSrc).toString('utf-8');
        } else {
            throw new Error("node-iconv not found. Cannot decode " + this + " (encoding is " + this.encoding + "). " +
                            "Please run 'npm install iconv' and try again");
        }
    }
});

module.exports = Text;
<|endoftext|>


<commit_before>/**
 * index.js
 *
 * Async stuff for html-snapshots
 *   Notifier class
 *
 * Copyright (c) 2013, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
var mkdirp = require("mkdirp");
var fs = require("fs");

module.exports = {
  Notifier: Notifier
};

/**
 * Notifier
 * Watches the outputDir and when all the files exist,
 * calls the listener indicating the snapshots are done.
 */
function Notifier() {

  // our private files collection
  // contains a file and timer:
  //   "filename": {timer: timerid}
  var files = {};

  // true if a timeout occurred
  var error = false;

  // our reference to the listener
  var callback = function(){};

  // our reference to the watcher
  var watcher;

  /**
   * Watch the outputDir, when a file exists, remove it from our files array.
   * If the files array is empty, call the listener and stop watching.
   */
  this.start = function(outputDir, listener) {
    callback = listener;

    // make the directory if it doesn't exist
    if (!fs.existsSync(outputDir)) {
      mkdirp.sync(outputDir);
    }

    // watch for events
    // arguments can repeat and are unreliable, so manage ourselves
    // uses the child process expected output to determine success or failure
    // if the file exists, then it succeeded.
    watcher = fs.watch(outputDir, (function(self){
      return function(event, filename) {
        //console.log("event: "+event+", filename: "+filename);
        for (var file in files) {
          if (fs.existsSync(file)) {
            self.remove(file);
            break;
          }
        }
        // if all the files exist, we're done
        if (self.fileCount() === 0) {
          callback(error ? false : undefined);
          watcher.close();
        }
      };
    })(this));
  };

  /**
   * boolean indicating that start has been called
   */
  this.isStarted = function() {
    return (typeof watcher !== "undefined");
  };

  /**
   * add a file to the files array if it's not there
   */
  this.add = function(outputFile, timeout) {
    if (!this.exists(outputFile)) {

      // help make sure we evaluate after the child process
      var padTimeout = parseInt(timeout, 10) + parseInt(250, 10);

      // setup a timeout handler to detect failure
      var timer = setTimeout((function(self){
        return function(){

          // if the output file has not already been removed
          if (self.exists(outputFile)) {
            error = !fs.existsSync(outputFile);
            self.remove(outputFile);

            // if we're done right now, finish
            if (self.fileCount() === 0) {
              callback(self.isError() ? false : undefined);
              if (self.isStarted()) {
                self.closeWatcher();
              }
            }
          }
        };
      })(this), parseInt(padTimeout, 10));

      // add the file tracking object
      files[outputFile] = { timer: timer };
    }
  };

  /**
   * remove a file from the files array if it's there
   */
  this.remove = function(outputFile) {
    if (this.exists(outputFile)) {
      clearTimeout(files[outputFile].timer);
      delete files[outputFile];
    }
  };

  /**
   * Provide a way to audit the file count
   */
  this.fileCount = function() {
    return Object.keys(files).length;
  };

  /**
   * Return true if a file exists in the internal files array
   */
  this.exists = function(outputFile) {
    return (!!files[outputFile]);
  };

  this.triggerError = function() {
    error = true;
  };

  this.isError = function() {
    return error;
  };

  this.closeWatcher = function() {
    return watcher.close();
  };
}<commit_msg>Fixed async error overwrite, increased fail wait padding<commit_after>/**
 * index.js
 *
 * Async stuff for html-snapshots
 *   Notifier class
 *
 * Copyright (c) 2013, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
var mkdirp = require("mkdirp");
var fs = require("fs");

module.exports = {
  Notifier: Notifier
};

/**
 * Notifier
 * Watches the outputDir and when all the files exist,
 * calls the listener indicating the snapshots are done.
 */
function Notifier() {

  // our private files collection
  // contains a file and timer:
  //   "filename": {timer: timerid}
  var files = {};

  // true if a timeout occurred
  var error = false;

  // our reference to the listener
  var callback = function(){};

  // our reference to the watcher
  var watcher;

  /**
   * Watch the outputDir, when a file exists, remove it from our files array.
   * If the files array is empty, call the listener and stop watching.
   */
  this.start = function(outputDir, listener) {
    callback = listener;

    // make the directory if it doesn't exist
    if (!fs.existsSync(outputDir)) {
      mkdirp.sync(outputDir);
    }

    // watch for events
    // arguments can repeat and are unreliable, so manage ourselves
    // uses the child process expected output to determine success or failure
    // if the file exists, then it succeeded.
    watcher = fs.watch(outputDir, (function(self){
      return function(event, filename) {
        //console.log("event: "+event+", filename: "+filename);
        for (var file in files) {
          if (fs.existsSync(file)) {
            self.remove(file);
            break;
          }
        }
        // if all the files exist, we're done
        if (self.fileCount() === 0) {
          callback(error ? false : undefined);
          watcher.close();
        }
      };
    })(this));
  };

  /**
   * boolean indicating that start has been called
   */
  this.isStarted = function() {
    return (typeof watcher !== "undefined");
  };

  /**
   * add a file to the files array if it's not there
   */
  this.add = function(outputFile, timeout) {
    if (!this.exists(outputFile)) {

      // help make sure we evaluate after the child process
      var padTimeout = parseInt(timeout, 10) + parseInt(1000, 10);

      // setup a timeout handler to detect failure
      var timer = setTimeout((function(self){
        return function(){

          // if the output file has not already been removed
          if (self.exists(outputFile)) {
            self.setError(!fs.existsSync(outputFile));
            self.remove(outputFile);

            // if we're done right now, finish
            if (self.fileCount() === 0) {
              callback(self.isError() ? false : undefined);
              if (self.isStarted()) {
                self.closeWatcher();
              }
            }
          }
        };
      })(this), parseInt(padTimeout, 10));

      // add the file tracking object
      files[outputFile] = { timer: timer };
    }
  };

  /**
   * remove a file from the files array if it's there
   */
  this.remove = function(outputFile) {
    if (this.exists(outputFile)) {
      clearTimeout(files[outputFile].timer);
      delete files[outputFile];
    }
  };

  /**
   * Provide a way to audit the file count
   */
  this.fileCount = function() {
    return Object.keys(files).length;
  };

  /**
   * Return true if a file exists in the internal files array
   */
  this.exists = function(outputFile) {
    return (!!files[outputFile]);
  };

  this.setError = function(value) {
    if (!error) {
      error = value;
    }
  };

  this.isError = function() {
    return error;
  };

  this.closeWatcher = function() {
    return watcher.close();
  };
}<|endoftext|>


<commit_before>/*!
  backbone-bootstrap-modals 0.1.3
  http://github.com/leafygreen/backbone-bootstrap-modals

  Licensed under the MIT license.
*/

(function (root, factory) {

  if (typeof define === "function" && define.amd) {
    // AMD (+ global for extensions)
    define(["underscore", "backbone"], function (_, Backbone) {
      return (root.BackboneBootstrapModals = factory(_, Backbone));
    });
  } else if (typeof exports === "object") {
    // CommonJS
    module.exports = factory(require("underscore"), require("backbone"));
  } else {
    // Browser
    root.BackboneBootstrapModals = factory(root._, root.Backbone);
  }}(this, function (_, Backbone) {

  "use strict";

// Create Project Namespace
var BackboneBootstrapModals = {};

// BaseHeaderView
// ---------------------------------
//
// A simple view representing the modal title and dismiss icon.

BackboneBootstrapModals.BaseHeaderView = Backbone.View.extend({
  className: 'modal-header',

  initialize: function (opts) {
    var options = opts || {};
    this.label = options.label || '';
    this.labelId = options.labelId || 'myModalLabel';
    this.labelTagName = options.labelTagName || 'h4';
    this.showClose = (options.showClose !== undefined) ? options.showClose : true;
  },

  render: function() {
    var $header = $('<'+this.labelTagName+'>', {
      'id': this.labelId,
      'class': 'modal-title'
    }).text(this.label);

    var $close;
    if (this.showClose) {
      $close = $('<button>', {
        'type': 'button',
        'class': 'close',
        'data-dismiss': 'modal',
        'aria-hidden': 'true'
      }).html('×');
    }
    this.$el.html([$close, $header]);
    return this;
  }

});

// BaseBodyView
// ---------------------------------
//
// A simple view representing a minimal modal body

BackboneBootstrapModals.BaseBodyView = Backbone.View.extend({
  className: 'modal-body',

  initialize: function (opts) {
    var options = opts || {};
    this.text = options.text;
    this.textTagName = options.textTagName || 'p';
  },

  render: function() {
    var html;
    if (this.text) {
      if (_.isArray(this.text)) {
        html = _.map(this.text, _.bind(this.createTag, this));
      } else {
        html = this.createTag(this.text);
      }
      this.$el.html(html);
    }
    return this;
  },

  createTag: function(text) {
    var $tag = $('<'+this.textTagName+'>').text(text);
    return $tag;
  }

});

// BaseFooterView
// ---------------------------------
//
// A simple view representing a set of modal action buttons

BackboneBootstrapModals.BaseFooterView = Backbone.View.extend({
  className: 'modal-footer',

  initialize: function (opts) {
    var options = opts || {};
    this.buttons = options.buttons || [];
  },

  render: function() {
    function createButton(button) {
      var $btn = $('<button>', _.extend({
        'id': button.id,
        'class': button.className
      }, button.attributes)).text(button.value);
      return $btn;
    }

    this.$el.html(this.buttons.map(createButton));
    return this;
  }

});

// BaseModal
// ---------------------------------
//
// The base class all other modals extend.

BackboneBootstrapModals.BaseModal = Backbone.View.extend({
  className: 'modal',
  attributes: {
    'tabindex': '-1',
    'role': 'dialog',
    'aria-labelledby': 'myModalLabel'
  },

  // Handlers for Bootstrap Modal Events: http://getbootstrap.com/javascript/#modals
  bootstrapModalEvents: {
    'show.bs.modal': 'onShowBsModal',
    'shown.bs.modal': 'onShownBsModal',
    'hide.bs.modal': 'onHideBsModal',
    'hidden.bs.modal': 'onHiddenBsModal'
  },

  // The default views if not overridden or specified in options
  headerView: BackboneBootstrapModals.BaseHeaderView,
  bodyView: BackboneBootstrapModals.BaseBodyView,
  footerView: BackboneBootstrapModals.BaseFooterView,

  // The default modal options if not overridden or specified in options
  modalOptions: {
    backdrop: true,
    keyboard: true
  },

  // properties to copy from options
  modalProperties: [
    'modalOptions',
    'headerView',
    'headerViewOptions',
    'bodyView',
    'bodyViewOptions',
    'footerView',
    'footerViewOptions'
  ],

  constructor: function(opts) {
    var options = opts || {};
    this.shown = false;
    _.extend(this, _.pick(options, this.modalProperties));
    Backbone.View.prototype.constructor.apply(this, arguments);
  },

  render: function() {
    // Remove any existing views before appending subviews to the layout
    this.removeSubviews();
    this.initializeSubviews();

    this.$el.html([
      '
',
      '
',
      '
',
      '
'
    ].join(''));

    var $modalContent = this.$el.find('.modal-content');

    if (this.headerView) {
      $modalContent.append(this.headerViewInstance.render().$el);
    }

    if (this.bodyView) {
      $modalContent.append(this.bodyViewInstance.render().$el);
    }

    if (this.footerView) {
      $modalContent.append(this.footerViewInstance.render().$el);
    }

    // Allow onRender callback for custom hooks
    if (this.onRender) { this.onRender.call(this); }

    if (!this.shown && this.modalOptions.show !== false) {
        this.$el.modal(this.modalOptions);
    }

    return this;
  },

  // Initialize views for header, body, and footer sections.
  initializeSubviews: function() {
    this.headerViewInstance = this.buildSubview(
      this.getHeaderView(),
      _.result(this, 'headerViewOptions'),
      'modal-header');

    this.bodyViewInstance = this.buildSubview(
      this.getBodyView(),
      _.result(this, 'bodyViewOptions'),
      'modal-body');

    this.footerViewInstance = this.buildSubview(
      this.getFooterView(),
      _.result(this, 'footerViewOptions'),
      'modal-footer');
  },

  // Accessors that can be overridden to allow dynamic subview definitions
  getHeaderView: function() {
    return this.headerView;
  },

  getBodyView: function() {
    return this.bodyView;
  },

  getFooterView: function() {
    return this.footerView;
  },

  // Construct view instance with specified options and
  // additionally propagate model/collection/className attributes
  buildSubview: function(viewClass, viewOptions, defaultClassName) {
    if (!viewClass) {
      throw new Error("view not specified");
    }

    var options = _.extend({
      model: this.model,
      collection: this.collection
    }, viewOptions);

    // Ensure the proper className if not specified through the viewClass or viewOptions
    if (!(viewClass.prototype.className || options.className)) {
      options.className = defaultClassName;
    }

    return new viewClass(options);
  },

  remove: function () {
    this.removeSubviews();
    Backbone.View.prototype.remove.apply(this, arguments);

    // Allow onClose callback for custom hooks
    if (this.onClose) { this.onClose.call(this); }

    return this;
  },

  removeSubviews: function() {
    if (this.headerViewInstance) { this.removeSubview(this.headerViewInstance); }
    if (this.bodyViewInstance) { this.removeSubview(this.bodyViewInstance);  }
    if (this.footerViewInstance) { this.removeSubview(this.footerViewInstance);  }
  },

  // Attempt to use Marionette's close first, falling back to Backbone's remove
  removeSubview: function(viewInstance) {
    if (Backbone.Marionette && viewInstance.close) {
        viewInstance.close.apply(viewInstance);
    } else if (viewInstance.remove) {
        viewInstance.remove.apply(viewInstance);
    }
  },

  // Override default implementation to always include bootstrapModalEvents
  // without clobbering the default events hash
  delegateEvents: function(events) {
    var combinedEvents = events || _.result(this, 'events') || {};

    _.each(this.getAdditionalEventsToDelegate(), function(eventHash) {
      _.extend(combinedEvents, eventHash);
    });

    Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
  },

  // Helper method for use in overridden delegateEvents call.
  // This can be overridden in extended classes to provide additional
  // events, e.g. ConfirmationModal.confirmationEvents
  getAdditionalEventsToDelegate: function() {
    return [this.bootstrapModalEvents];
  },

  show: function() {
    this.$el.modal('show');
  },

  hide: function() {
    this.$el.modal('hide');
  },

  // This event fires immediately when the show instance method is called.
  onShowBsModal: function() {
    if (this.onShow) { this.onShow.call(this); }
  },

  // This event is fired when the modal has been made visible to the user (will wait for CSS transitions to complete).
  onShownBsModal: function() {
      this.shown = true;
      if (this.onShown) { this.onShown.call(this); }
  },

  // This event is fired immediately when the hide instance method has been called.
  onHideBsModal: function() {
    if (this.onHide) { this.onHide.call(this); }
  },

  // This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete).
  onHiddenBsModal: function() {
    this.shown = false;
    this.remove();
    if (this.onHidden) { this.onHidden.call(this); }
  },

});

// ConfirmationModal
// ---------------------------------
//
// A simple modal for simple confirmation dialogs

BackboneBootstrapModals.ConfirmationModal = BackboneBootstrapModals.BaseModal.extend({

  confirmationEvents: {
    'click #confirmation-confirm-btn': 'onClickConfirm',
    'keypress .modal-body': 'onKeyPress'
  },

  // Default set of BaseModal options for use as ConfirmationModal
  headerViewOptions: function() {
    return {
      label: _.result(this, 'label'),
      labelId: _.result(this, 'labelId'),
      labelTagName: _.result(this, 'labelTagName'),
      showClose: _.result(this, 'showClose')
    };
  },

  bodyViewOptions: function() {
    return {
      text: _.result(this, 'text'),
      textTagName: _.result(this, 'textTagName')
    };
  },

  footerViewOptions: function() {
    var buttons = [],
        showCancel = _.result(this, 'showCancel');
    if (showCancel === undefined || showCancel === true) {
      buttons.push({
        id: 'confirmation-cancel-btn',
        className: 'btn '+ (_.result(this, 'cancelClassName') || 'btn-default'),
        value: (_.result(this, 'cancelText') || 'Cancel'),
        attributes: { 'data-dismiss': 'modal', 'aria-hidden': 'true' }
      });
    }
    buttons.push({
      id: 'confirmation-confirm-btn',
      className: 'btn '+ (_.result(this, 'confirmClassName') || 'btn-primary'),
      value: (_.result(this, 'confirmText') || 'Confirm')
    });
    return {
      buttons: buttons
    };
  },

  // properties to copy from options
  confirmationProperties: [
    'label',
    'labelId',
    'labelTagName',
    'showClose',
    'text',
    'textTagName',
    'confirmText',
    'confirmClassName',
    'cancelText',
    'cancelClassName',
    'showCancel',
    'onConfirm'
  ],

  initialize: function(opts) {
    var options = opts || {};
    _.extend(this, _.pick(options, this.confirmationProperties));
  },

  // Override BaseModal hook to add additional default delegated events
  getAdditionalEventsToDelegate: function() {
    var eventHashes = BackboneBootstrapModals.BaseModal.prototype.getAdditionalEventsToDelegate.call(this);
    return eventHashes.concat(this.confirmationEvents);
  },

  onKeyPress: function(e) {
    // if the user presses the enter key
    if (e.which === 13) {
      e.preventDefault();
      this.$('#confirmation-confirm-btn').click();
    }
  },

  onClickConfirm: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.confirmProgress(e);
  },

  confirmProgress: function(e){
    // Execute the specified callback if it exists, then hide the modal.
    // The modal will not be hidden if the callback returns false.
    if (this.onConfirm) {
        if (this.onConfirm.call(this, e) !== false) {
            this.hide();
        }
    } else {
        this.hide();
    }
  }
});

// WizardModal
// ---------------------------------
//
// A simple modal for dialogs with multi-step wizard behavior

BackboneBootstrapModals.WizardModal = BackboneBootstrapModals.BaseModal.extend({

  wizardEvents: {
    'click #confirmation-previous-btn': 'onClickPrevious',
    'click #confirmation-next-btn': 'onClickNext',
    'keypress .modal-body': 'onKeyPress'
  },

  getBodyView: function(options) {
    return this.currentStep.view;
  },

  headerViewOptions: function() {
    return {
      label: this.currentStep.label
    };
  },

  bodyViewOptions: function() {
    return this.currentStep.viewOptions;
  },

  footerViewOptions: function() {
    var buttons = this.getButtonsForCurrentStep();
    return {
      buttons: buttons
    };
  },

  initialize: function(opts) {
    this.initializeSteps(opts);
    this.setCurrentStep(0); // always start with the first element
  },

  initializeSteps: function(opts) {
    // Override any defined stepGraph with the one in options if present
    if (opts.stepGraph) {
      this.stepGraph = opts.stepGraph;
    }
    // Validate the steps structure
    if (!this.stepGraph || !_.isArray(this.stepGraph) || !this.stepGraph.length) {
      throw new Error("steps array must be specified and non-empty");
    }
  },

  setCurrentStep: function(index) {
    this.currentStep = this.stepGraph[index];
  },

  getButtonsForCurrentStep: function() {
    var previousStepIndex = this.currentStep.previousIndex,
        previousText = this.currentStep.previousText || 'Previous',
        previousClassName = this.currentStep.previousClassName || 'btn-default',
        nextStepIndex = this.currentStep.nextIndex,
        nextText = this.currentStep.nextText || 'Next',
        nextClassName = this.currentStep.nextClassName || 'btn-primary',
        buttons = [];
    if (previousStepIndex !== undefined) {
      buttons.push({ id: 'confirmation-previous-btn', className: 'btn '+previousClassName, value: previousText });
    }
    buttons.push({ id: 'confirmation-next-btn', className: 'btn '+nextClassName, value: nextText });
    return buttons;
  },

  // Override BaseModal hook to add additional default delegated events
  getAdditionalEventsToDelegate: function() {
    var eventHashes = BackboneBootstrapModals.BaseModal.prototype.getAdditionalEventsToDelegate.call(this);
    return eventHashes.concat(this.wizardEvents);
  },

  renderPreviousStep: function() {
    this.setCurrentStep(this.currentStep.previousIndex);
    this.render();
  },

  renderNextStep: function() {
    var nextStepIndex = this.currentStep.nextIndex;
    if (nextStepIndex !== undefined) {
      this.setCurrentStep(nextStepIndex);
      this.render();
    } else {
      // If no more steps, hide the dialog
      this.hide();
    }
  },

  onClickPrevious: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.renderPreviousStep();
  },

  onKeyPress: function(e) {
    // if the user presses the enter key
    if (e.which === 13) {
      e.preventDefault();
      this.$('#confirmation-next-btn').click();
    }
  },

  onClickNext: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.goForward(e);
  },

  goForward: function(e) {
    // Execute the specified callback if it exists, then proceed.
    // The modal will not proceed if the callback returns false.
    if (this.currentStep.onNext) {
        if (this.currentStep.onNext.call(this, e) === false) {
            return;
        }
    }

    this.renderNextStep();
  }
});

  return BackboneBootstrapModals;
}));<commit_msg>Ensure that jQuery is required also.<commit_after>/*!
  backbone-bootstrap-modals 0.1.3
  http://github.com/leafygreen/backbone-bootstrap-modals

  Licensed under the MIT license.
*/

(function (root, factory) {

  if (typeof define === "function" && define.amd) {
    // AMD (+ global for extensions)
    define(["underscore", "backbone", "jquery"], function (_, Backbone, $) {
      return (root.BackboneBootstrapModals = factory(_, Backbone, $));
    });
  } else if (typeof exports === "object") {
    // CommonJS
    module.exports = factory(require("underscore"), require("backbone"), require("jquery"));
  } else {
    // Browser
    root.BackboneBootstrapModals = factory(root._, root.Backbone, root.$);
  }}(this, function (_, Backbone, $) {

  "use strict";

// Create Project Namespace
var BackboneBootstrapModals = {};

// BaseHeaderView
// ---------------------------------
//
// A simple view representing the modal title and dismiss icon.

BackboneBootstrapModals.BaseHeaderView = Backbone.View.extend({
  className: 'modal-header',

  initialize: function (opts) {
    var options = opts || {};
    this.label = options.label || '';
    this.labelId = options.labelId || 'myModalLabel';
    this.labelTagName = options.labelTagName || 'h4';
    this.showClose = (options.showClose !== undefined) ? options.showClose : true;
  },

  render: function() {
    var $header = $('<'+this.labelTagName+'>', {
      'id': this.labelId,
      'class': 'modal-title'
    }).text(this.label);

    var $close;
    if (this.showClose) {
      $close = $('<button>', {
        'type': 'button',
        'class': 'close',
        'data-dismiss': 'modal',
        'aria-hidden': 'true'
      }).html('×');
    }
    this.$el.html([$close, $header]);
    return this;
  }

});

// BaseBodyView
// ---------------------------------
//
// A simple view representing a minimal modal body

BackboneBootstrapModals.BaseBodyView = Backbone.View.extend({
  className: 'modal-body',

  initialize: function (opts) {
    var options = opts || {};
    this.text = options.text;
    this.textTagName = options.textTagName || 'p';
  },

  render: function() {
    var html;
    if (this.text) {
      if (_.isArray(this.text)) {
        html = _.map(this.text, _.bind(this.createTag, this));
      } else {
        html = this.createTag(this.text);
      }
      this.$el.html(html);
    }
    return this;
  },

  createTag: function(text) {
    var $tag = $('<'+this.textTagName+'>').text(text);
    return $tag;
  }

});

// BaseFooterView
// ---------------------------------
//
// A simple view representing a set of modal action buttons

BackboneBootstrapModals.BaseFooterView = Backbone.View.extend({
  className: 'modal-footer',

  initialize: function (opts) {
    var options = opts || {};
    this.buttons = options.buttons || [];
  },

  render: function() {
    function createButton(button) {
      var $btn = $('<button>', _.extend({
        'id': button.id,
        'class': button.className
      }, button.attributes)).text(button.value);
      return $btn;
    }

    this.$el.html(this.buttons.map(createButton));
    return this;
  }

});

// BaseModal
// ---------------------------------
//
// The base class all other modals extend.

BackboneBootstrapModals.BaseModal = Backbone.View.extend({
  className: 'modal',
  attributes: {
    'tabindex': '-1',
    'role': 'dialog',
    'aria-labelledby': 'myModalLabel'
  },

  // Handlers for Bootstrap Modal Events: http://getbootstrap.com/javascript/#modals
  bootstrapModalEvents: {
    'show.bs.modal': 'onShowBsModal',
    'shown.bs.modal': 'onShownBsModal',
    'hide.bs.modal': 'onHideBsModal',
    'hidden.bs.modal': 'onHiddenBsModal'
  },

  // The default views if not overridden or specified in options
  headerView: BackboneBootstrapModals.BaseHeaderView,
  bodyView: BackboneBootstrapModals.BaseBodyView,
  footerView: BackboneBootstrapModals.BaseFooterView,

  // The default modal options if not overridden or specified in options
  modalOptions: {
    backdrop: true,
    keyboard: true
  },

  // properties to copy from options
  modalProperties: [
    'modalOptions',
    'headerView',
    'headerViewOptions',
    'bodyView',
    'bodyViewOptions',
    'footerView',
    'footerViewOptions'
  ],

  constructor: function(opts) {
    var options = opts || {};
    this.shown = false;
    _.extend(this, _.pick(options, this.modalProperties));
    Backbone.View.prototype.constructor.apply(this, arguments);
  },

  render: function() {
    // Remove any existing views before appending subviews to the layout
    this.removeSubviews();
    this.initializeSubviews();

    this.$el.html([
      '
',
      '
',
      '
',
      '
'
    ].join(''));

    var $modalContent = this.$el.find('.modal-content');

    if (this.headerView) {
      $modalContent.append(this.headerViewInstance.render().$el);
    }

    if (this.bodyView) {
      $modalContent.append(this.bodyViewInstance.render().$el);
    }

    if (this.footerView) {
      $modalContent.append(this.footerViewInstance.render().$el);
    }

    // Allow onRender callback for custom hooks
    if (this.onRender) { this.onRender.call(this); }

    if (!this.shown && this.modalOptions.show !== false) {
        this.$el.modal(this.modalOptions);
    }

    return this;
  },

  // Initialize views for header, body, and footer sections.
  initializeSubviews: function() {
    this.headerViewInstance = this.buildSubview(
      this.getHeaderView(),
      _.result(this, 'headerViewOptions'),
      'modal-header');

    this.bodyViewInstance = this.buildSubview(
      this.getBodyView(),
      _.result(this, 'bodyViewOptions'),
      'modal-body');

    this.footerViewInstance = this.buildSubview(
      this.getFooterView(),
      _.result(this, 'footerViewOptions'),
      'modal-footer');
  },

  // Accessors that can be overridden to allow dynamic subview definitions
  getHeaderView: function() {
    return this.headerView;
  },

  getBodyView: function() {
    return this.bodyView;
  },

  getFooterView: function() {
    return this.footerView;
  },

  // Construct view instance with specified options and
  // additionally propagate model/collection/className attributes
  buildSubview: function(viewClass, viewOptions, defaultClassName) {
    if (!viewClass) {
      throw new Error("view not specified");
    }

    var options = _.extend({
      model: this.model,
      collection: this.collection
    }, viewOptions);

    // Ensure the proper className if not specified through the viewClass or viewOptions
    if (!(viewClass.prototype.className || options.className)) {
      options.className = defaultClassName;
    }

    return new viewClass(options);
  },

  remove: function () {
    this.removeSubviews();
    Backbone.View.prototype.remove.apply(this, arguments);

    // Allow onClose callback for custom hooks
    if (this.onClose) { this.onClose.call(this); }

    return this;
  },

  removeSubviews: function() {
    if (this.headerViewInstance) { this.removeSubview(this.headerViewInstance); }
    if (this.bodyViewInstance) { this.removeSubview(this.bodyViewInstance);  }
    if (this.footerViewInstance) { this.removeSubview(this.footerViewInstance);  }
  },

  // Attempt to use Marionette's close first, falling back to Backbone's remove
  removeSubview: function(viewInstance) {
    if (Backbone.Marionette && viewInstance.close) {
        viewInstance.close.apply(viewInstance);
    } else if (viewInstance.remove) {
        viewInstance.remove.apply(viewInstance);
    }
  },

  // Override default implementation to always include bootstrapModalEvents
  // without clobbering the default events hash
  delegateEvents: function(events) {
    var combinedEvents = events || _.result(this, 'events') || {};

    _.each(this.getAdditionalEventsToDelegate(), function(eventHash) {
      _.extend(combinedEvents, eventHash);
    });

    Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
  },

  // Helper method for use in overridden delegateEvents call.
  // This can be overridden in extended classes to provide additional
  // events, e.g. ConfirmationModal.confirmationEvents
  getAdditionalEventsToDelegate: function() {
    return [this.bootstrapModalEvents];
  },

  show: function() {
    this.$el.modal('show');
  },

  hide: function() {
    this.$el.modal('hide');
  },

  // This event fires immediately when the show instance method is called.
  onShowBsModal: function() {
    if (this.onShow) { this.onShow.call(this); }
  },

  // This event is fired when the modal has been made visible to the user (will wait for CSS transitions to complete).
  onShownBsModal: function() {
      this.shown = true;
      if (this.onShown) { this.onShown.call(this); }
  },

  // This event is fired immediately when the hide instance method has been called.
  onHideBsModal: function() {
    if (this.onHide) { this.onHide.call(this); }
  },

  // This event is fired when the modal has finished being hidden from the user (will wait for CSS transitions to complete).
  onHiddenBsModal: function() {
    this.shown = false;
    this.remove();
    if (this.onHidden) { this.onHidden.call(this); }
  },

});

// ConfirmationModal
// ---------------------------------
//
// A simple modal for simple confirmation dialogs

BackboneBootstrapModals.ConfirmationModal = BackboneBootstrapModals.BaseModal.extend({

  confirmationEvents: {
    'click #confirmation-confirm-btn': 'onClickConfirm',
    'keypress .modal-body': 'onKeyPress'
  },

  // Default set of BaseModal options for use as ConfirmationModal
  headerViewOptions: function() {
    return {
      label: _.result(this, 'label'),
      labelId: _.result(this, 'labelId'),
      labelTagName: _.result(this, 'labelTagName'),
      showClose: _.result(this, 'showClose')
    };
  },

  bodyViewOptions: function() {
    return {
      text: _.result(this, 'text'),
      textTagName: _.result(this, 'textTagName')
    };
  },

  footerViewOptions: function() {
    var buttons = [],
        showCancel = _.result(this, 'showCancel');
    if (showCancel === undefined || showCancel === true) {
      buttons.push({
        id: 'confirmation-cancel-btn',
        className: 'btn '+ (_.result(this, 'cancelClassName') || 'btn-default'),
        value: (_.result(this, 'cancelText') || 'Cancel'),
        attributes: { 'data-dismiss': 'modal', 'aria-hidden': 'true' }
      });
    }
    buttons.push({
      id: 'confirmation-confirm-btn',
      className: 'btn '+ (_.result(this, 'confirmClassName') || 'btn-primary'),
      value: (_.result(this, 'confirmText') || 'Confirm')
    });
    return {
      buttons: buttons
    };
  },

  // properties to copy from options
  confirmationProperties: [
    'label',
    'labelId',
    'labelTagName',
    'showClose',
    'text',
    'textTagName',
    'confirmText',
    'confirmClassName',
    'cancelText',
    'cancelClassName',
    'showCancel',
    'onConfirm'
  ],

  initialize: function(opts) {
    var options = opts || {};
    _.extend(this, _.pick(options, this.confirmationProperties));
  },

  // Override BaseModal hook to add additional default delegated events
  getAdditionalEventsToDelegate: function() {
    var eventHashes = BackboneBootstrapModals.BaseModal.prototype.getAdditionalEventsToDelegate.call(this);
    return eventHashes.concat(this.confirmationEvents);
  },

  onKeyPress: function(e) {
    // if the user presses the enter key
    if (e.which === 13) {
      e.preventDefault();
      this.$('#confirmation-confirm-btn').click();
    }
  },

  onClickConfirm: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.confirmProgress(e);
  },

  confirmProgress: function(e){
    // Execute the specified callback if it exists, then hide the modal.
    // The modal will not be hidden if the callback returns false.
    if (this.onConfirm) {
        if (this.onConfirm.call(this, e) !== false) {
            this.hide();
        }
    } else {
        this.hide();
    }
  }
});

// WizardModal
// ---------------------------------
//
// A simple modal for dialogs with multi-step wizard behavior

BackboneBootstrapModals.WizardModal = BackboneBootstrapModals.BaseModal.extend({

  wizardEvents: {
    'click #confirmation-previous-btn': 'onClickPrevious',
    'click #confirmation-next-btn': 'onClickNext',
    'keypress .modal-body': 'onKeyPress'
  },

  getBodyView: function(options) {
    return this.currentStep.view;
  },

  headerViewOptions: function() {
    return {
      label: this.currentStep.label
    };
  },

  bodyViewOptions: function() {
    return this.currentStep.viewOptions;
  },

  footerViewOptions: function() {
    var buttons = this.getButtonsForCurrentStep();
    return {
      buttons: buttons
    };
  },

  initialize: function(opts) {
    this.initializeSteps(opts);
    this.setCurrentStep(0); // always start with the first element
  },

  initializeSteps: function(opts) {
    // Override any defined stepGraph with the one in options if present
    if (opts.stepGraph) {
      this.stepGraph = opts.stepGraph;
    }
    // Validate the steps structure
    if (!this.stepGraph || !_.isArray(this.stepGraph) || !this.stepGraph.length) {
      throw new Error("steps array must be specified and non-empty");
    }
  },

  setCurrentStep: function(index) {
    this.currentStep = this.stepGraph[index];
  },

  getButtonsForCurrentStep: function() {
    var previousStepIndex = this.currentStep.previousIndex,
        previousText = this.currentStep.previousText || 'Previous',
        previousClassName = this.currentStep.previousClassName || 'btn-default',
        nextStepIndex = this.currentStep.nextIndex,
        nextText = this.currentStep.nextText || 'Next',
        nextClassName = this.currentStep.nextClassName || 'btn-primary',
        buttons = [];
    if (previousStepIndex !== undefined) {
      buttons.push({ id: 'confirmation-previous-btn', className: 'btn '+previousClassName, value: previousText });
    }
    buttons.push({ id: 'confirmation-next-btn', className: 'btn '+nextClassName, value: nextText });
    return buttons;
  },

  // Override BaseModal hook to add additional default delegated events
  getAdditionalEventsToDelegate: function() {
    var eventHashes = BackboneBootstrapModals.BaseModal.prototype.getAdditionalEventsToDelegate.call(this);
    return eventHashes.concat(this.wizardEvents);
  },

  renderPreviousStep: function() {
    this.setCurrentStep(this.currentStep.previousIndex);
    this.render();
  },

  renderNextStep: function() {
    var nextStepIndex = this.currentStep.nextIndex;
    if (nextStepIndex !== undefined) {
      this.setCurrentStep(nextStepIndex);
      this.render();
    } else {
      // If no more steps, hide the dialog
      this.hide();
    }
  },

  onClickPrevious: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.renderPreviousStep();
  },

  onKeyPress: function(e) {
    // if the user presses the enter key
    if (e.which === 13) {
      e.preventDefault();
      this.$('#confirmation-next-btn').click();
    }
  },

  onClickNext: function(e) {
    e.preventDefault();
    e.currentTarget.disabled = true;
    this.goForward(e);
  },

  goForward: function(e) {
    // Execute the specified callback if it exists, then proceed.
    // The modal will not proceed if the callback returns false.
    if (this.currentStep.onNext) {
        if (this.currentStep.onNext.call(this, e) === false) {
            return;
        }
    }

    this.renderNextStep();
  }
});

  return BackboneBootstrapModals;
}));
<|endoftext|>


<commit_before>/*globals jQuery */

/*
* Copyright (c) 2012 Washington State Department of Transportation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*
*/

(function ($) {
    "use strict";

    function compareListItems(a, b) {
        var lia, lib;
        lia = $(a).data("arcGisServerListItem");
        lib = $(b).data("arcGisServerListItem");
        if (lia._name > lib._name) {
            return 1;
        } else if (lia._name < lib._name) {
            return -1;
        } else {
            return 0;
        }
    }

    // A regex that matches an ArcGIS Server REST endpoint.  Capture 1 is the protocol, 2 is the server name, capture 3 is the instance name (usually "ArcGIS").
    var restEndpointRe = /(https?\:\/\/)(.+)\/(\w+)\/rest(?:\/services)?/i;

    $.widget("ui.arcGisServerList", {
        options: {
            servers: null
        },
        _serverList: null,
        _inputBox: null,
        _addButton: null,
        _addServer: function(server) {
            var self = this;
            $("
").attr({
                "data-server": server
            }).appendTo(this._serverList).arcGisServerListItem({
                server: server,
                remove: function(event, ui) {
                    $("[data-server='" + ui.server + "']").remove();
                    self._trigger("remove", self, {server: server});
                }
            });
            this._trigger("add", self, {server: server});
        },
        getServerNames: function() {
            var i, l, serverItems = $("[data-server]", this.element), servers = null;
            for (i=0, l=serverItems.length; i < l; i++) {
                if (!servers) {
                    servers = [];
                }
                servers.push($(serverItems[i]).data("server"));
            }
            return servers;
        },
        sort: function() {
            // Sorts items in the list by the server name.
            var list = $(".ui-ags-list", this.element)[0];
            $("> li", list).sort(compareListItems).appendTo(list);
            // Trigger the "sort" event.
            this._trigger("sort", this, {});
            return this;
        },
        _create: function() {
            var self = this, addServer, removeServer, i, l, inputDiv;
            $(self.element).addClass('ui-widget');
            inputDiv = $("
").appendTo(self.element);
            self._inputBox = $("<input type='url' class='ui-ags-url-box' placeholder='Enter server name'>").appendTo(inputDiv);
            self._addButton = $("<button type='button'>Add ArcGIS Server</button>").appendTo(inputDiv).button();
            self._serverList = $("
").appendTo(this.element);

            // If any servers were defined in the options, add them now.
            if (self.options.servers !== null) {
                for (i=0, l=self.options.servers.length; i < l; i++) {
                    self._addServer(self.options.servers[i]);
                }
            }

            self._addButton.click(function() {
                self._addServer(self._inputBox.val());
            });
            return this;
        }
    });

    /**
     * This represents one of the individual items in the arcGisServerList
     */
    $.widget("ui.arcGisServerListItem", {
        options: {
            server: null
        },
        // _url: null,
        _protocol: "http://",
        _name: null,
        _instance: "ArcGIS",
        _list: null,
        _create: function() {
            var self = this, match, link, removeAction;

            removeAction = function() {
                self._trigger("remove", self, {"server": self.options.server});
                return false;
            };

            if (this.options.server === null) {
                throw new Error("Server name not provided.");
            }
            match = restEndpointRe.exec(this.options.server);
            // Check the server parameter to see if it is a full URL or just the server name.
            if (match) {
                this._protocol = match[1];
                this._name = match[2];
                this._instance = match[3];
            } else {
                this._name = this.options.server;
            }

            // Create the list of links.
            $(this.element).text(this._name);
            this._list = $("
").appendTo(this.element);

            // Add the manager link.
            link = $("ArcGIS Server Manager").attr({
                href: [this._protocol + this._name, this._instance, "Manager"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            // Add rest admin link.
            link = $("REST administration").attr({
                href: [this._protocol + this._name, this._instance, "rest", "admin"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            // Add the REST endpoint link.
            link = $("REST endpoint").attr({
                href: [this._protocol + this._name, this._instance, "rest"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            link = $("Remove this server from the list").click(removeAction);
            $("
").append(link).appendTo(this._list);
        }
    });

}(jQuery));<commit_msg>Added sort button<commit_after>/*globals jQuery */

/*
* Copyright (c) 2012 Washington State Department of Transportation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*
*/

(function ($) {
    "use strict";

    function compareListItems(a, b) {
        var lia, lib;
        lia = $(a).data("arcGisServerListItem");
        lib = $(b).data("arcGisServerListItem");
        if (lia._name > lib._name) {
            return 1;
        } else if (lia._name < lib._name) {
            return -1;
        } else {
            return 0;
        }
    }

    // A regex that matches an ArcGIS Server REST endpoint.  Capture 1 is the protocol, 2 is the server name, capture 3 is the instance name (usually "ArcGIS").
    var restEndpointRe = /(https?\:\/\/)(.+)\/(\w+)\/rest(?:\/services)?/i;

    $.widget("ui.arcGisServerList", {
        options: {
            servers: null
        },
        _serverList: null,
        _inputBox: null,
        _addButton: null,
        _sortButton: null,
        _addServer: function(server) {
            var self = this;
            $("
").attr({
                "data-server": server
            }).appendTo(this._serverList).arcGisServerListItem({
                server: server,
                remove: function(event, ui) {
                    $("[data-server='" + ui.server + "']").remove();
                    self._trigger("remove", self, {server: server});
                }
            });
            this._trigger("add", self, {server: server});
        },
        getServerNames: function() {
            var i, l, serverItems = $("[data-server]", this.element), servers = null;
            for (i=0, l=serverItems.length; i < l; i++) {
                if (!servers) {
                    servers = [];
                }
                servers.push($(serverItems[i]).data("server"));
            }
            return servers;
        },
        sort: function() {
            // Sorts items in the list by the server name.
            var list = $(".ui-ags-list", this.element)[0];
            $("> li", list).sort(compareListItems).appendTo(list);
            // Trigger the "sort" event.
            this._trigger("sort", this, {});
            return this;
        },
        _create: function() {
            var self = this, addServer, removeServer, i, l, inputDiv;
            $(self.element).addClass('ui-widget');
            inputDiv = $("
").appendTo(self.element);
            self._inputBox = $("<input type='url' class='ui-ags-url-box' placeholder='Enter server name'>").appendTo(inputDiv);
            self._addButton = $("<button type='button'>Add ArcGIS Server</button>").appendTo(inputDiv).button({
                icons: {
                    primary: "ui-icon-plusthick"
                }
            });
            // Add the sort button
            self._sortButton = $("<button type='button'>Sort</button>").appendTo(self.element).button({
                icons: {
                    primary: "ui-icon-shuffle"
                }
            });
            self._serverList = $("
").appendTo(this.element);

            // If any servers were defined in the options, add them now.
            if (self.options.servers !== null) {
                for (i=0, l=self.options.servers.length; i < l; i++) {
                    self._addServer(self.options.servers[i]);
                }
            }

            self._addButton.click(function() {
                self._addServer(self._inputBox.val());
            });
            self._sortButton.click(function() {
                self.sort();
            });
            return this;
        }
    });

    /**
     * This represents one of the individual items in the arcGisServerList
     */
    $.widget("ui.arcGisServerListItem", {
        options: {
            server: null
        },
        // _url: null,
        _protocol: "http://",
        _name: null,
        _instance: "ArcGIS",
        _list: null,
        _create: function() {
            var self = this, match, link, removeAction;

            removeAction = function() {
                self._trigger("remove", self, {"server": self.options.server});
                return false;
            };

            if (this.options.server === null) {
                throw new Error("Server name not provided.");
            }
            match = restEndpointRe.exec(this.options.server);
            // Check the server parameter to see if it is a full URL or just the server name.
            if (match) {
                this._protocol = match[1];
                this._name = match[2];
                this._instance = match[3];
            } else {
                this._name = this.options.server;
            }

            // Create the list of links.
            $(this.element).text(this._name);
            this._list = $("
").appendTo(this.element);

            // Add the manager link.
            link = $("ArcGIS Server Manager").attr({
                href: [this._protocol + this._name, this._instance, "Manager"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            // Add rest admin link.
            link = $("REST administration").attr({
                href: [this._protocol + this._name, this._instance, "rest", "admin"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            // Add the REST endpoint link.
            link = $("REST endpoint").attr({
                href: [this._protocol + this._name, this._instance, "rest"].join("/")
            });
            $("
").append(link).appendTo(this._list);

            link = $("Remove this server from the list").click(removeAction);
            $("
").append(link).appendTo(this._list);
        }
    });

}(jQuery));<|endoftext|>


<commit_before>import React, { useState } from 'react'
import { connect } from 'react-redux'
import Paper from '@material-ui/core/Paper'
import TextField from '@material-ui/core/TextField'
import Button from '@material-ui/core/Button'
import { makeStyles } from '@material-ui/core/styles'
import { addTodo, removeTodo, updateTodo } from '../actions/todo'
import TodoItem from './TodoItem'

const useStyles = makeStyles(theme => ({
	container: {
		display: 'flex',
		flexWrap: 'wrap',
		borderBottom: '1px dashed #3f51b5',
		justifyContent: 'center'
	},
	button: {
		margin: theme.spacing(1),
		textTransform: 'none'
	},
	textField: {
		marginLeft: theme.spacing(1),
		marginRight: theme.spacing(1),
		width: 200
	},
	dense: {
		marginTop: 19
	},
	menu: {
		width: 200
	},
	paper: {
		desktop: {
			margin: 0,
			marginBottom: 20,
			width: 360,
			padding: 0,
			minHeight: 210
		},
		mobile: {
			margin: 0,
			marginBottom: 20,
			width: 360,
			padding: 0,
			minHeight: 220
		}
	}
}))

const Todo = ({ todos, addTodo, removeTodo, updateTodo, matches }) => {
	const classes = useStyles()
	const [text, changeText] = useState('')

	const handleAddTodo = e => {
		e.preventDefault()

		addTodo(text)
		changeText('')
	}

	const handleTextChange = e => {
		changeText(e.target.value)
	}

	return (
		<div className={`Todo ${matches ? 'Todo__desktop' : 'Todo__mobile'}`}>
			<Paper className={matches ? classes.paper.desktop : classes.paper.mobile}>
				<form className={classes.container} onSubmit={handleAddTodo}>
					<TextField
						id="standard-name"
						label="What must be done?"
						className={classes.textField}
						value={text}
						onChange={handleTextChange}
						margin="normal"
					/>
					<Button
						variant="outlined"
						onClick={handleAddTodo}
						color={'primary'}
						className={classes.button}
					>
						Add
					</Button>
				</form>

					{todos.map((todo, i) => (
						<TodoItem
							key={i}
							todo={todo}
							remove={removeTodo}
							update={updateTodo}
						/>
					))}

			</Paper>

	)
}

export default connect(
	({ todos }) => ({ todos }),
	{ addTodo, removeTodo, updateTodo }
)(Todo)
<commit_msg>add icon instead<commit_after>import React, { useState } from 'react'
import { connect } from 'react-redux'
import Paper from '@material-ui/core/Paper'
import TextField from '@material-ui/core/TextField'
import Button from '@material-ui/core/Button'
import { makeStyles } from '@material-ui/core/styles'
import AddIcon from '@material-ui/icons/Add';
import { addTodo, removeTodo, updateTodo } from '../actions/todo'
import TodoItem from './TodoItem'

const useStyles = makeStyles(theme => ({
	container: {
		display: 'flex',
		flexWrap: 'wrap',
		borderBottom: '1px dashed #3f51b5',
		justifyContent: 'center'
	},
	button: {
		margin: theme.spacing(1),
		textTransform: 'none'
	},
	textField: {
		marginLeft: theme.spacing(1),
		marginRight: theme.spacing(1),
		width: 200
	},
	dense: {
		marginTop: 19
	},
	menu: {
		width: 200
	},
	paper: {
		desktop: {
			margin: 0,
			marginBottom: 20,
			width: 360,
			padding: 0,
			minHeight: 210
		},
		mobile: {
			margin: 0,
			marginBottom: 20,
			width: 360,
			padding: 0,
			minHeight: 220
		}
	}
}))

const Todo = ({ todos, addTodo, removeTodo, updateTodo, matches }) => {
	const classes = useStyles()
	const [text, changeText] = useState('')

	const handleAddTodo = e => {
		e.preventDefault()

		addTodo(text)
		changeText('')
	}

	const handleTextChange = e => {
		changeText(e.target.value)
	}

	return (
		<div className={`Todo ${matches ? 'Todo__desktop' : 'Todo__mobile'}`}>
			<Paper className={matches ? classes.paper.desktop : classes.paper.mobile}>
				<form className={classes.container} onSubmit={handleAddTodo}>
					<TextField
						id="standard-name"
						label="What must be done?"
						className={classes.textField}
						value={text}
						onChange={handleTextChange}
						margin="normal"
					/>
					<Button
						variant="outlined"
						onClick={handleAddTodo}
						color={'primary'}
						className={classes.button}
					>
						<AddIcon />
					</Button>
				</form>

					{todos.map((todo, i) => (
						<TodoItem
							key={i}
							todo={todo}
							remove={removeTodo}
							update={updateTodo}
						/>
					))}

			</Paper>

	)
}

export default connect(
	({ todos }) => ({ todos }),
	{ addTodo, removeTodo, updateTodo }
)(Todo)
<|endoftext|>


<commit_before>var express = require('express'),
    invoiceSchema = require('shared/schemas/invoice'),
    validator = require('shared/schemas/validator'),
    _ = require('underscore');

var router = module.exports = express.Router();

router.get('/', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  Invoice.query(function(qb) {
    qb.where({
      environment_id: req.user.get('environment_id')
    });
    qb.orderBy('invoice_number');
  }).fetchAll({withRelated: ['customer']}).then(function(col) {
    res.send(col.map(function(one) { return one.decorate(); }));
  });
});

router.post('/', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  var data = _.omit(req.body, 'environment_id', 'invoice_number', 'id');
  var errorReport = validator.validate(data, invoiceSchema);
  if (errorReport.errors.length) return res.status(400).send({error: 'Validation faled', details: errorReport});
  req.getEnvironmentSettings().then(function(sett) {
    return Invoice.create(_.extend(data, {environment_id: req.user.get('environment_id')}), {
      invoice_number: sett.get('next_invoice_number') || 1000
    }).then(function(inv) {
      return inv.fetch({withRelated: ['customer']}).then(function(inv) {
        res.status(201).send(inv.decorate());
      });
    });
  }).catch(next);
});

router.get('/:id', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  Invoice.where('id', req.params.id).fetch({withRelated: ['customer']}).then(function(inv) {
    if (!inv) return next();
    res.send(inv.decorate());
  }).catch(next);
});
/*
router.put('/:id', function(req, res, next) {
  var User = req.app.get('bookshelf').models.User;
  User.where({id: req.params.id, environment_id: req.user.get('environment_id')}).fetch().then(function(user) {
    if (!user) return res.status(404).send({error: 'Not found'});
    var data = _.omit(req.body, 'environment_id', 'id', 'environment', 'password_hash', 'environment');
    var errorReport = validator.validate(data, userSchema);
    if (errorReport.errors.length) return res.status(400).send({error: 'Validation failed', details: errorReport});
    return user.save(data).then(function(user) {
      return res.send(user.decorate());
    });
  }).catch(next);
});*/
<commit_msg>Route for saving invoices<commit_after>'use strict';
var express = require('express'),
    invoiceSchema = require('shared/schemas/invoice'),
    validator = require('shared/schemas/validator'),
    _ = require('underscore');

var router = module.exports = express.Router();

router.get('/', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  Invoice.query(function(qb) {
    qb.where({
      environment_id: req.user.get('environment_id')
    });
    qb.orderBy('invoice_number');
  }).fetchAll({withRelated: ['customer']}).then(function(col) {
    res.send(col.map(function(one) { return one.decorate(); }));
  });
});

router.post('/', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  var data = _.omit(req.body, 'environment_id', 'invoice_number', 'id');
  var errorReport = validator.validate(data, invoiceSchema);
  if (errorReport.errors.length) return res.status(400).send({error: 'Validation faled', details: errorReport});
  req.getEnvironmentSettings().then(function(sett) {
    return Invoice.create(_.extend(data, {environment_id: req.user.get('environment_id')}), {
      invoice_number: sett.get('next_invoice_number') || 1000
    }).then(function(inv) {
      return inv.fetch({withRelated: ['customer']}).then(function(inv) {
        res.status(201).send(inv.decorate());
      });
    });
  }).catch(next);
});

router.get('/:id', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  Invoice.where({
    id: req.params.id,
    environment_id: req.user.get('environment_id')
  }).fetch({withRelated: ['customer']}).then(function(inv) {
    if (!inv) return next();
    res.send(inv.decorate());
  }).catch(next);
});

router.put('/:id', function(req, res, next) {
  var Invoice = req.app.get('bookshelf').models.Invoice;
  Invoice.where({
    id: req.params.id,
    environment_id: req.user.get('environment_id')
  }).fetch({withRelated: ['customer']}).then(function(inv) {
    if (!inv) return next();
    var data = _.omit(req.body, 'environment_id', 'invoice_number', 'id', 'customer');
    var errorReport = validator.validate(data, invoiceSchema);
    if (errorReport.errors.length) return res.status(400).send({error: 'Validation faled', details: errorReport});
    return inv.save(data).then(function(inv) {
      res.send(inv.decorate());
    });
  }).catch(next);
});
<|endoftext|>