FormView.extend = function (obj) {
    var child = function () {
       FormView.apply(this, arguments);
    };

    extend(child.prototype, FormView.prototype);
    extend(child.prototype, obj);

    return child;
};
示例#2
0
文件: flowplayer.js 项目: cpm828/demo
         load: function(video, callback) {

            if (api.error || api.loading) return;
            api.video = {};

            api.finished = false;

            video = video || conf.clip;

            // resolve URL
            video = extend({}, urlResolver.resolve(video, conf.clip.sources));
            if (api.playing || api.engine) video.autoplay = true;
            var engineImpl = selectEngine(video);
            if (!engineImpl) return setTimeout(function() { api.trigger("error", [api, { code: flowplayer.support.flashVideo ? 5 : 10 }]); }) && api;
            if (!engineImpl.engineName) throw new Error('engineName property of factory should be exposed');
            if (!api.engine || engineImpl.engineName !== api.engine.engineName) {
              api.ready = false;
              if (api.engine) {
                api.engine.unload();
                api.conf.autoplay = true;
              }
              engine = api.engine = engineImpl(api, root);
              api.one('ready', function() {
                setTimeout(function() {
                  if (api.muted) api.mute(true, true);
                  else engine.volume(api.volumeLevel);
                });
              });
            }

            extend(video, engine.pick(video.sources.filter(function(source) { // Filter out sources explicitly configured for some other engine
              if (!source.engine) return true;
              return source.engine === engine.engineName;
            })));

            if (video.src) {
               var e = api.trigger('load', [api, video, engine], true);
               if (!e.defaultPrevented) {
                  api.ready = false;
                  engine.load(video);

                  // callback
                  if (isFunction(video)) callback = video;
                  if (callback) api.one("ready", callback);
               } else {
                  api.loading = false;
               }
            }

            return api;
         },
示例#3
0
exports.request = function (method, url, params) {
    var options = extend({
        url: url,
        method: method,
        json: true,
        jar: request.jar()
    }, params || {} );
    var deferred = Q.defer();
    var reqObj = defaultedRequestObj || request;
    reqObj(options, function (error, response, body) {
/**
Chakram Response Object
@desc Encapsulates the results of a HTTP call into a single object
@typedef {Object} ChakramResponse
@property {Error} error - An error when applicable
@property {Object} response - An {@link http://nodejs.org/api/http.html#http_http_incomingmessage http.IncomingMessage} object
@property {String|Buffer|Object} body - The response body. Typically a JSON object unless the json option has been set to false, in which case will be either a String or Buffer
@property {Object} jar - A {@link https://github.com/goinstant/tough-cookie tough cookie} jar
@property {String} url - The request's original URL
 */
        deferred.resolve({
            error : error,
            response: response,
            body: body,
            jar: options.jar,
            url: url
        });
    });
    return deferred.promise;
};
        'root element handling': function (t) {
            var container = document.createElement('div');
            var viewRoot = document.createElement('div');
            container.appendChild(viewRoot);
            var view = new ViewClass(extend(requiredOptions || {}, {el: viewRoot}));

            // note it's possible this isn't the same element as passed in, it's ok to autorender
            // and replace the element you were passed.
            t.ok(view.el.nodeName, 'should have a `this.el` that is an element if passed into constructor');
            t.equal(view.el.parentNode, container, 'parent container should be the same');

            // these should still be true, post-`render()`
            t.doesNotThrow(function () {
                view.render();
            }, 'should not error when calling render');

            t.ok(view.el.nodeName, 'should have a `this.el` that is an element if passed into constructor');
            t.equal(view.el.parentNode, container, 'parent container should be the same');

            // calling remove should remove itself from the DOM
            view.remove();
            t.ok(!view.el.parentNode, 'after calling remove should no longer have a parent node');
            t.ok(container.children.length === 0, 'container should have no children');
            t.end();
        }
示例#5
0
module.exports = function(source) {
  var filename = loaderUtils.getRemainingRequest(this)
  var content = source;
  var map;
  var options = {};
  var result;

  this.cacheable && this.cacheable();

  // Process query and setup options/defaults/forced for Traceur
  extend(options, defaults, loaderUtils.parseQuery(this.query), {
    sourceMaps: true
  });
  Object.keys(options).forEach(function(key) {
    switch(options[key]) {
      case 'true':
        options[key] = true;
        break;
      case 'false':
        options[key] = false;
        break;
      case 'undefined':
        options[key] = undefined;
        break;
      case 'null':
        options[key] = null;
        break;
    }
  });

  // Handle Traceur runtime
  if(options.filename === traceur.RUNTIME_PATH) {
    return content;
  }
  if(options.runtime) {
    content = 'require("' + traceur.RUNTIME_PATH + '");' + content;
  }

  // Parse code through Traceur
  try {
    delete options.runtime;
    var compiler = new traceur.Compiler(options);
    result = compiler.compile(content);

    // Process source map (if available) and return result
    if(options.sourceMaps) {
      map = JSON.parse(compiler.getSourceMap());
      map.sourcesContent = [source];
      this.callback(null, result, map);
    }
    else {
      return result.js;
    }
  }
  catch(errors) {
    throw new Error(errors.join(os.EOL));
  }
};
示例#6
0
文件: admin.js 项目: afkm/frame
    constructor: function (attrs) {

        extend(this, attrs);

        Object.defineProperty(this, '_groups', {
            writable: true,
            enumerable: false
        });
    },
示例#7
0
 els().forEach(function(el) {
    var src = el.href;
    el.setAttribute('data-index', player.conf.playlist.length);
    var itm = resolver.resolve(src, player.conf.clip.sources);
    if ($) {
      extend(itm, $(el).data());
    }
    player.conf.playlist.push(itm);
 });
 return this.map(function (model) {
     if (model.serialize) {
         return model.serialize();
     } else {
         var out = {};
         extend(out, model);
         delete out.collection;
         return out;
     }
 });
 reset: function (models, options) {
     options || (options = {});
     for (var i = 0, length = this.models.length; i < length; i++) {
         this._removeReference(this.models[i], options);
     }
     options.previousModels = this.models;
     this._reset();
     models = this.add(models, extend({silent: true}, options));
     if (!options.silent) this.trigger('reset', this, options);
     return models;
 },
示例#10
0
  api.shareUrl = function(directEmbed) {
    if (directEmbed && c.embed && c.embed.iframe) return c.embed.iframe;
    if (typeof api.conf.share === 'string') return api.conf.share;
    var title = encodeURIComponent(api.video.title || (common.find('title')[0] || {}).innerHTML || 'Flowplayer video')
      , conf = encodeURIComponent(btoa(JSON.stringify(extend({}, api.conf, api.extensions)).replace(/[\u007F-\uFFFF]/g, function(chr) {
    return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4)
})))
      , redirect = encodeURIComponent(window.location.toString())
      , baseUrl = directEmbed ? 'https://flowplayer.com/e/' : 'https://flowplayer.com/s/';
    return baseUrl + '?t=' + title + '&c=' + conf + '&r=' + redirect;
  };
示例#11
0
 player.play = function(i) {
    if (i === undefined) return player.resume();
    if (typeof i === 'number' && !player.conf.playlist[i]) return player;
    else if (typeof i != 'number') return player.load.apply(null, arguments);
    var arg = extend({index: i}, player.conf.playlist[i]);
    if (i === player.video.index) return player.load(arg, function() { player.resume(); });
    player.off('resume.fromfirst'); // Don't start from beginning if clip explicitely chosen
    player.load(arg, function() {
      player.video.index = i;
    });
    return player;
 };
function Collection(models, options) {
    options || (options = {});
    if (options.model) this.model = options.model;
    if (options.comparator) this.comparator = options.comparator;
    if (options.parent) this.parent = options.parent;
    if (!this.mainIndex) {
        var idAttribute = this.model && this.model.prototype && this.model.prototype.idAttribute;
        this.mainIndex = idAttribute || 'id';
    }
    this._reset();
    this.initialize.apply(this, arguments);
    if (models) this.reset(models, extend({silent: true}, options));
}
示例#13
0
文件: index.js 项目: kentjs/router
	this.middleware.push(function*(upstream) {
		if(!prefix_regex.test(this.path))
			return yield* upstream

		var old_ctx = {
				  mount: this.mount
				, path: this.path
		    }
		  , new_ctx = {
		    	  mount: (this.mount || '') + prefix
				, path: this.path.replace(prefix_regex, '/')
		    }
		
		extend(this, new_ctx)

		yield* downstream.call(this, function*() {
			extend(this, old_ctx)
			yield* upstream
			extend(this, new_ctx)
		}.call(this))

		extend(this, old_ctx)
	})
示例#14
0
module.exports = function(file, opts) {
  var data = '',
  config = extend({
    compress: false,
    paths: []
  }, opts);

  if (!/\.css$|\.less$/.test(file)) {
    return through();
  }

  return through(write, end);

  function write(buf) {
    data += buf;
  }

  function end() {
    var self = this;
    var fileConfig = extend({}, config);

    // Injects the path of the current file.
    fileConfig.filename = file;

    less.render(data, fileConfig, function (err, output) {
      if (err) {
        self.emit('error', new Error(getErrorMessage(err), file, err.line));
      } else {
        self.queue(output.css);
      }
      self.queue(null);
    });
  }

  function getErrorMessage(err) {
    var msg = err.message;
    if (err.line) {
      msg += ", line " + err.line;
    }
    if (err.column) {
      msg += ", column " + err.column;
    }
    if (err.extract) {
      msg += ": \"" + err.extract + "\"";
    }

    return msg;
  }
};
        'handling values passed in instantiation': function (t) {
            var parent = {update: function () {}};
            var view = new ViewClass(extend(requiredOptions || {}, {
                value: validValue,
                parent: parent,
                name: 'awesome name'
            }));

            t.equal(view.value, validValue, 'should have maintained its value');
            t.strictEqual(view.valid, true, 'should be `valid` at init when passed valid value');
            t.equal(view.parent, parent, 'should have kept its `parent`');
            t.equal(view.name, 'awesome name', 'should have kepts its `name`');

            t.end();
        }
示例#16
0
/**
 * Configure text-files-loader
 * @param  {Object} options check defaultOptions
 * @return {[type]}         [description]
 */
function setup(options) {
  var key
  var config = extend({}, defaultOptions)

  for (key in options) {
    if (config.hasOwnProperty(key)) {
      config[key] = options[key]
    }
    else {
      throw new Error('Invalid option "' + key + '"')
    }
  }

  module.exports.config = config
}
示例#17
0
module.exports = function (phrase, inject, callback) {
    // Parse arguments
    if (typeof phrase === 'undefined') phrase = '';
    if (typeof inject === 'undefined') inject = null;
    if (typeof inject === 'function') callback = inject;
    if (typeof callback === 'undefined') callback = null;

    // Merge
    if (inject !== null) {
        afinn = extend(afinn, inject);
    }

    // Storage objects
    var tokens      = tokenize(phrase),
        score       = 0,
        words       = [],
        positive    = [],
        negative    = [];

    // Iterate over tokens
    var len = tokens.length;
    while (len--) { 
        var obj = tokens[len];
        var item = afinn[obj];
        if (typeof item === 'undefined') continue;

        words.push(obj);
        if (item > 0) positive.push(obj);
        if (item < 0) negative.push(obj);

        score += item;
    }

    // Handle optional async interface
    var result = {
        score:          score,
        comparative:    score / tokens.length,
        tokens:         tokens,
        words:          words,
        positive:       positive,
        negative:       negative
    };

    if (callback === null) return result;
    process.nextTick(function () {
        callback(null, result);
    });
};
    _prepareModel: function (attrs, options) {
        // if we haven't defined a constructor, skip this
        if (!this.model) return attrs;

        if (this.isModel(attrs)) {
            if (!attrs.collection) attrs.collection = this;
            return attrs;
        } else {
            options = options ? extend({}, options) : {};
            options.collection = this;
            var model = new this.model(attrs, options);
            if (!model.validationError) return model;
            this.trigger('invalid', this, model.validationError, options);
            return false;
        }
    },
示例#19
0
 _handleWorkQueueStep: function(resolve) {
   if (this._pathSamplingQueue.length > 0) {
     var item = this._pathSamplingQueue.pop();
     if (item.samplesLeft > 0) {
       var startIndex = (numSamples - item.samplesLeft);
       for (var i = startIndex; i < startIndex + batchSize; i++) {
         var point = item.element.getPointAtLength((i / numSamples) * item.totalLength);
         item.sampleArray[i * 2 + 0] = point.x;
         item.sampleArray[i * 2 + 1] = point.y;
       }
       this._pathSamplingQueue.push(extend(item, {samplesLeft: item.samplesLeft - batchSize}));
     }
   } else {
     resolve();
   }
 },
示例#20
0
  function end() {
    var self = this;
    var fileConfig = extend({}, config);

    // Injects the path of the current file.
    fileConfig.filename = file;

    less.render(data, fileConfig, function (err, output) {
      if (err) {
        self.emit('error', new Error(getErrorMessage(err), file, err.line));
      } else {
        self.queue(output.css);
      }
      self.queue(null);
    });
  }
        'basics': function (t) {
            var counter;
            var parent = {
                update: function (field) {
                    counter++;
                    this.passedField = field;
                }
            };
            var view = new ViewClass(extend(requiredOptions || {}, {parent: parent}));

            // helper we can call
            function ensureProperties(str) {
                str = str || '';
                t.ok(hasPropertyDefinition(view, 'value'), 'has `value` property' + str);
                t.equal(typeof view.name, 'string', 'has `name` property that is a string' + str);
                t.notEqual(view.name, '', '`name` property should not be empty string' + str);
                t.ok(isFunction(view.setValue), 'has `setValue` method' + str);
                t.equal(typeof view.valid, 'boolean', 'has `valid` property that is a boolean' + str);
                t.equal(parent, view.parent, 'has same `parent` property' + str);
            }

            t.ok(view, 'should init with `new`');
            ensureProperties();

            counter = 0;

            t.doesNotThrow(function () {
                view.setValue(validValue);
            }, 'should not error when setting valid value');

            t.notEqual(counter, 0, 'should have called `update` on parent when value changed');
            t.equal(parent.passedField, view, 'should have passed itself to the parent when changed');

            // all this should still be true after setting a value
            ensureProperties(' after setting a value');

            // all this should be true after calling `beforeSubmit`, if present.
            if (isFunction(view.beforeSubmit)) {
                view.beforeSubmit();
                ensureProperties(' after `beforeSubmit`');
            }

            t.end();
        },
示例#22
0
            window[callbackId] = function(type, arg) {
               var video = loadVideo;

               if (conf.debug) console.log("--", type, arg);

               var event = {
                 type: type
               };

               switch (type) {

                  // RTMP sends a lot of finish events in vain
                  // case "finish": if (conf.rtmp) return;
                  case "ready": arg = extend(video, arg); break;
                  case "click": event.flash = true; break;
                  case "keydown": event.which = arg; break;
                  case "seek": video.time = arg; break;
                  case "status":
                     player.trigger("progress", [player, arg.time]);

                     if (arg.buffer < video.bytes && !video.buffered) {
                        video.buffer = arg.buffer / video.bytes * video.duration;
                        player.trigger("buffer", video.buffer);
                     } else if (!video.buffered) {
                        video.buffered = true;
                        player.trigger("buffered");
                     }

                     break;
               }
               if (type === 'click' || type === 'keydown') {
                 event.target = root;
                 bean.fire(root, type, [event]);
               }
               else if (type != 'buffered' && type !== 'unload') {
                  // add some delay so that player is truly ready after an event
                  setTimeout(function() { player.trigger(event, [player, arg]); }, 1);
               } else if (type === 'unload') {
                 player.trigger(event, [player, arg]);
               }

            };
示例#23
0
文件: builder.js 项目: Retsly/builder
function Builder (opts) {
  opts = opts || {}

  // we delete from opts for stylus' sake
  this.root = opts.root || process.cwd()
  this.out = opts.out || process.cwd() + '/build'
  delete opts.root
  delete opts.out

  // defaults
  this.opts = extend({
    install: true,
    require: true,
    runtime: true,
    string: false,
    linenos: dev,
    destination: this.out,
    path: 'app',
  }, opts)
}
示例#24
0
  var videoTagConfig = function(videoTag) {
    var clip = videoTag.data() || {}, conf = {};
    $.each(['autoplay', 'loop', 'preload', 'poster'], function(i, key) {
      var val = videoTag.attr(key);
      if (val !== undefined && ['autoplay', 'poster'].indexOf(key) !== -1) conf[key] = val ? val : true;
      else if (val !== undefined) clip[key] = val ? val : true;
    });
    clip.subtitles = videoTag.find('track').map(function() {
      var tr = $(this);
      return {
        src: tr.attr('src'),
        kind: tr.attr('kind'),
        label: tr.attr('label'),
        srclang: tr.attr('srclang'),
        'default': tr.prop('default')
      };
    }).get();

    clip.sources = (new URLResolver()).sourcesFromVideoTag(videoTag, $);
    return extend(conf, {clip: clip});
  };
示例#25
0
exports.request = function (method, url, params) {
    var options = extend({
        url: url,
        method: method,
        json: true
    }, params || {} );

    // options.jar is either a jar object or true
    // In case user passes {jar: true} option to request, we need reference to global jar to do assertions.
    options.jar = options.jar || defaultsJar;
    if (options.jar === true) {
        options.jar = globalRequestJar;
    } else if (options.jar === undefined) {
        // Create new jar for this request for backwards compatibility
        options.jar = request.jar();
    }

    var deferred = Q.defer();
    var reqObj = defaultedRequestObj || request;
    reqObj(options, function (error, response, body) {
/**
Chakram Response Object
@desc Encapsulates the results of a HTTP call into a single object
@typedef {Object} ChakramResponse
@property {Error} error - An error when applicable
@property {Object} response - An {@link http://nodejs.org/api/http.html#http_http_incomingmessage http.IncomingMessage} object
@property {String|Buffer|Object} body - The response body. Typically a JSON object unless the json option has been set to false, in which case will be either a String or Buffer
@property {Object} jar - A {@link https://github.com/goinstant/tough-cookie tough cookie} jar
@property {String} url - The request's original URL
 */
        deferred.resolve({
            error : error,
            response: response,
            body: body,
            jar: options.jar,
            url: url
        });
    });
    return deferred.promise;
};
示例#26
0
router.get('/',function(req,res){

  console.log(req.query);

  var params = {
     'nonce':1,
     'from':'yintai',
     'ts':parseInt(new Date().getTime()/1000),
     'data':JSON.stringify(req.query)
  };

  extend(params,{
    'sign':sign(params)
  });

  console.log(params);

  // 请求参数
  var args = {
    headers:{'Content-type':'application/x-www-form-urlencoded'},
    data: param(params)
  };

  //请求地址
  var path = util.format('%s%s',remote,url.parse(req.originalUrl).pathname.replace('/proxy/',''));

  console.log('请求地址:'+path);
  console.log(args);

  // 请求远程
  client.post(path,args,function(data,response){
    res.send(data);
  }).on('error',function(err){
    res.send(err);
  });
});
示例#27
0
var AnswersStore = extend({}, EventEmitter.prototype, {

	getAnswers: function() {
		return _answers;
	},

	setAnswers: function(answers){
		_answers = answers;
	},

	setQuestionType: function(qType){
		_questionType = qType;
	},

	resetSelected: function() {
		_answers.forEach(function(item){
			item.selected = false;
		});
	},

	getAnswersCount: function() {
		return _answers.length;
	},

	getAnswerIndex: function(uuid){
		var ans = getAnswerWithIndex(uuid);
		return ans ? ans.index : null;
	},

	getConditions: function(uuid) {
		var ans = _answers.find(function(item){
			return item.uuid == uuid;
		});
		if (ans)
			return ans.conditions || [];
		return [];
	},

	getConditionsText: function(uuid) {
		var ans = _answers.find(function(item){
			return item.uuid == uuid;
		});
		if (ans)
			return ans.conditionsText || [];
		return [];
	},

	getConformities: function(uuid) {
		var ans = _answers.find(function(item){
			return item.uuid == uuid;
		});
		if (ans)
			return ans.conformities || [];
		return [];
	},

	getAnswerImg: function(uuid) {
		var ans = _answers.find(function(item){
			return item.uuid == uuid;
		});
		if (ans)
			return ans.img;
	},

	emitChange: function() {
		this.emit('change');
	},

	addChangeListener: function(callBack) {
		this.on('change', callBack);
	},

	removeChangeListener: function(callBack) {
		this.removeListener('change', callBack);
	}
});
    set: function (models, options) {
        options = extend({add: true, remove: true, merge: true}, options);
        if (options.parse) models = this.parse(models, options);
        var singular = !isArray(models);
        models = singular ? (models ? [models] : []) : models.slice();
        var id, model, attrs, existing, sort, i, length;
        var at = options.at;
        var sortable = this.comparator && (at == null) && options.sort !== false;
        var sortAttr = ('string' === typeof this.comparator) ? this.comparator : null;
        var toAdd = [], toRemove = [], modelMap = {};
        var add = options.add, merge = options.merge, remove = options.remove;
        var order = !sortable && add && remove ? [] : false;
        var targetProto = this.model && this.model.prototype || Object.prototype;

        // Turn bare objects into model references, and prevent invalid models
        // from being added.
        for (i = 0, length = models.length; i < length; i++) {
            attrs = models[i] || {};
            if (this.isModel(attrs)) {
                id = model = attrs;
            } else if (targetProto.generateId) {
                id = targetProto.generateId(attrs);
            } else {
                id = attrs[this.mainIndex];
            }

            // If a duplicate is found, prevent it from being added and
            // optionally merge it into the existing model.
            if (existing = this.get(id)) {
                if (remove) modelMap[existing.cid || existing[this.mainIndex]] = true;
                if (merge) {
                    attrs = attrs === model ? model.attributes : attrs;
                    if (options.parse) attrs = existing.parse(attrs, options);
                    // if this is model
                    if (existing.set) {
                        existing.set(attrs, options);
                        if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
                    } else {
                        // if not just update the properties
                        extend(existing, attrs);
                    }
                }
                models[i] = existing;

            // If this is a new, valid model, push it to the `toAdd` list.
            } else if (add) {
                model = models[i] = this._prepareModel(attrs, options);
                if (!model) continue;
                toAdd.push(model);
                this._addReference(model, options);
            }

            // Do not add multiple models with the same `id`.
            model = existing || model;
            if (!model) continue;
            if (order && ((model.isNew && model.isNew() || !model[this.mainIndex]) || !modelMap[model.cid || model[this.mainIndex]])) order.push(model);
            modelMap[model[this.mainIndex]] = true;
        }

        // Remove nonexistent models if appropriate.
        if (remove) {
            for (i = 0, length = this.length; i < length; i++) {
                model = this.models[i];
                if (!modelMap[model.cid || model[this.mainIndex]]) toRemove.push(model);
            }
            if (toRemove.length) this.remove(toRemove, options);
        }

        // See if sorting is needed, update `length` and splice in new models.
        if (toAdd.length || (order && order.length)) {
            if (sortable) sort = true;
            if (at != null) {
                for (i = 0, length = toAdd.length; i < length; i++) {
                    this.models.splice(at + i, 0, toAdd[i]);
                }
            } else {
                var orderedModels = order || toAdd;
                for (i = 0, length = orderedModels.length; i < length; i++) {
                    this.models.push(orderedModels[i]);
                }
            }
        }

        // Silently sort the collection if appropriate.
        if (sort) this.sort({silent: true});

        // Unless silenced, it's time to fire all appropriate add/sort events.
        if (!options.silent) {
            for (i = 0, length = toAdd.length; i < length; i++) {
                model = toAdd[i];
                if (model.trigger) {
                    model.trigger('add', model, this, options);
                } else {
                    this.trigger('add', model, this, options);
                }
            }
            if (sort || (order && order.length)) this.trigger('sort', this, options);
        }

        // Return the added (or merged) model (or models).
        return singular ? models[0] : models;
    },
 add: function (models, options) {
     return this.set(models, extend({merge: false, add: true, remove: false}, options));
 },
extend(Collection.prototype, BackboneEvents, {
    initialize: function () {},

    isModel: function (model) {
        return this.model && model instanceof this.model;
    },

    add: function (models, options) {
        return this.set(models, extend({merge: false, add: true, remove: false}, options));
    },

    // overridable parse method
    parse: function (res, options) {
        return res;
    },

    // overridable serialize method
    serialize: function () {
        return this.map(function (model) {
            if (model.serialize) {
                return model.serialize();
            } else {
                var out = {};
                extend(out, model);
                delete out.collection;
                return out;
            }
        });
    },

    toJSON: function () {
        return this.serialize();
    },

    set: function (models, options) {
        options = extend({add: true, remove: true, merge: true}, options);
        if (options.parse) models = this.parse(models, options);
        var singular = !isArray(models);
        models = singular ? (models ? [models] : []) : models.slice();
        var id, model, attrs, existing, sort, i, length;
        var at = options.at;
        var sortable = this.comparator && (at == null) && options.sort !== false;
        var sortAttr = ('string' === typeof this.comparator) ? this.comparator : null;
        var toAdd = [], toRemove = [], modelMap = {};
        var add = options.add, merge = options.merge, remove = options.remove;
        var order = !sortable && add && remove ? [] : false;
        var targetProto = this.model && this.model.prototype || Object.prototype;

        // Turn bare objects into model references, and prevent invalid models
        // from being added.
        for (i = 0, length = models.length; i < length; i++) {
            attrs = models[i] || {};
            if (this.isModel(attrs)) {
                id = model = attrs;
            } else if (targetProto.generateId) {
                id = targetProto.generateId(attrs);
            } else {
                id = attrs[this.mainIndex];
            }

            // If a duplicate is found, prevent it from being added and
            // optionally merge it into the existing model.
            if (existing = this.get(id)) {
                if (remove) modelMap[existing.cid || existing[this.mainIndex]] = true;
                if (merge) {
                    attrs = attrs === model ? model.attributes : attrs;
                    if (options.parse) attrs = existing.parse(attrs, options);
                    // if this is model
                    if (existing.set) {
                        existing.set(attrs, options);
                        if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
                    } else {
                        // if not just update the properties
                        extend(existing, attrs);
                    }
                }
                models[i] = existing;

            // If this is a new, valid model, push it to the `toAdd` list.
            } else if (add) {
                model = models[i] = this._prepareModel(attrs, options);
                if (!model) continue;
                toAdd.push(model);
                this._addReference(model, options);
            }

            // Do not add multiple models with the same `id`.
            model = existing || model;
            if (!model) continue;
            if (order && ((model.isNew && model.isNew() || !model[this.mainIndex]) || !modelMap[model.cid || model[this.mainIndex]])) order.push(model);
            modelMap[model[this.mainIndex]] = true;
        }

        // Remove nonexistent models if appropriate.
        if (remove) {
            for (i = 0, length = this.length; i < length; i++) {
                model = this.models[i];
                if (!modelMap[model.cid || model[this.mainIndex]]) toRemove.push(model);
            }
            if (toRemove.length) this.remove(toRemove, options);
        }

        // See if sorting is needed, update `length` and splice in new models.
        if (toAdd.length || (order && order.length)) {
            if (sortable) sort = true;
            if (at != null) {
                for (i = 0, length = toAdd.length; i < length; i++) {
                    this.models.splice(at + i, 0, toAdd[i]);
                }
            } else {
                var orderedModels = order || toAdd;
                for (i = 0, length = orderedModels.length; i < length; i++) {
                    this.models.push(orderedModels[i]);
                }
            }
        }

        // Silently sort the collection if appropriate.
        if (sort) this.sort({silent: true});

        // Unless silenced, it's time to fire all appropriate add/sort events.
        if (!options.silent) {
            for (i = 0, length = toAdd.length; i < length; i++) {
                model = toAdd[i];
                if (model.trigger) {
                    model.trigger('add', model, this, options);
                } else {
                    this.trigger('add', model, this, options);
                }
            }
            if (sort || (order && order.length)) this.trigger('sort', this, options);
        }

        // Return the added (or merged) model (or models).
        return singular ? models[0] : models;
    },

    get: function (query, indexName) {
        if (query == null) return;
        var index = this._indexes[indexName || this.mainIndex];
        return (index && (index[query] || index[query[this.mainIndex]])) || this._indexes.cid[query] || this._indexes.cid[query.cid];
    },

    // Get the model at the given index.
    at: function (index) {
        return this.models[index];
    },

    remove: function (models, options) {
        var singular = !isArray(models);
        var i, length, model, index;

        models = singular ? [models] : slice.call(models);
        options || (options = {});
        for (i = 0, length = models.length; i < length; i++) {
            model = models[i] = this.get(models[i]);
            if (!model) continue;
            this._deIndex(model);
            index = this.models.indexOf(model);
            this.models.splice(index, 1);
            if (!options.silent) {
                options.index = index;
                if (model.trigger) {
                    model.trigger('remove', model, this, options);
                } else {
                    this.trigger('remove', model, this, options);
                }
            }
            this._removeReference(model, options);
        }
        return singular ? models[0] : models;
    },

    // When you have more items than you want to add or remove individually,
    // you can reset the entire set with a new list of models, without firing
    // any granular `add` or `remove` events. Fires `reset` when finished.
    // Useful for bulk operations and optimizations.
    reset: function (models, options) {
        options || (options = {});
        for (var i = 0, length = this.models.length; i < length; i++) {
            this._removeReference(this.models[i], options);
        }
        options.previousModels = this.models;
        this._reset();
        models = this.add(models, extend({silent: true}, options));
        if (!options.silent) this.trigger('reset', this, options);
        return models;
    },

    sort: function (options) {
        var self = this;
        if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
        options || (options = {});

        if (typeof this.comparator === 'string') {
            this.models.sort(function (left, right) {
                if (left.get) {
                    left = left.get(self.comparator);
                    right = right.get(self.comparator);
                } else {
                    left = left[self.comparator];
                    right = right[self.comparator];
                }
                if (left > right || left === void 0) return 1;
                if (left < right || right === void 0) return -1;
                return 0;
            });
        } else if (this.comparator.length === 1) {
            this.models.sort(function (left, right) {
                left = self.comparator(left);
                right = self.comparator(right);
                if (left > right || left === void 0) return 1;
                if (left < right || right === void 0) return -1;
                return 0;
            });
        } else {
            this.models.sort(bind(this.comparator,this));
        }

        if (!options.silent) this.trigger('sort', this, options);
        return this;
    },

    // Private method to reset all internal state. Called when the collection
    // is first initialized or reset.
    _reset: function () {
        var list = slice.call(this.indexes || []);
        var i = 0;
        list.push(this.mainIndex);
        list.push('cid');
        var l = list.length;
        this.models = [];
        this._indexes = {};
        for (; i < l; i++) {
            this._indexes[list[i]] = {};
        }
    },

    _prepareModel: function (attrs, options) {
        // if we haven't defined a constructor, skip this
        if (!this.model) return attrs;

        if (this.isModel(attrs)) {
            if (!attrs.collection) attrs.collection = this;
            return attrs;
        } else {
            options = options ? extend({}, options) : {};
            options.collection = this;
            var model = new this.model(attrs, options);
            if (!model.validationError) return model;
            this.trigger('invalid', this, model.validationError, options);
            return false;
        }
    },

    _deIndex: function (model) {
        for (var name in this._indexes) {
            delete this._indexes[name][model.hasOwnProperty(name) ? model[name] : (model.get && model.get(name))];
        }
    },

    _index: function (model) {
        for (var name in this._indexes) {
            var indexVal = model.hasOwnProperty(name) ? model[name] : (model.get && model.get(name));
            if (indexVal != null) this._indexes[name][indexVal] = model;
        }
    },

    // Internal method to create a model's ties to a collection.
    _addReference: function (model, options) {
        this._index(model);
        if (!model.collection) model.collection = this;
        if (model.on) model.on('all', this._onModelEvent, this);
    },

        // Internal method to sever a model's ties to a collection.
    _removeReference: function (model, options) {
        if (this === model.collection) delete model.collection;
        this._deIndex(model);
        if (model.off) model.off('all', this._onModelEvent, this);
    },

    _onModelEvent: function (event, model, collection, options) {
        if ((event === 'add' || event === 'remove') && collection !== this) return;
        if (event === 'destroy') this.remove(model, options);
        if (model && event === 'change:' + this.mainIndex) {
            this._deIndex(model);
            this._index(model);
        }
        this.trigger.apply(this, arguments);
    }
});