Skip to content

Instantly share code, notes, and snippets.

@justinvdm
Created October 8, 2013 12:18
Show Gist options
  • Save justinvdm/6883795 to your computer and use it in GitHub Desktop.
Save justinvdm/6883795 to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
diff --git a/go/base/static/js/vendor/backbone-1.0.0.js b/go/base/static/js/vendor/backbone-1.0.0.js
deleted file mode 100644
index 3512d42..0000000
--- a/go/base/static/js/vendor/backbone-1.0.0.js
+++ /dev/null
@@ -1,1571 +0,0 @@
-// Backbone.js 1.0.0
-
-// (c) 2010-2013 Jeremy Ashkenas, DocumentCloud Inc.
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://backbonejs.org
-
-(function(){
-
- // Initial Setup
- // -------------
-
- // Save a reference to the global object (`window` in the browser, `exports`
- // on the server).
- var root = this;
-
- // Save the previous value of the `Backbone` variable, so that it can be
- // restored later on, if `noConflict` is used.
- var previousBackbone = root.Backbone;
-
- // Create local references to array methods we'll want to use later.
- var array = [];
- var push = array.push;
- var slice = array.slice;
- var splice = array.splice;
-
- // The top-level namespace. All public Backbone classes and modules will
- // be attached to this. Exported for both the browser and the server.
- var Backbone;
- if (typeof exports !== 'undefined') {
- Backbone = exports;
- } else {
- Backbone = root.Backbone = {};
- }
-
- // Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '1.0.0';
-
- // Require Underscore, if we're on the server, and it's not already present.
- var _ = root._;
- if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
-
- // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
- // the `$` variable.
- Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$;
-
- // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
- // to its previous owner. Returns a reference to this Backbone object.
- Backbone.noConflict = function() {
- root.Backbone = previousBackbone;
- return this;
- };
-
- // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
- // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
- // set a `X-Http-Method-Override` header.
- Backbone.emulateHTTP = false;
-
- // Turn on `emulateJSON` to support legacy servers that can't deal with direct
- // `application/json` requests ... will encode the body as
- // `application/x-www-form-urlencoded` instead and will send the model in a
- // form param named `model`.
- Backbone.emulateJSON = false;
-
- // Backbone.Events
- // ---------------
-
- // A module that can be mixed in to *any object* in order to provide it with
- // custom events. You may bind with `on` or remove with `off` callback
- // functions to an event; `trigger`-ing an event fires all callbacks in
- // succession.
- //
- // var object = {};
- // _.extend(object, Backbone.Events);
- // object.on('expand', function(){ alert('expanded'); });
- // object.trigger('expand');
- //
- var Events = Backbone.Events = {
-
- // Bind an event to a `callback` function. Passing `"all"` will bind
- // the callback to all events fired.
- on: function(name, callback, context) {
- if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
- this._events || (this._events = {});
- var events = this._events[name] || (this._events[name] = []);
- events.push({callback: callback, context: context, ctx: context || this});
- return this;
- },
-
- // Bind an event to only be triggered a single time. After the first time
- // the callback is invoked, it will be removed.
- once: function(name, callback, context) {
- if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
- var self = this;
- var once = _.once(function() {
- self.off(name, once);
- callback.apply(this, arguments);
- });
- once._callback = callback;
- return this.on(name, once, context);
- },
-
- // Remove one or many callbacks. If `context` is null, removes all
- // callbacks with that function. If `callback` is null, removes all
- // callbacks for the event. If `name` is null, removes all bound
- // callbacks for all events.
- off: function(name, callback, context) {
- var retain, ev, events, names, i, l, j, k;
- if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
- if (!name && !callback && !context) {
- this._events = {};
- return this;
- }
-
- names = name ? [name] : _.keys(this._events);
- for (i = 0, l = names.length; i < l; i++) {
- name = names[i];
- if (events = this._events[name]) {
- this._events[name] = retain = [];
- if (callback || context) {
- for (j = 0, k = events.length; j < k; j++) {
- ev = events[j];
- if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
- (context && context !== ev.context)) {
- retain.push(ev);
- }
- }
- }
- if (!retain.length) delete this._events[name];
- }
- }
-
- return this;
- },
-
- // Trigger one or many events, firing all bound callbacks. Callbacks are
- // passed the same arguments as `trigger` is, apart from the event name
- // (unless you're listening on `"all"`, which will cause your callback to
- // receive the true name of the event as the first argument).
- trigger: function(name) {
- if (!this._events) return this;
- var args = slice.call(arguments, 1);
- if (!eventsApi(this, 'trigger', name, args)) return this;
- var events = this._events[name];
- var allEvents = this._events.all;
- if (events) triggerEvents(events, args);
- if (allEvents) triggerEvents(allEvents, arguments);
- return this;
- },
-
- // Tell this object to stop listening to either specific events ... or
- // to every object it's currently listening to.
- stopListening: function(obj, name, callback) {
- var listeners = this._listeners;
- if (!listeners) return this;
- var deleteListener = !name && !callback;
- if (typeof name === 'object') callback = this;
- if (obj) (listeners = {})[obj._listenerId] = obj;
- for (var id in listeners) {
- listeners[id].off(name, callback, this);
- if (deleteListener) delete this._listeners[id];
- }
- return this;
- }
-
- };
-
- // Regular expression used to split event strings.
- var eventSplitter = /\s+/;
-
- // Implement fancy features of the Events API such as multiple event
- // names `"change blur"` and jQuery-style event maps `{change: action}`
- // in terms of the existing API.
- var eventsApi = function(obj, action, name, rest) {
- if (!name) return true;
-
- // Handle event maps.
- if (typeof name === 'object') {
- for (var key in name) {
- obj[action].apply(obj, [key, name[key]].concat(rest));
- }
- return false;
- }
-
- // Handle space separated event names.
- if (eventSplitter.test(name)) {
- var names = name.split(eventSplitter);
- for (var i = 0, l = names.length; i < l; i++) {
- obj[action].apply(obj, [names[i]].concat(rest));
- }
- return false;
- }
-
- return true;
- };
-
- // A difficult-to-believe, but optimized internal dispatch function for
- // triggering events. Tries to keep the usual cases speedy (most internal
- // Backbone events have 3 arguments).
- var triggerEvents = function(events, args) {
- var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
- switch (args.length) {
- case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
- case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
- case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
- case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
- default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
- }
- };
-
- var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
-
- // Inversion-of-control versions of `on` and `once`. Tell *this* object to
- // listen to an event in another object ... keeping track of what it's
- // listening to.
- _.each(listenMethods, function(implementation, method) {
- Events[method] = function(obj, name, callback) {
- var listeners = this._listeners || (this._listeners = {});
- var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
- listeners[id] = obj;
- if (typeof name === 'object') callback = this;
- obj[implementation](name, callback, this);
- return this;
- };
- });
-
- // Aliases for backwards compatibility.
- Events.bind = Events.on;
- Events.unbind = Events.off;
-
- // Allow the `Backbone` object to serve as a global event bus, for folks who
- // want global "pubsub" in a convenient place.
- _.extend(Backbone, Events);
-
- // Backbone.Model
- // --------------
-
- // Backbone **Models** are the basic data object in the framework --
- // frequently representing a row in a table in a database on your server.
- // A discrete chunk of data and a bunch of useful, related methods for
- // performing computations and transformations on that data.
-
- // Create a new model with the specified attributes. A client id (`cid`)
- // is automatically generated and assigned for you.
- var Model = Backbone.Model = function(attributes, options) {
- var defaults;
- var attrs = attributes || {};
- options || (options = {});
- this.cid = _.uniqueId('c');
- this.attributes = {};
- _.extend(this, _.pick(options, modelOptions));
- if (options.parse) attrs = this.parse(attrs, options) || {};
- if (defaults = _.result(this, 'defaults')) {
- attrs = _.defaults({}, attrs, defaults);
- }
- this.set(attrs, options);
- this.changed = {};
- this.initialize.apply(this, arguments);
- };
-
- // A list of options to be attached directly to the model, if provided.
- var modelOptions = ['url', 'urlRoot', 'collection'];
-
- // Attach all inheritable methods to the Model prototype.
- _.extend(Model.prototype, Events, {
-
- // A hash of attributes whose current and previous value differ.
- changed: null,
-
- // The value returned during the last failed validation.
- validationError: null,
-
- // The default name for the JSON `id` attribute is `"id"`. MongoDB and
- // CouchDB users may want to set this to `"_id"`.
- idAttribute: 'id',
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
-
- // Return a copy of the model's `attributes` object.
- toJSON: function(options) {
- return _.clone(this.attributes);
- },
-
- // Proxy `Backbone.sync` by default -- but override this if you need
- // custom syncing semantics for *this* particular model.
- sync: function() {
- return Backbone.sync.apply(this, arguments);
- },
-
- // Get the value of an attribute.
- get: function(attr) {
- return this.attributes[attr];
- },
-
- // Get the HTML-escaped value of an attribute.
- escape: function(attr) {
- return _.escape(this.get(attr));
- },
-
- // Returns `true` if the attribute contains a value that is not null
- // or undefined.
- has: function(attr) {
- return this.get(attr) != null;
- },
-
- // Set a hash of model attributes on the object, firing `"change"`. This is
- // the core primitive operation of a model, updating the data and notifying
- // anyone who needs to know about the change in state. The heart of the beast.
- set: function(key, val, options) {
- var attr, attrs, unset, changes, silent, changing, prev, current;
- if (key == null) return this;
-
- // Handle both `"key", value` and `{key: value}` -style arguments.
- if (typeof key === 'object') {
- attrs = key;
- options = val;
- } else {
- (attrs = {})[key] = val;
- }
-
- options || (options = {});
-
- // Run validation.
- if (!this._validate(attrs, options)) return false;
-
- // Extract attributes and options.
- unset = options.unset;
- silent = options.silent;
- changes = [];
- changing = this._changing;
- this._changing = true;
-
- if (!changing) {
- this._previousAttributes = _.clone(this.attributes);
- this.changed = {};
- }
- current = this.attributes, prev = this._previousAttributes;
-
- // Check for changes of `id`.
- if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
-
- // For each `set` attribute, update or delete the current value.
- for (attr in attrs) {
- val = attrs[attr];
- if (!_.isEqual(current[attr], val)) changes.push(attr);
- if (!_.isEqual(prev[attr], val)) {
- this.changed[attr] = val;
- } else {
- delete this.changed[attr];
- }
- unset ? delete current[attr] : current[attr] = val;
- }
-
- // Trigger all relevant attribute changes.
- if (!silent) {
- if (changes.length) this._pending = true;
- for (var i = 0, l = changes.length; i < l; i++) {
- this.trigger('change:' + changes[i], this, current[changes[i]], options);
- }
- }
-
- // You might be wondering why there's a `while` loop here. Changes can
- // be recursively nested within `"change"` events.
- if (changing) return this;
- if (!silent) {
- while (this._pending) {
- this._pending = false;
- this.trigger('change', this, options);
- }
- }
- this._pending = false;
- this._changing = false;
- return this;
- },
-
- // Remove an attribute from the model, firing `"change"`. `unset` is a noop
- // if the attribute doesn't exist.
- unset: function(attr, options) {
- return this.set(attr, void 0, _.extend({}, options, {unset: true}));
- },
-
- // Clear all attributes on the model, firing `"change"`.
- clear: function(options) {
- var attrs = {};
- for (var key in this.attributes) attrs[key] = void 0;
- return this.set(attrs, _.extend({}, options, {unset: true}));
- },
-
- // Determine if the model has changed since the last `"change"` event.
- // If you specify an attribute name, determine if that attribute has changed.
- hasChanged: function(attr) {
- if (attr == null) return !_.isEmpty(this.changed);
- return _.has(this.changed, attr);
- },
-
- // Return an object containing all the attributes that have changed, or
- // false if there are no changed attributes. Useful for determining what
- // parts of a view need to be updated and/or what attributes need to be
- // persisted to the server. Unset attributes will be set to undefined.
- // You can also pass an attributes object to diff against the model,
- // determining if there *would be* a change.
- changedAttributes: function(diff) {
- if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
- var val, changed = false;
- var old = this._changing ? this._previousAttributes : this.attributes;
- for (var attr in diff) {
- if (_.isEqual(old[attr], (val = diff[attr]))) continue;
- (changed || (changed = {}))[attr] = val;
- }
- return changed;
- },
-
- // Get the previous value of an attribute, recorded at the time the last
- // `"change"` event was fired.
- previous: function(attr) {
- if (attr == null || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
- },
-
- // Get all of the attributes of the model at the time of the previous
- // `"change"` event.
- previousAttributes: function() {
- return _.clone(this._previousAttributes);
- },
-
- // Fetch the model from the server. If the server's representation of the
- // model differs from its current attributes, they will be overridden,
- // triggering a `"change"` event.
- fetch: function(options) {
- options = options ? _.clone(options) : {};
- if (options.parse === void 0) options.parse = true;
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- if (!model.set(model.parse(resp, options), options)) return false;
- if (success) success(model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
- return this.sync('read', this, options);
- },
-
- // Set a hash of model attributes, and sync the model to the server.
- // If the server returns an attributes hash that differs, the model's
- // state will be `set` again.
- save: function(key, val, options) {
- var attrs, method, xhr, attributes = this.attributes;
-
- // Handle both `"key", value` and `{key: value}` -style arguments.
- if (key == null || typeof key === 'object') {
- attrs = key;
- options = val;
- } else {
- (attrs = {})[key] = val;
- }
-
- // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
- if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
-
- options = _.extend({validate: true}, options);
-
- // Do not persist invalid models.
- if (!this._validate(attrs, options)) return false;
-
- // Set temporary attributes if `{wait: true}`.
- if (attrs && options.wait) {
- this.attributes = _.extend({}, attributes, attrs);
- }
-
- // After a successful server-side save, the client is (optionally)
- // updated with the server-side state.
- if (options.parse === void 0) options.parse = true;
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- // Ensure attributes are restored during synchronous saves.
- model.attributes = attributes;
- var serverAttrs = model.parse(resp, options);
- if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
- if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
- return false;
- }
- if (success) success(model, resp, options);
- model.trigger('sync', model, resp, options);
- };
- wrapError(this, options);
-
- method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
- if (method === 'patch') options.attrs = attrs;
- xhr = this.sync(method, this, options);
-
- // Restore attributes.
- if (attrs && options.wait) this.attributes = attributes;
-
- return xhr;
- },
-
- // Destroy this model on the server if it was already persisted.
- // Optimistically removes the model from its collection, if it has one.
- // If `wait: true` is passed, waits for the server to respond before removal.
- destroy: function(options) {
- options = options ? _.clone(options) : {};
- var model = this;
- var success = options.success;
-
- var destroy = function() {
- model.trigger('destroy', model, model.collection, options);
- };
-
- options.success = function(resp) {
- if (options.wait || model.isNew()) destroy();
- if (success) success(model, resp, options);
- if (!model.isNew()) model.trigger('sync', model, resp, options);
- };
-
- if (this.isNew()) {
- options.success();
- return false;
- }
- wrapError(this, options);
-
- var xhr = this.sync('delete', this, options);
- if (!options.wait) destroy();
- return xhr;
- },
-
- // Default URL for the model's representation on the server -- if you're
- // using Backbone's restful methods, override this to change the endpoint
- // that will be called.
- url: function() {
- var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
- if (this.isNew()) return base;
- return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
- },
-
- // **parse** converts a response into the hash of attributes to be `set` on
- // the model. The default implementation is just to pass the response along.
- parse: function(resp, options) {
- return resp;
- },
-
- // Create a new model with identical attributes to this one.
- clone: function() {
- return new this.constructor(this.attributes);
- },
-
- // A model is new if it has never been saved to the server, and lacks an id.
- isNew: function() {
- return this.id == null;
- },
-
- // Check if the model is currently in a valid state.
- isValid: function(options) {
- return this._validate({}, _.extend(options || {}, { validate: true }));
- },
-
- // Run validation against the next complete set of model attributes,
- // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
- _validate: function(attrs, options) {
- if (!options.validate || !this.validate) return true;
- attrs = _.extend({}, this.attributes, attrs);
- var error = this.validationError = this.validate(attrs, options) || null;
- if (!error) return true;
- this.trigger('invalid', this, error, _.extend(options || {}, {validationError: error}));
- return false;
- }
-
- });
-
- // Underscore methods that we want to implement on the Model.
- var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
-
- // Mix in each Underscore method as a proxy to `Model#attributes`.
- _.each(modelMethods, function(method) {
- Model.prototype[method] = function() {
- var args = slice.call(arguments);
- args.unshift(this.attributes);
- return _[method].apply(_, args);
- };
- });
-
- // Backbone.Collection
- // -------------------
-
- // If models tend to represent a single row of data, a Backbone Collection is
- // more analagous to a table full of data ... or a small slice or page of that
- // table, or a collection of rows that belong together for a particular reason
- // -- all of the messages in this particular folder, all of the documents
- // belonging to this particular author, and so on. Collections maintain
- // indexes of their models, both in order, and for lookup by `id`.
-
- // Create a new **Collection**, perhaps to contain a specific type of `model`.
- // If a `comparator` is specified, the Collection will maintain
- // its models in sort order, as they're added and removed.
- var Collection = Backbone.Collection = function(models, options) {
- options || (options = {});
- if (options.url) this.url = options.url;
- if (options.model) this.model = options.model;
- if (options.comparator !== void 0) this.comparator = options.comparator;
- this._reset();
- this.initialize.apply(this, arguments);
- if (models) this.reset(models, _.extend({silent: true}, options));
- };
-
- // Default options for `Collection#set`.
- var setOptions = {add: true, remove: true, merge: true};
- var addOptions = {add: true, merge: false, remove: false};
-
- // Define the Collection's inheritable methods.
- _.extend(Collection.prototype, Events, {
-
- // The default model for a collection is just a **Backbone.Model**.
- // This should be overridden in most cases.
- model: Model,
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
-
- // The JSON representation of a Collection is an array of the
- // models' attributes.
- toJSON: function(options) {
- return this.map(function(model){ return model.toJSON(options); });
- },
-
- // Proxy `Backbone.sync` by default.
- sync: function() {
- return Backbone.sync.apply(this, arguments);
- },
-
- // Add a model, or list of models to the set.
- add: function(models, options) {
- return this.set(models, _.defaults(options || {}, addOptions));
- },
-
- // Remove a model, or a list of models from the set.
- remove: function(models, options) {
- models = _.isArray(models) ? models.slice() : [models];
- options || (options = {});
- var i, l, index, model;
- for (i = 0, l = models.length; i < l; i++) {
- model = this.get(models[i]);
- if (!model) continue;
- delete this._byId[model.id];
- delete this._byId[model.cid];
- index = this.indexOf(model);
- this.models.splice(index, 1);
- this.length--;
- if (!options.silent) {
- options.index = index;
- model.trigger('remove', model, this, options);
- }
- this._removeReference(model);
- }
- return this;
- },
-
- // Update a collection by `set`-ing a new list of models, adding new ones,
- // removing models that are no longer present, and merging models that
- // already exist in the collection, as necessary. Similar to **Model#set**,
- // the core operation for updating the data contained by the collection.
- set: function(models, options) {
- options = _.defaults(options || {}, setOptions);
- if (options.parse) models = this.parse(models, options);
- if (!_.isArray(models)) models = models ? [models] : [];
- var i, l, model, attrs, existing, sort;
- var at = options.at;
- var sortable = this.comparator && (at == null) && options.sort !== false;
- var sortAttr = _.isString(this.comparator) ? this.comparator : null;
- var toAdd = [], toRemove = [], modelMap = {};
-
- // Turn bare objects into model references, and prevent invalid models
- // from being added.
- for (i = 0, l = models.length; i < l; i++) {
- if (!(model = this._prepareModel(models[i], options))) continue;
-
- // If a duplicate is found, prevent it from being added and
- // optionally merge it into the existing model.
- if (existing = this.get(model)) {
- if (options.remove) modelMap[existing.cid] = true;
- if (options.merge) {
- existing.set(model.attributes, options);
- if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
- }
-
- // This is a new model, push it to the `toAdd` list.
- } else if (options.add) {
- toAdd.push(model);
-
- // Listen to added models' events, and index models for lookup by
- // `id` and by `cid`.
- model.on('all', this._onModelEvent, this);
- this._byId[model.cid] = model;
- if (model.id != null) this._byId[model.id] = model;
- }
- }
-
- // Remove nonexistent models if appropriate.
- if (options.remove) {
- for (i = 0, l = this.length; i < l; ++i) {
- if (!modelMap[(model = this.models[i]).cid]) 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) {
- if (sortable) sort = true;
- this.length += toAdd.length;
- if (at != null) {
- splice.apply(this.models, [at, 0].concat(toAdd));
- } else {
- push.apply(this.models, toAdd);
- }
- }
-
- // Silently sort the collection if appropriate.
- if (sort) this.sort({silent: true});
-
- if (options.silent) return this;
-
- // Trigger `add` events.
- for (i = 0, l = toAdd.length; i < l; i++) {
- (model = toAdd[i]).trigger('add', model, this, options);
- }
-
- // Trigger `sort` if the collection was sorted.
- if (sort) this.trigger('sort', this, options);
- return this;
- },
-
- // 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, l = this.models.length; i < l; i++) {
- this._removeReference(this.models[i]);
- }
- options.previousModels = this.models;
- this._reset();
- this.add(models, _.extend({silent: true}, options));
- if (!options.silent) this.trigger('reset', this, options);
- return this;
- },
-
- // Add a model to the end of the collection.
- push: function(model, options) {
- model = this._prepareModel(model, options);
- this.add(model, _.extend({at: this.length}, options));
- return model;
- },
-
- // Remove a model from the end of the collection.
- pop: function(options) {
- var model = this.at(this.length - 1);
- this.remove(model, options);
- return model;
- },
-
- // Add a model to the beginning of the collection.
- unshift: function(model, options) {
- model = this._prepareModel(model, options);
- this.add(model, _.extend({at: 0}, options));
- return model;
- },
-
- // Remove a model from the beginning of the collection.
- shift: function(options) {
- var model = this.at(0);
- this.remove(model, options);
- return model;
- },
-
- // Slice out a sub-array of models from the collection.
- slice: function(begin, end) {
- return this.models.slice(begin, end);
- },
-
- // Get a model from the set by id.
- get: function(obj) {
- if (obj == null) return void 0;
- return this._byId[obj.id != null ? obj.id : obj.cid || obj];
- },
-
- // Get the model at the given index.
- at: function(index) {
- return this.models[index];
- },
-
- // Return models with matching attributes. Useful for simple cases of
- // `filter`.
- where: function(attrs, first) {
- if (_.isEmpty(attrs)) return first ? void 0 : [];
- return this[first ? 'find' : 'filter'](function(model) {
- for (var key in attrs) {
- if (attrs[key] !== model.get(key)) return false;
- }
- return true;
- });
- },
-
- // Return the first model with matching attributes. Useful for simple cases
- // of `find`.
- findWhere: function(attrs) {
- return this.where(attrs, true);
- },
-
- // Force the collection to re-sort itself. You don't need to call this under
- // normal circumstances, as the set will maintain sort order as each item
- // is added.
- sort: function(options) {
- if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
- options || (options = {});
-
- // Run sort based on type of `comparator`.
- if (_.isString(this.comparator) || this.comparator.length === 1) {
- this.models = this.sortBy(this.comparator, this);
- } else {
- this.models.sort(_.bind(this.comparator, this));
- }
-
- if (!options.silent) this.trigger('sort', this, options);
- return this;
- },
-
- // Figure out the smallest index at which a model should be inserted so as
- // to maintain order.
- sortedIndex: function(model, value, context) {
- value || (value = this.comparator);
- var iterator = _.isFunction(value) ? value : function(model) {
- return model.get(value);
- };
- return _.sortedIndex(this.models, model, iterator, context);
- },
-
- // Pluck an attribute from each model in the collection.
- pluck: function(attr) {
- return _.invoke(this.models, 'get', attr);
- },
-
- // Fetch the default set of models for this collection, resetting the
- // collection when they arrive. If `reset: true` is passed, the response
- // data will be passed through the `reset` method instead of `set`.
- fetch: function(options) {
- options = options ? _.clone(options) : {};
- if (options.parse === void 0) options.parse = true;
- var success = options.success;
- var collection = this;
- options.success = function(resp) {
- var method = options.reset ? 'reset' : 'set';
- collection[method](resp, options);
- if (success) success(collection, resp, options);
- collection.trigger('sync', collection, resp, options);
- };
- wrapError(this, options);
- return this.sync('read', this, options);
- },
-
- // Create a new instance of a model in this collection. Add the model to the
- // collection immediately, unless `wait: true` is passed, in which case we
- // wait for the server to agree.
- create: function(model, options) {
- options = options ? _.clone(options) : {};
- if (!(model = this._prepareModel(model, options))) return false;
- if (!options.wait) this.add(model, options);
- var collection = this;
- var success = options.success;
- options.success = function(resp) {
- if (options.wait) collection.add(model, options);
- if (success) success(model, resp, options);
- };
- model.save(null, options);
- return model;
- },
-
- // **parse** converts a response into a list of models to be added to the
- // collection. The default implementation is just to pass it through.
- parse: function(resp, options) {
- return resp;
- },
-
- // Create a new collection with an identical list of models as this one.
- clone: function() {
- return new this.constructor(this.models);
- },
-
- // Private method to reset all internal state. Called when the collection
- // is first initialized or reset.
- _reset: function() {
- this.length = 0;
- this.models = [];
- this._byId = {};
- },
-
- // Prepare a hash of attributes (or other model) to be added to this
- // collection.
- _prepareModel: function(attrs, options) {
- if (attrs instanceof Model) {
- if (!attrs.collection) attrs.collection = this;
- return attrs;
- }
- options || (options = {});
- options.collection = this;
- var model = new this.model(attrs, options);
- if (!model._validate(attrs, options)) {
- this.trigger('invalid', this, attrs, options);
- return false;
- }
- return model;
- },
-
- // Internal method to sever a model's ties to a collection.
- _removeReference: function(model) {
- if (this === model.collection) delete model.collection;
- model.off('all', this._onModelEvent, this);
- },
-
- // Internal method called every time a model in the set fires an event.
- // Sets need to update their indexes when models change ids. All other
- // events simply proxy through. "add" and "remove" events that originate
- // in other collections are ignored.
- _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:' + model.idAttribute) {
- delete this._byId[model.previous(model.idAttribute)];
- if (model.id != null) this._byId[model.id] = model;
- }
- this.trigger.apply(this, arguments);
- }
-
- });
-
- // Underscore methods that we want to implement on the Collection.
- // 90% of the core usefulness of Backbone Collections is actually implemented
- // right here:
- var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
- 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
- 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
- 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
- 'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
- 'isEmpty', 'chain'];
-
- // Mix in each Underscore method as a proxy to `Collection#models`.
- _.each(methods, function(method) {
- Collection.prototype[method] = function() {
- var args = slice.call(arguments);
- args.unshift(this.models);
- return _[method].apply(_, args);
- };
- });
-
- // Underscore methods that take a property name as an argument.
- var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
-
- // Use attributes instead of properties.
- _.each(attributeMethods, function(method) {
- Collection.prototype[method] = function(value, context) {
- var iterator = _.isFunction(value) ? value : function(model) {
- return model.get(value);
- };
- return _[method](this.models, iterator, context);
- };
- });
-
- // Backbone.View
- // -------------
-
- // Backbone Views are almost more convention than they are actual code. A View
- // is simply a JavaScript object that represents a logical chunk of UI in the
- // DOM. This might be a single item, an entire list, a sidebar or panel, or
- // even the surrounding frame which wraps your whole app. Defining a chunk of
- // UI as a **View** allows you to define your DOM events declaratively, without
- // having to worry about render order ... and makes it easy for the view to
- // react to specific changes in the state of your models.
-
- // Creating a Backbone.View creates its initial element outside of the DOM,
- // if an existing element is not provided...
- var View = Backbone.View = function(options) {
- this.cid = _.uniqueId('view');
- this._configure(options || {});
- this._ensureElement();
- this.initialize.apply(this, arguments);
- this.delegateEvents();
- };
-
- // Cached regex to split keys for `delegate`.
- var delegateEventSplitter = /^(\S+)\s*(.*)$/;
-
- // List of view options to be merged as properties.
- var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
-
- // Set up all inheritable **Backbone.View** properties and methods.
- _.extend(View.prototype, Events, {
-
- // The default `tagName` of a View's element is `"div"`.
- tagName: 'div',
-
- // jQuery delegate for element lookup, scoped to DOM elements within the
- // current view. This should be prefered to global lookups where possible.
- $: function(selector) {
- return this.$el.find(selector);
- },
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
-
- // **render** is the core function that your view should override, in order
- // to populate its element (`this.el`), with the appropriate HTML. The
- // convention is for **render** to always return `this`.
- render: function() {
- return this;
- },
-
- // Remove this view by taking the element out of the DOM, and removing any
- // applicable Backbone.Events listeners.
- remove: function() {
- this.$el.remove();
- this.stopListening();
- return this;
- },
-
- // Change the view's element (`this.el` property), including event
- // re-delegation.
- setElement: function(element, delegate) {
- if (this.$el) this.undelegateEvents();
- this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
- this.el = this.$el[0];
- if (delegate !== false) this.delegateEvents();
- return this;
- },
-
- // Set callbacks, where `this.events` is a hash of
- //
- // *{"event selector": "callback"}*
- //
- // {
- // 'mousedown .title': 'edit',
- // 'click .button': 'save'
- // 'click .open': function(e) { ... }
- // }
- //
- // pairs. Callbacks will be bound to the view, with `this` set properly.
- // Uses event delegation for efficiency.
- // Omitting the selector binds the event to `this.el`.
- // This only works for delegate-able events: not `focus`, `blur`, and
- // not `change`, `submit`, and `reset` in Internet Explorer.
- delegateEvents: function(events) {
- if (!(events || (events = _.result(this, 'events')))) return this;
- this.undelegateEvents();
- for (var key in events) {
- var method = events[key];
- if (!_.isFunction(method)) method = this[events[key]];
- if (!method) continue;
-
- var match = key.match(delegateEventSplitter);
- var eventName = match[1], selector = match[2];
- method = _.bind(method, this);
- eventName += '.delegateEvents' + this.cid;
- if (selector === '') {
- this.$el.on(eventName, method);
- } else {
- this.$el.on(eventName, selector, method);
- }
- }
- return this;
- },
-
- // Clears all callbacks previously bound to the view with `delegateEvents`.
- // You usually don't need to use this, but may wish to if you have multiple
- // Backbone views attached to the same DOM element.
- undelegateEvents: function() {
- this.$el.off('.delegateEvents' + this.cid);
- return this;
- },
-
- // Performs the initial configuration of a View with a set of options.
- // Keys with special meaning *(e.g. model, collection, id, className)* are
- // attached directly to the view. See `viewOptions` for an exhaustive
- // list.
- _configure: function(options) {
- if (this.options) options = _.extend({}, _.result(this, 'options'), options);
- _.extend(this, _.pick(options, viewOptions));
- this.options = options;
- },
-
- // Ensure that the View has a DOM element to render into.
- // If `this.el` is a string, pass it through `$()`, take the first
- // matching element, and re-assign it to `el`. Otherwise, create
- // an element from the `id`, `className` and `tagName` properties.
- _ensureElement: function() {
- if (!this.el) {
- var attrs = _.extend({}, _.result(this, 'attributes'));
- if (this.id) attrs.id = _.result(this, 'id');
- if (this.className) attrs['class'] = _.result(this, 'className');
- var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
- this.setElement($el, false);
- } else {
- this.setElement(_.result(this, 'el'), false);
- }
- }
-
- });
-
- // Backbone.sync
- // -------------
-
- // Override this function to change the manner in which Backbone persists
- // models to the server. You will be passed the type of request, and the
- // model in question. By default, makes a RESTful Ajax request
- // to the model's `url()`. Some possible customizations could be:
- //
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
- // * Send up the models as XML instead of JSON.
- // * Persist models via WebSockets instead of Ajax.
- //
- // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
- // as `POST`, with a `_method` parameter containing the true HTTP method,
- // as well as all requests with the body as `application/x-www-form-urlencoded`
- // instead of `application/json` with the model in a param named `model`.
- // Useful when interfacing with server-side languages like **PHP** that make
- // it difficult to read the body of `PUT` requests.
- Backbone.sync = function(method, model, options) {
- var type = methodMap[method];
-
- // Default options, unless specified.
- _.defaults(options || (options = {}), {
- emulateHTTP: Backbone.emulateHTTP,
- emulateJSON: Backbone.emulateJSON
- });
-
- // Default JSON-request options.
- var params = {type: type, dataType: 'json'};
-
- // Ensure that we have a URL.
- if (!options.url) {
- params.url = _.result(model, 'url') || urlError();
- }
-
- // Ensure that we have the appropriate request data.
- if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
- params.contentType = 'application/json';
- params.data = JSON.stringify(options.attrs || model.toJSON(options));
- }
-
- // For older servers, emulate JSON by encoding the request into an HTML-form.
- if (options.emulateJSON) {
- params.contentType = 'application/x-www-form-urlencoded';
- params.data = params.data ? {model: params.data} : {};
- }
-
- // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
- // And an `X-HTTP-Method-Override` header.
- if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
- params.type = 'POST';
- if (options.emulateJSON) params.data._method = type;
- var beforeSend = options.beforeSend;
- options.beforeSend = function(xhr) {
- xhr.setRequestHeader('X-HTTP-Method-Override', type);
- if (beforeSend) return beforeSend.apply(this, arguments);
- };
- }
-
- // Don't process data on a non-GET request.
- if (params.type !== 'GET' && !options.emulateJSON) {
- params.processData = false;
- }
-
- // If we're sending a `PATCH` request, and we're in an old Internet Explorer
- // that still has ActiveX enabled by default, override jQuery to use that
- // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
- if (params.type === 'PATCH' && window.ActiveXObject &&
- !(window.external && window.external.msActiveXFilteringEnabled)) {
- params.xhr = function() {
- return new ActiveXObject("Microsoft.XMLHTTP");
- };
- }
-
- // Make the request, allowing the user to override any Ajax options.
- var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
- model.trigger('request', model, xhr, options);
- return xhr;
- };
-
- // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
- var methodMap = {
- 'create': 'POST',
- 'update': 'PUT',
- 'patch': 'PATCH',
- 'delete': 'DELETE',
- 'read': 'GET'
- };
-
- // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
- // Override this if you'd like to use a different library.
- Backbone.ajax = function() {
- return Backbone.$.ajax.apply(Backbone.$, arguments);
- };
-
- // Backbone.Router
- // ---------------
-
- // Routers map faux-URLs to actions, and fire events when routes are
- // matched. Creating a new one sets its `routes` hash, if not set statically.
- var Router = Backbone.Router = function(options) {
- options || (options = {});
- if (options.routes) this.routes = options.routes;
- this._bindRoutes();
- this.initialize.apply(this, arguments);
- };
-
- // Cached regular expressions for matching named param parts and splatted
- // parts of route strings.
- var optionalParam = /\((.*?)\)/g;
- var namedParam = /(\(\?)?:\w+/g;
- var splatParam = /\*\w+/g;
- var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
-
- // Set up all inheritable **Backbone.Router** properties and methods.
- _.extend(Router.prototype, Events, {
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize: function(){},
-
- // Manually bind a single named route to a callback. For example:
- //
- // this.route('search/:query/p:num', 'search', function(query, num) {
- // ...
- // });
- //
- route: function(route, name, callback) {
- if (!_.isRegExp(route)) route = this._routeToRegExp(route);
- if (_.isFunction(name)) {
- callback = name;
- name = '';
- }
- if (!callback) callback = this[name];
- var router = this;
- Backbone.history.route(route, function(fragment) {
- var args = router._extractParameters(route, fragment);
- callback && callback.apply(router, args);
- router.trigger.apply(router, ['route:' + name].concat(args));
- router.trigger('route', name, args);
- Backbone.history.trigger('route', router, name, args);
- });
- return this;
- },
-
- // Simple proxy to `Backbone.history` to save a fragment into the history.
- navigate: function(fragment, options) {
- Backbone.history.navigate(fragment, options);
- return this;
- },
-
- // Bind all defined routes to `Backbone.history`. We have to reverse the
- // order of the routes here to support behavior where the most general
- // routes can be defined at the bottom of the route map.
- _bindRoutes: function() {
- if (!this.routes) return;
- this.routes = _.result(this, 'routes');
- var route, routes = _.keys(this.routes);
- while ((route = routes.pop()) != null) {
- this.route(route, this.routes[route]);
- }
- },
-
- // Convert a route string into a regular expression, suitable for matching
- // against the current location hash.
- _routeToRegExp: function(route) {
- route = route.replace(escapeRegExp, '\\$&')
- .replace(optionalParam, '(?:$1)?')
- .replace(namedParam, function(match, optional){
- return optional ? match : '([^\/]+)';
- })
- .replace(splatParam, '(.*?)');
- return new RegExp('^' + route + '$');
- },
-
- // Given a route, and a URL fragment that it matches, return the array of
- // extracted decoded parameters. Empty or unmatched parameters will be
- // treated as `null` to normalize cross-browser behavior.
- _extractParameters: function(route, fragment) {
- var params = route.exec(fragment).slice(1);
- return _.map(params, function(param) {
- return param ? decodeURIComponent(param) : null;
- });
- }
-
- });
-
- // Backbone.History
- // ----------------
-
- // Handles cross-browser history management, based on either
- // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
- // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
- // and URL fragments. If the browser supports neither (old IE, natch),
- // falls back to polling.
- var History = Backbone.History = function() {
- this.handlers = [];
- _.bindAll(this, 'checkUrl');
-
- // Ensure that `History` can be used outside of the browser.
- if (typeof window !== 'undefined') {
- this.location = window.location;
- this.history = window.history;
- }
- };
-
- // Cached regex for stripping a leading hash/slash and trailing space.
- var routeStripper = /^[#\/]|\s+$/g;
-
- // Cached regex for stripping leading and trailing slashes.
- var rootStripper = /^\/+|\/+$/g;
-
- // Cached regex for detecting MSIE.
- var isExplorer = /msie [\w.]+/;
-
- // Cached regex for removing a trailing slash.
- var trailingSlash = /\/$/;
-
- // Has the history handling already been started?
- History.started = false;
-
- // Set up all inheritable **Backbone.History** properties and methods.
- _.extend(History.prototype, Events, {
-
- // The default interval to poll for hash changes, if necessary, is
- // twenty times a second.
- interval: 50,
-
- // Gets the true hash value. Cannot use location.hash directly due to bug
- // in Firefox where location.hash will always be decoded.
- getHash: function(window) {
- var match = (window || this).location.href.match(/#(.*)$/);
- return match ? match[1] : '';
- },
-
- // Get the cross-browser normalized URL fragment, either from the URL,
- // the hash, or the override.
- getFragment: function(fragment, forcePushState) {
- if (fragment == null) {
- if (this._hasPushState || !this._wantsHashChange || forcePushState) {
- fragment = this.location.pathname;
- var root = this.root.replace(trailingSlash, '');
- if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
- } else {
- fragment = this.getHash();
- }
- }
- return fragment.replace(routeStripper, '');
- },
-
- // Start the hash change handling, returning `true` if the current URL matches
- // an existing route, and `false` otherwise.
- start: function(options) {
- if (History.started) throw new Error("Backbone.history has already been started");
- History.started = true;
-
- // Figure out the initial configuration. Do we need an iframe?
- // Is pushState desired ... is it available?
- this.options = _.extend({}, {root: '/'}, this.options, options);
- this.root = this.options.root;
- this._wantsHashChange = this.options.hashChange !== false;
- this._wantsPushState = !!this.options.pushState;
- this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
- var fragment = this.getFragment();
- var docMode = document.documentMode;
- var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
-
- // Normalize root to always include a leading and trailing slash.
- this.root = ('/' + this.root + '/').replace(rootStripper, '/');
-
- if (oldIE && this._wantsHashChange) {
- this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
- this.navigate(fragment);
- }
-
- // Depending on whether we're using pushState or hashes, and whether
- // 'onhashchange' is supported, determine how we check the URL state.
- if (this._hasPushState) {
- Backbone.$(window).on('popstate', this.checkUrl);
- } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
- Backbone.$(window).on('hashchange', this.checkUrl);
- } else if (this._wantsHashChange) {
- this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
- }
-
- // Determine if we need to change the base url, for a pushState link
- // opened by a non-pushState browser.
- this.fragment = fragment;
- var loc = this.location;
- var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
-
- // If we've started off with a route from a `pushState`-enabled browser,
- // but we're currently in a browser that doesn't support it...
- if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
- this.fragment = this.getFragment(null, true);
- this.location.replace(this.root + this.location.search + '#' + this.fragment);
- // Return immediately as browser will do redirect to new url
- return true;
-
- // Or if we've started out with a hash-based route, but we're currently
- // in a browser where it could be `pushState`-based instead...
- } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
- this.fragment = this.getHash().replace(routeStripper, '');
- this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
- }
-
- if (!this.options.silent) return this.loadUrl();
- },
-
- // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
- // but possibly useful for unit testing Routers.
- stop: function() {
- Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
- clearInterval(this._checkUrlInterval);
- History.started = false;
- },
-
- // Add a route to be tested when the fragment changes. Routes added later
- // may override previous routes.
- route: function(route, callback) {
- this.handlers.unshift({route: route, callback: callback});
- },
-
- // Checks the current URL to see if it has changed, and if it has,
- // calls `loadUrl`, normalizing across the hidden iframe.
- checkUrl: function(e) {
- var current = this.getFragment();
- if (current === this.fragment && this.iframe) {
- current = this.getFragment(this.getHash(this.iframe));
- }
- if (current === this.fragment) return false;
- if (this.iframe) this.navigate(current);
- this.loadUrl() || this.loadUrl(this.getHash());
- },
-
- // Attempt to load the current URL fragment. If a route succeeds with a
- // match, returns `true`. If no defined routes matches the fragment,
- // returns `false`.
- loadUrl: function(fragmentOverride) {
- var fragment = this.fragment = this.getFragment(fragmentOverride);
- var matched = _.any(this.handlers, function(handler) {
- if (handler.route.test(fragment)) {
- handler.callback(fragment);
- return true;
- }
- });
- return matched;
- },
-
- // Save a fragment into the hash history, or replace the URL state if the
- // 'replace' option is passed. You are responsible for properly URL-encoding
- // the fragment in advance.
- //
- // The options object can contain `trigger: true` if you wish to have the
- // route callback be fired (not usually desirable), or `replace: true`, if
- // you wish to modify the current URL without adding an entry to the history.
- navigate: function(fragment, options) {
- if (!History.started) return false;
- if (!options || options === true) options = {trigger: options};
- fragment = this.getFragment(fragment || '');
- if (this.fragment === fragment) return;
- this.fragment = fragment;
- var url = this.root + fragment;
-
- // If pushState is available, we use it to set the fragment as a real URL.
- if (this._hasPushState) {
- this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
-
- // If hash changes haven't been explicitly disabled, update the hash
- // fragment to store history.
- } else if (this._wantsHashChange) {
- this._updateHash(this.location, fragment, options.replace);
- if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
- // Opening and closing the iframe tricks IE7 and earlier to push a
- // history entry on hash-tag change. When replace is true, we don't
- // want this.
- if(!options.replace) this.iframe.document.open().close();
- this._updateHash(this.iframe.location, fragment, options.replace);
- }
-
- // If you've told us that you explicitly don't want fallback hashchange-
- // based history, then `navigate` becomes a page refresh.
- } else {
- return this.location.assign(url);
- }
- if (options.trigger) this.loadUrl(fragment);
- },
-
- // Update the hash location, either replacing the current entry, or adding
- // a new one to the browser history.
- _updateHash: function(location, fragment, replace) {
- if (replace) {
- var href = location.href.replace(/(javascript:|#).*$/, '');
- location.replace(href + '#' + fragment);
- } else {
- // Some browsers require that `hash` contains a leading #.
- location.hash = '#' + fragment;
- }
- }
-
- });
-
- // Create the default Backbone.history.
- Backbone.history = new History;
-
- // Helpers
- // -------
-
- // Helper function to correctly set up the prototype chain, for subclasses.
- // Similar to `goog.inherits`, but uses a hash of prototype properties and
- // class properties to be extended.
- var extend = function(protoProps, staticProps) {
- var parent = this;
- var child;
-
- // The constructor function for the new subclass is either defined by you
- // (the "constructor" property in your `extend` definition), or defaulted
- // by us to simply call the parent's constructor.
- if (protoProps && _.has(protoProps, 'constructor')) {
- child = protoProps.constructor;
- } else {
- child = function(){ return parent.apply(this, arguments); };
- }
-
- // Add static properties to the constructor function, if supplied.
- _.extend(child, parent, staticProps);
-
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function.
- var Surrogate = function(){ this.constructor = child; };
- Surrogate.prototype = parent.prototype;
- child.prototype = new Surrogate;
-
- // Add prototype properties (instance properties) to the subclass,
- // if supplied.
- if (protoProps) _.extend(child.prototype, protoProps);
-
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.__super__ = parent.prototype;
-
- return child;
- };
-
- // Set up inheritance for the model, collection, router, view and history.
- Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
-
- // Throw an error when a URL is needed, and none is supplied.
- var urlError = function() {
- throw new Error('A "url" property or function must be specified');
- };
-
- // Wrap an optional error callback with a fallback error event.
- var wrapError = function (model, options) {
- var error = options.error;
- options.error = function(resp) {
- if (error) error(model, resp, options);
- model.trigger('error', model, resp, options);
- };
- };
-
-}).call(this);
diff --git a/go/base/static/js/vendor/backbone-relational-0.8.5.js b/go/base/static/js/vendor/backbone-relational-0.8.5.js
deleted file mode 100644
index 16fd296..0000000
--- a/go/base/static/js/vendor/backbone-relational-0.8.5.js
+++ /dev/null
@@ -1,1860 +0,0 @@
-/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
-/**
- * Backbone-relational.js 0.8.5
- * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
- *
- * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
- * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
- * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
- */
-( function( undefined ) {
- "use strict";
-
- /**
- * CommonJS shim
- **/
- var _, Backbone, exports;
- if ( typeof window === 'undefined' ) {
- _ = require( 'underscore' );
- Backbone = require( 'backbone' );
- exports = module.exports = Backbone;
- }
- else {
- _ = window._;
- Backbone = window.Backbone;
- exports = window;
- }
-
- Backbone.Relational = {
- showWarnings: true
- };
-
- /**
- * Semaphore mixin; can be used as both binary and counting.
- **/
- Backbone.Semaphore = {
- _permitsAvailable: null,
- _permitsUsed: 0,
-
- acquire: function() {
- if ( this._permitsAvailable && this._permitsUsed >= this._permitsAvailable ) {
- throw new Error( 'Max permits acquired' );
- }
- else {
- this._permitsUsed++;
- }
- },
-
- release: function() {
- if ( this._permitsUsed === 0 ) {
- throw new Error( 'All permits released' );
- }
- else {
- this._permitsUsed--;
- }
- },
-
- isLocked: function() {
- return this._permitsUsed > 0;
- },
-
- setAvailablePermits: function( amount ) {
- if ( this._permitsUsed > amount ) {
- throw new Error( 'Available permits cannot be less than used permits' );
- }
- this._permitsAvailable = amount;
- }
- };
-
- /**
- * A BlockingQueue that accumulates items while blocked (via 'block'),
- * and processes them when unblocked (via 'unblock').
- * Process can also be called manually (via 'process').
- */
- Backbone.BlockingQueue = function() {
- this._queue = [];
- };
- _.extend( Backbone.BlockingQueue.prototype, Backbone.Semaphore, {
- _queue: null,
-
- add: function( func ) {
- if ( this.isBlocked() ) {
- this._queue.push( func );
- }
- else {
- func();
- }
- },
-
- process: function() {
- while ( this._queue && this._queue.length ) {
- this._queue.shift()();
- }
- },
-
- block: function() {
- this.acquire();
- },
-
- unblock: function() {
- this.release();
- if ( !this.isBlocked() ) {
- this.process();
- }
- },
-
- isBlocked: function() {
- return this.isLocked();
- }
- });
- /**
- * Global event queue. Accumulates external events ('add:<key>', 'remove:<key>' and 'change:<key>')
- * until the top-level object is fully initialized (see 'Backbone.RelationalModel').
- */
- Backbone.Relational.eventQueue = new Backbone.BlockingQueue();
-
- /**
- * Backbone.Store keeps track of all created (and destruction of) Backbone.RelationalModel.
- * Handles lookup for relations.
- */
- Backbone.Store = function() {
- this._collections = [];
- this._reverseRelations = [];
- this._orphanRelations = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- };
- _.extend( Backbone.Store.prototype, Backbone.Events, {
- /**
- * Create a new `Relation`.
- * @param {Backbone.RelationalModel} [model]
- * @param {Object} relation
- * @param {Object} [options]
- */
- initializeRelation: function( model, relation, options ) {
- var type = !_.isString( relation.type ) ? relation.type : Backbone[ relation.type ] || this.getObjectByName( relation.type );
- if ( type && type.prototype instanceof Backbone.Relation ) {
- new type( model, relation, options ); // Also pushes the new Relation into `model._relations`
- }
- else {
- Backbone.Relational.showWarnings && typeof console !== 'undefined' && console.warn( 'Relation=%o; missing or invalid relation type!', relation );
- }
- },
-
- /**
- * Add a scope for `getObjectByName` to look for model types by name.
- * @param {Object} scope
- */
- addModelScope: function( scope ) {
- this._modelScopes.push( scope );
- },
-
- /**
- * Remove a scope.
- * @param {Object} scope
- */
- removeModelScope: function( scope ) {
- this._modelScopes = _.without( this._modelScopes, scope );
- },
-
- /**
- * Add a set of subModelTypes to the store, that can be used to resolve the '_superModel'
- * for a model later in 'setupSuperModel'.
- *
- * @param {Backbone.RelationalModel} subModelTypes
- * @param {Backbone.RelationalModel} superModelType
- */
- addSubModels: function( subModelTypes, superModelType ) {
- this._subModels.push({
- 'superModelType': superModelType,
- 'subModels': subModelTypes
- });
- },
-
- /**
- * Check if the given modelType is registered as another model's subModel. If so, add it to the super model's
- * '_subModels', and set the modelType's '_superModel', '_subModelTypeName', and '_subModelTypeAttribute'.
- *
- * @param {Backbone.RelationalModel} modelType
- */
- setupSuperModel: function( modelType ) {
- _.find( this._subModels, function( subModelDef ) {
- return _.find( subModelDef.subModels || [], function( subModelTypeName, typeValue ) {
- var subModelType = this.getObjectByName( subModelTypeName );
-
- if ( modelType === subModelType ) {
- // Set 'modelType' as a child of the found superModel
- subModelDef.superModelType._subModels[ typeValue ] = modelType;
-
- // Set '_superModel', '_subModelTypeValue', and '_subModelTypeAttribute' on 'modelType'.
- modelType._superModel = subModelDef.superModelType;
- modelType._subModelTypeValue = typeValue;
- modelType._subModelTypeAttribute = subModelDef.superModelType.prototype.subModelTypeAttribute;
- return true;
- }
- }, this );
- }, this );
- },
-
- /**
- * Add a reverse relation. Is added to the 'relations' property on model's prototype, and to
- * existing instances of 'model' in the store as well.
- * @param {Object} relation
- * @param {Backbone.RelationalModel} relation.model
- * @param {String} relation.type
- * @param {String} relation.key
- * @param {String|Object} relation.relatedModel
- */
- addReverseRelation: function( relation ) {
- var exists = _.any( this._reverseRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._reverseRelations.push( relation );
- this._addRelation( relation.model, relation );
- this.retroFitRelation( relation );
- }
- },
-
- /**
- * Deposit a `relation` for which the `relatedModel` can't be resolved at the moment.
- *
- * @param {Object} relation
- */
- addOrphanRelation: function( relation ) {
- var exists = _.any( this._orphanRelations, function( rel ) {
- return _.all( relation || [], function( val, key ) {
- return val === rel[ key ];
- });
- });
-
- if ( !exists && relation.model && relation.type ) {
- this._orphanRelations.push( relation );
- }
- },
-
- /**
- * Try to initialize any `_orphanRelation`s
- */
- processOrphanRelations: function() {
- // Make sure to operate on a copy since we're removing while iterating
- _.each( this._orphanRelations.slice( 0 ), function( rel ) {
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- if ( relatedModel ) {
- this.initializeRelation( null, rel );
- this._orphanRelations = _.without( this._orphanRelations, rel );
- }
- }, this );
- },
-
- /**
- *
- * @param {Backbone.RelationalModel.constructor} type
- * @param {Object} relation
- * @private
- */
- _addRelation: function( type, relation ) {
- if ( !type.prototype.relations ) {
- type.prototype.relations = [];
- }
- type.prototype.relations.push( relation );
-
- _.each( type._subModels || [], function( subModel ) {
- this._addRelation( subModel, relation );
- }, this );
- },
-
- /**
- * Add a 'relation' to all existing instances of 'relation.model' in the store
- * @param {Object} relation
- */
- retroFitRelation: function( relation ) {
- var coll = this.getCollection( relation.model, false );
- coll && coll.each( function( model ) {
- if ( !( model instanceof relation.model ) ) {
- return;
- }
-
- new relation.type( model, relation );
- }, this );
- },
-
- /**
- * Find the Store's collection for a certain type of model.
- * @param {Backbone.RelationalModel} type
- * @param {Boolean} [create=true] Should a collection be created if none is found?
- * @return {Backbone.Collection} A collection if found (or applicable for 'model'), or null
- */
- getCollection: function( type, create ) {
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- var rootModel = type;
- while ( rootModel._superModel ) {
- rootModel = rootModel._superModel;
- }
-
- var coll = _.findWhere( this._collections, { model: rootModel } );
-
- if ( !coll && create !== false ) {
- coll = this._createCollection( rootModel );
- }
-
- return coll;
- },
-
- /**
- * Find a model type on one of the modelScopes by name. Names are split on dots.
- * @param {String} name
- * @return {Object}
- */
- getObjectByName: function( name ) {
- var parts = name.split( '.' ),
- type = null;
-
- _.find( this._modelScopes, function( scope ) {
- type = _.reduce( parts || [], function( memo, val ) {
- return memo ? memo[ val ] : undefined;
- }, scope );
-
- if ( type && type !== scope ) {
- return true;
- }
- }, this );
-
- return type;
- },
-
- _createCollection: function( type ) {
- var coll;
-
- // If 'type' is an instance, take its constructor
- if ( type instanceof Backbone.RelationalModel ) {
- type = type.constructor;
- }
-
- // Type should inherit from Backbone.RelationalModel.
- if ( type.prototype instanceof Backbone.RelationalModel ) {
- coll = new Backbone.Collection();
- coll.model = type;
-
- this._collections.push( coll );
- }
-
- return coll;
- },
-
- /**
- * Find the attribute that is to be used as the `id` on a given object
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- * @return {String|Number}
- */
- resolveIdForItem: function( type, item ) {
- var id = _.isString( item ) || _.isNumber( item ) ? item : null;
-
- if ( id === null ) {
- if ( item instanceof Backbone.RelationalModel ) {
- id = item.id;
- }
- else if ( _.isObject( item ) ) {
- id = item[ type.prototype.idAttribute ];
- }
- }
-
- // Make all falsy values `null` (except for 0, which could be an id.. see '/issues/179')
- if ( !id && id !== 0 ) {
- id = null;
- }
-
- return id;
- },
-
- /**
- * Find a specific model of a certain `type` in the store
- * @param type
- * @param {String|Number|Object|Backbone.RelationalModel} item
- */
- find: function( type, item ) {
- var id = this.resolveIdForItem( type, item );
- var coll = this.getCollection( type );
-
- // Because the found object could be of any of the type's superModel
- // types, only return it if it's actually of the type asked for.
- if ( coll ) {
- var obj = coll.get( id );
-
- if ( obj instanceof type ) {
- return obj;
- }
- }
-
- return null;
- },
-
- /**
- * Add a 'model' to its appropriate collection. Retain the original contents of 'model.collection'.
- * @param {Backbone.RelationalModel} model
- */
- register: function( model ) {
- var coll = this.getCollection( model );
-
- if ( coll ) {
- var modelColl = model.collection;
- coll.add( model );
- this.listenTo( model, 'destroy', this.unregister, this );
- model.collection = modelColl;
- }
- },
-
- /**
- * Check if the given model may use the given `id`
- * @param model
- * @param [id]
- */
- checkId: function( model, id ) {
- var coll = this.getCollection( model ),
- duplicate = coll && coll.get( id );
-
- if ( duplicate && model !== duplicate ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Duplicate id! Old RelationalModel=%o, new RelationalModel=%o', duplicate, model );
- }
-
- throw new Error( "Cannot instantiate more than one Backbone.RelationalModel with the same id per type!" );
- }
- },
-
- /**
- * Explicitly update a model's id in its store collection
- * @param {Backbone.RelationalModel} model
- */
- update: function( model ) {
- var coll = this.getCollection( model );
- // This triggers updating the lookup indices kept in a collection
- coll._onModelEvent( 'change:' + model.idAttribute, model, coll );
-
- // Trigger an event on model so related models (having the model's new id in their keyContents) can add it.
- model.trigger( 'relational:change:id', model, coll );
- },
-
- /**
- * Remove a 'model' from the store.
- * @param {Backbone.RelationalModel} model
- */
- unregister: function( model ) {
- this.stopListening( model, 'destroy', this.unregister );
- var coll = this.getCollection( model );
- coll && coll.remove( model );
- },
-
- /**
- * Reset the `store` to it's original state. The `reverseRelations` are kept though, since attempting to
- * re-initialize these on models would lead to a large amount of warnings.
- */
- reset: function() {
- this.stopListening();
- this._collections = [];
- this._subModels = [];
- this._modelScopes = [ exports ];
- }
- });
- Backbone.Relational.store = new Backbone.Store();
-
- /**
- * The main Relation class, from which 'HasOne' and 'HasMany' inherit. Internally, 'relational:<key>' events
- * are used to regulate addition and removal of models from relations.
- *
- * @param {Backbone.RelationalModel} [instance] Model that this relation is created for. If no model is supplied,
- * Relation just tries to instantiate it's `reverseRelation` if specified, and bails out after that.
- * @param {Object} options
- * @param {string} options.key
- * @param {Backbone.RelationalModel.constructor} options.relatedModel
- * @param {Boolean|String} [options.includeInJSON=true] Serialize the given attribute for related model(s)' in toJSON, or just their ids.
- * @param {Boolean} [options.createModels=true] Create objects from the contents of keys if the object is not found in Backbone.store.
- * @param {Object} [options.reverseRelation] Specify a bi-directional relation. If provided, Relation will reciprocate
- * the relation to the 'relatedModel'. Required and optional properties match 'options', except that it also needs
- * {Backbone.Relation|String} type ('HasOne' or 'HasMany').
- * @param {Object} opts
- */
- Backbone.Relation = function( instance, options, opts ) {
- this.instance = instance;
- // Make sure 'options' is sane, and fill with defaults from subclasses and this object's prototype
- options = _.isObject( options ) ? options : {};
- this.reverseRelation = _.defaults( options.reverseRelation || {}, this.options.reverseRelation );
- this.options = _.defaults( options, this.options, Backbone.Relation.prototype.options );
-
- this.reverseRelation.type = !_.isString( this.reverseRelation.type ) ? this.reverseRelation.type :
- Backbone[ this.reverseRelation.type ] || Backbone.Relational.store.getObjectByName( this.reverseRelation.type );
-
- this.key = this.options.key;
- this.keySource = this.options.keySource || this.key;
- this.keyDestination = this.options.keyDestination || this.keySource || this.key;
-
- this.model = this.options.model || this.instance.constructor;
- this.relatedModel = this.options.relatedModel;
- if ( _.isString( this.relatedModel ) ) {
- this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
- }
-
- if ( !this.checkPreconditions() ) {
- return;
- }
-
- // Add the reverse relation on 'relatedModel' to the store's reverseRelations
- if ( !this.options.isAutoRelation && this.reverseRelation.type && this.reverseRelation.key ) {
- Backbone.Relational.store.addReverseRelation( _.defaults( {
- isAutoRelation: true,
- model: this.relatedModel,
- relatedModel: this.model,
- reverseRelation: this.options // current relation is the 'reverseRelation' for its own reverseRelation
- },
- this.reverseRelation // Take further properties from this.reverseRelation (type, key, etc.)
- ) );
- }
-
- if ( instance ) {
- var contentKey = this.keySource;
- if ( contentKey !== this.key && typeof this.instance.get( this.key ) === 'object' ) {
- contentKey = this.key;
- }
-
- this.setKeyContents( this.instance.get( contentKey ) );
- this.relatedCollection = Backbone.Relational.store.getCollection( this.relatedModel );
-
- // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
- if ( this.keySource !== this.key ) {
- this.instance.unset( this.keySource, { silent: true } );
- }
-
- // Add this Relation to instance._relations
- this.instance._relations[ this.key ] = this;
-
- this.initialize( opts );
-
- if ( this.options.autoFetch ) {
- this.instance.fetchRelated( this.key, _.isObject( this.options.autoFetch ) ? this.options.autoFetch : {} );
- }
-
- // When 'relatedModel' are created or destroyed, check if it affects this relation.
- this.listenTo( this.instance, 'destroy', this.destroy )
- .listenTo( this.relatedCollection, 'relational:add relational:change:id', this.tryAddRelated )
- .listenTo( this.relatedCollection, 'relational:remove', this.removeRelated )
- }
- };
- // Fix inheritance :\
- Backbone.Relation.extend = Backbone.Model.extend;
- // Set up all inheritable **Backbone.Relation** properties and methods.
- _.extend( Backbone.Relation.prototype, Backbone.Events, Backbone.Semaphore, {
- options: {
- createModels: true,
- includeInJSON: true,
- isAutoRelation: false,
- autoFetch: false,
- parse: false
- },
-
- instance: null,
- key: null,
- keyContents: null,
- relatedModel: null,
- relatedCollection: null,
- reverseRelation: null,
- related: null,
-
- /**
- * Check several pre-conditions.
- * @return {Boolean} True if pre-conditions are satisfied, false if they're not.
- */
- checkPreconditions: function() {
- var i = this.instance,
- k = this.key,
- m = this.model,
- rm = this.relatedModel,
- warn = Backbone.Relational.showWarnings && typeof console !== 'undefined';
-
- if ( !m || !k || !rm ) {
- warn && console.warn( 'Relation=%o: missing model, key or relatedModel (%o, %o, %o).', this, m, k, rm );
- return false;
- }
- // Check if the type in 'model' inherits from Backbone.RelationalModel
- if ( !( m.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: model does not inherit from Backbone.RelationalModel (%o).', this, i );
- return false;
- }
- // Check if the type in 'relatedModel' inherits from Backbone.RelationalModel
- if ( !( rm.prototype instanceof Backbone.RelationalModel ) ) {
- warn && console.warn( 'Relation=%o: relatedModel does not inherit from Backbone.RelationalModel (%o).', this, rm );
- return false;
- }
- // Check if this is not a HasMany, and the reverse relation is HasMany as well
- if ( this instanceof Backbone.HasMany && this.reverseRelation.type === Backbone.HasMany ) {
- warn && console.warn( 'Relation=%o: relation is a HasMany, and the reverseRelation is HasMany as well.', this );
- return false;
- }
- // Check if we're not attempting to create a relationship on a `key` that's already used.
- if ( i && _.keys( i._relations ).length ) {
- var existing = _.find( i._relations, function( rel ) {
- return rel.key === k;
- }, this );
-
- if ( existing ) {
- warn && console.warn( 'Cannot create relation=%o on %o for model=%o: already taken by relation=%o.',
- this, k, i, existing );
- return false;
- }
- }
-
- return true;
- },
-
- /**
- * Set the related model(s) for this relation
- * @param {Backbone.Model|Backbone.Collection} related
- */
- setRelated: function( related ) {
- this.related = related;
-
- this.instance.acquire();
- this.instance.attributes[ this.key ] = related;
- this.instance.release();
- },
-
- /**
- * Determine if a relation (on a different RelationalModel) is the reverse
- * relation of the current one.
- * @param {Backbone.Relation} relation
- * @return {Boolean}
- */
- _isReverseRelation: function( relation ) {
- return relation.instance instanceof this.relatedModel && this.reverseRelation.key === relation.key &&
- this.key === relation.reverseRelation.key;
- },
-
- /**
- * Get the reverse relations (pointing back to 'this.key' on 'this.instance') for the currently related model(s).
- * @param {Backbone.RelationalModel} [model] Get the reverse relations for a specific model.
- * If not specified, 'this.related' is used.
- * @return {Backbone.Relation[]}
- */
- getReverseRelations: function( model ) {
- var reverseRelations = [];
- // Iterate over 'model', 'this.related.models' (if this.related is a Backbone.Collection), or wrap 'this.related' in an array.
- var models = !_.isUndefined( model ) ? [ model ] : this.related && ( this.related.models || [ this.related ] );
- _.each( models || [], function( related ) {
- _.each( related.getRelations() || [], function( relation ) {
- if ( this._isReverseRelation( relation ) ) {
- reverseRelations.push( relation );
- }
- }, this );
- }, this );
-
- return reverseRelations;
- },
-
- /**
- * When `this.instance` is destroyed, cleanup our relations.
- * Get reverse relation, call removeRelated on each.
- */
- destroy: function() {
- this.stopListening();
-
- if ( this instanceof Backbone.HasOne ) {
- this.setRelated( null );
- }
- else if ( this instanceof Backbone.HasMany ) {
- this.setRelated( this._prepareCollection() );
- }
-
- _.each( this.getReverseRelations(), function( relation ) {
- relation.removeRelated( this.instance );
- }, this );
- }
- });
-
- Backbone.HasOne = Backbone.Relation.extend({
- options: {
- reverseRelation: { type: 'HasMany' }
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- var related = this.findRelated( opts );
- this.setRelated( related );
-
- // Notify new 'related' object of the new relation.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, opts );
- }, this );
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- if ( this.keyContents instanceof this.relatedModel ) {
- related = this.keyContents;
- }
- else if ( this.keyContents || this.keyContents === 0 ) { // since 0 can be a valid `id` as well
- var opts = _.defaults( { create: this.options.createModels }, options );
- related = this.relatedModel.findOrCreate( this.keyContents, opts );
- }
-
- // Nullify `keyId` if we have a related model; in case it was already part of the relation
- if ( this.related ) {
- this.keyId = null;
- }
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to an `id`, for easier comparison
- * @param {String|Number|Backbone.Model} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents;
- this.keyId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, this.keyContents );
- },
-
- /**
- * Event handler for `change:<key>`.
- * If the key is changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- // Don't accept recursive calls to onChange (like onChange->findRelated->findOrCreate->initializeRelations->addRelated->onChange)
- if ( this.isLocked() ) {
- return;
- }
- this.acquire();
- options = options ? _.clone( options ) : {};
-
- // 'options.__related' is set by 'addRelated'/'removeRelated'. If it is set, the change
- // is the result of a call from a relation. If it's not, the change is the result of
- // a 'set' call on this.instance.
- var changed = _.isUndefined( options.__related ),
- oldRelated = changed ? this.related : options.__related;
-
- if ( changed ) {
- this.setKeyContents( attr );
- var related = this.findRelated( options );
- this.setRelated( related );
- }
-
- // Notify old 'related' object of the terminated relation
- if ( oldRelated && this.related !== oldRelated ) {
- _.each( this.getReverseRelations( oldRelated ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
- }
-
- // Notify new 'related' object of the new relation. Note we do re-apply even if this.related is oldRelated;
- // that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'.
- // In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet.
- _.each( this.getReverseRelations(), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Fire the 'change:<key>' event if 'related' was updated
- if ( !options.silent && this.related !== oldRelated ) {
- var dit = this;
- this.changed = true;
- Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- });
- }
- this.release();
- },
-
- /**
- * If a new 'this.relatedModel' appears in the 'store', try to match it to the last set 'keyContents'
- */
- tryAddRelated: function( model, coll, options ) {
- if ( ( this.keyId || this.keyId === 0 ) && model.id === this.keyId ) { // since 0 can be a valid `id` as well
- this.addRelated( model, options );
- this.keyId = null;
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( model !== dit.related ) {
- var oldRelated = dit.related || null;
- dit.setRelated( model );
- dit.onChange( dit.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( !this.related ) {
- return;
- }
-
- if ( model === this.related ) {
- var oldRelated = this.related || null;
- this.setRelated( null );
- this.onChange( this.instance, model, _.defaults( { __related: oldRelated }, options ) );
- }
- }
- });
-
- Backbone.HasMany = Backbone.Relation.extend({
- collectionType: null,
-
- options: {
- reverseRelation: { type: 'HasOne' },
- collectionType: Backbone.Collection,
- collectionKey: true,
- collectionOptions: {}
- },
-
- initialize: function( opts ) {
- this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
-
- // Handle a custom 'collectionType'
- this.collectionType = this.options.collectionType;
- if ( _.isString( this.collectionType ) ) {
- this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
- }
- if ( this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
- throw new Error( '`collectionType` must inherit from Backbone.Collection' );
- }
-
- var related = this.findRelated( opts );
- this.setRelated( related );
- },
-
- /**
- * Bind events and setup collectionKeys for a collection that is to be used as the backing store for a HasMany.
- * If no 'collection' is supplied, a new collection will be created of the specified 'collectionType' option.
- * @param {Backbone.Collection} [collection]
- * @return {Backbone.Collection}
- */
- _prepareCollection: function( collection ) {
- if ( this.related ) {
- this.stopListening( this.related );
- }
-
- if ( !collection || !( collection instanceof Backbone.Collection ) ) {
- var options = _.isFunction( this.options.collectionOptions ) ?
- this.options.collectionOptions( this.instance ) : this.options.collectionOptions;
-
- collection = new this.collectionType( null, options );
- }
-
- collection.model = this.relatedModel;
-
- if ( this.options.collectionKey ) {
- var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
-
- if ( collection[ key ] && collection[ key ] !== this.instance ) {
- if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
- console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
- }
- }
- else if ( key ) {
- collection[ key ] = this.instance;
- }
- }
-
- this.listenTo( collection, 'relational:add', this.handleAddition )
- .listenTo( collection, 'relational:remove', this.handleRemoval )
- .listenTo( collection, 'relational:reset', this.handleReset );
-
- return collection;
- },
-
- /**
- * Find related Models.
- * @param {Object} [options]
- * @return {Backbone.Collection}
- */
- findRelated: function( options ) {
- var related = null;
-
- options = _.defaults( { parse: this.options.parse }, options );
-
- // Replace 'this.related' by 'this.keyContents' if it is a Backbone.Collection
- if ( this.keyContents instanceof Backbone.Collection ) {
- this._prepareCollection( this.keyContents );
- related = this.keyContents;
- }
- // Otherwise, 'this.keyContents' should be an array of related object ids.
- // Re-use the current 'this.related' if it is a Backbone.Collection; otherwise, create a new collection.
- else {
- var toAdd = [];
-
- _.each( this.keyContents, function( attributes ) {
- if ( attributes instanceof this.relatedModel ) {
- var model = attributes;
- }
- else {
- // If `merge` is true, update models here, instead of during update.
- model = this.relatedModel.findOrCreate( attributes,
- _.extend( { merge: true }, options, { create: this.options.createModels } )
- );
- }
-
- model && toAdd.push( model );
- }, this );
-
- if ( this.related instanceof Backbone.Collection ) {
- related = this.related;
- }
- else {
- related = this._prepareCollection();
- }
-
- // By now, both `merge` and `parse` will already have been executed for models if they were specified.
- // Disable them to prevent additional calls.
- related.set( toAdd, _.defaults( { merge: false, parse: false }, options ) );
- }
-
- // Remove entries from `keyIds` that were already part of the relation (and are thus 'unchanged')
- this.keyIds = _.difference( this.keyIds, _.pluck( related.models, 'id' ) );
-
- return related;
- },
-
- /**
- * Normalize and reduce `keyContents` to a list of `ids`, for easier comparison
- * @param {String|Number|String[]|Number[]|Backbone.Collection} keyContents
- */
- setKeyContents: function( keyContents ) {
- this.keyContents = keyContents instanceof Backbone.Collection ? keyContents : null;
- this.keyIds = [];
-
- if ( !this.keyContents && ( keyContents || keyContents === 0 ) ) { // since 0 can be a valid `id` as well
- // Handle cases the an API/user supplies just an Object/id instead of an Array
- this.keyContents = _.isArray( keyContents ) ? keyContents : [ keyContents ];
-
- _.each( this.keyContents, function( item ) {
- var itemId = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
- if ( itemId || itemId === 0 ) {
- this.keyIds.push( itemId );
- }
- }, this );
- }
- },
-
- /**
- * Event handler for `change:<key>`.
- * If the contents of the key are changed, notify old & new reverse relations and initialize the new relation.
- */
- onChange: function( model, attr, options ) {
- options = options ? _.clone( options ) : {};
- this.setKeyContents( attr );
- this.changed = false;
-
- var related = this.findRelated( options );
- this.setRelated( related );
-
- if ( !options.silent ) {
- var dit = this;
- Backbone.Relational.eventQueue.add( function() {
- // The `changed` flag can be set in `handleAddition` or `handleRemoval`
- if ( dit.changed ) {
- dit.instance.trigger( 'change:' + dit.key, dit.instance, dit.related, options, true );
- dit.changed = false;
- }
- });
- }
- },
-
- /**
- * When a model is added to a 'HasMany', trigger 'add' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', must set 'this.instance' as their related).
- */
- handleAddition: function( model, coll, options ) {
- //console.debug('handleAddition called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.addRelated( this.instance, options );
- }, this );
-
- // Only trigger 'add' once the newly added model is initialized (so, has its relations set up)
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'add:' + dit.key, model, dit.related, options );
- });
- },
-
- /**
- * When a model is removed from a 'HasMany', trigger 'remove' on 'this.instance' and notify reverse relations.
- * (should be 'HasOne', which should be nullified)
- */
- handleRemoval: function( model, coll, options ) {
- //console.debug('handleRemoval called; args=%o', arguments);
- options = options ? _.clone( options ) : {};
- this.changed = true;
-
- _.each( this.getReverseRelations( model ), function( relation ) {
- relation.removeRelated( this.instance, null, options );
- }, this );
-
- var dit = this;
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
- });
- },
-
- handleReset: function( coll, options ) {
- var dit = this;
- options = options ? _.clone( options ) : {};
- !options.silent && Backbone.Relational.eventQueue.add( function() {
- dit.instance.trigger( 'reset:' + dit.key, dit.related, options );
- });
- },
-
- tryAddRelated: function( model, coll, options ) {
- var item = _.contains( this.keyIds, model.id );
-
- if ( item ) {
- this.addRelated( model, options );
- this.keyIds = _.without( this.keyIds, model.id );
- }
- },
-
- addRelated: function( model, options ) {
- // Allow 'model' to set up its relations before proceeding.
- // (which can result in a call to 'addRelated' from a relation of 'model')
- var dit = this;
- model.queue( function() {
- if ( dit.related && !dit.related.get( model ) ) {
- dit.related.add( model, _.defaults( { parse: false }, options ) );
- }
- });
- },
-
- removeRelated: function( model, coll, options ) {
- if ( this.related.get( model ) ) {
- this.related.remove( model, options );
- }
- }
- });
-
- /**
- * A type of Backbone.Model that also maintains relations to other models and collections.
- * New events when compared to the original:
- * - 'add:<key>' (model, related collection, options)
- * - 'remove:<key>' (model, related collection, options)
- * - 'change:<key>' (model, related model or collection, options)
- */
- Backbone.RelationalModel = Backbone.Model.extend({
- relations: null, // Relation descriptions on the prototype
- _relations: null, // Relation instances
- _isInitialized: false,
- _deferProcessing: false,
- _queue: null,
-
- subModelTypeAttribute: 'type',
- subModelTypes: null,
-
- constructor: function( attributes, options ) {
- // Nasty hack, for cases like 'model.get( <HasMany key> ).add( item )'.
- // Defer 'processQueue', so that when 'Relation.createModels' is used we trigger 'HasMany'
- // collection events only after the model is really fully set up.
- // Example: event for "p.on( 'add:jobs' )" -> "p.get('jobs').add( { company: c.id, person: p.id } )".
- if ( options && options.collection ) {
- var dit = this,
- collection = this.collection = options.collection;
-
- // Prevent `collection` from cascading down to nested models; they shouldn't go into this `if` clause.
- delete options.collection;
-
- this._deferProcessing = true;
-
- var processQueue = function( model ) {
- if ( model === dit ) {
- dit._deferProcessing = false;
- dit.processQueue();
- collection.off( 'relational:add', processQueue );
- }
- };
- collection.on( 'relational:add', processQueue );
-
- // So we do process the queue eventually, regardless of whether this model actually gets added to 'options.collection'.
- _.defer( function() {
- processQueue( dit );
- });
- }
-
- Backbone.Relational.store.processOrphanRelations();
-
- this._queue = new Backbone.BlockingQueue();
- this._queue.block();
- Backbone.Relational.eventQueue.block();
-
- try {
- Backbone.Model.apply( this, arguments );
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
- },
-
- /**
- * Override 'trigger' to queue 'change' and 'change:*' events
- */
- trigger: function( eventName ) {
- if ( eventName.length > 5 && eventName.indexOf( 'change' ) === 0 ) {
- var dit = this,
- args = arguments;
-
- Backbone.Relational.eventQueue.add( function() {
- if ( !dit._isInitialized ) {
- return;
- }
-
- // Determine if the `change` event is still valid, now that all relations are populated
- var changed = true;
- if ( eventName === 'change' ) {
- changed = dit.hasChanged();
- }
- else {
- var attr = eventName.slice( 7 ),
- rel = dit.getRelation( attr );
-
- if ( rel ) {
- // If `attr` is a relation, `change:attr` get triggered from `Relation.onChange`.
- // These take precedence over `change:attr` events triggered by `Model.set`.
- // The relation set a fourth attribute to `true`. If this attribute is present,
- // continue triggering this event; otherwise, it's from `Model.set` and should be stopped.
- changed = ( args[ 4 ] === true );
-
- // If this event was triggered by a relation, set the right value in `this.changed`
- // (a Collection or Model instead of raw data).
- if ( changed ) {
- dit.changed[ attr ] = args[ 2 ];
- }
- // Otherwise, this event is from `Model.set`. If the relation doesn't report a change,
- // remove attr from `dit.changed` so `hasChanged` doesn't take it into account.
- else if ( !rel.changed ) {
- delete dit.changed[ attr ];
- }
- }
- }
-
- changed && Backbone.Model.prototype.trigger.apply( dit, args );
- });
- }
- else {
- Backbone.Model.prototype.trigger.apply( this, arguments );
- }
-
- return this;
- },
-
- /**
- * Initialize Relations present in this.relations; determine the type (HasOne/HasMany), then creates a new instance.
- * Invoked in the first call so 'set' (which is made from the Backbone.Model constructor).
- */
- initializeRelations: function( options ) {
- this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
- this._relations = {};
-
- _.each( this.relations || [], function( rel ) {
- Backbone.Relational.store.initializeRelation( this, rel, options );
- }, this );
-
- this._isInitialized = true;
- this.release();
- this.processQueue();
- },
-
- /**
- * When new values are set, notify this model's relations (also if options.silent is set).
- * (Relation.setRelated locks this model before calling 'set' on it to prevent loops)
- */
- updateRelations: function( options ) {
- if ( this._isInitialized && !this.isLocked() ) {
- _.each( this._relations, function( rel ) {
- // Update from data in `rel.keySource` if set, or `rel.key` otherwise
- var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
- if ( rel.related !== val ) {
- this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
- }
- }, this );
- }
- },
-
- /**
- * Either add to the queue (if we're not initialized yet), or execute right away.
- */
- queue: function( func ) {
- this._queue.add( func );
- },
-
- /**
- * Process _queue
- */
- processQueue: function() {
- if ( this._isInitialized && !this._deferProcessing && this._queue.isBlocked() ) {
- this._queue.unblock();
- }
- },
-
- /**
- * Get a specific relation.
- * @param key {string} The relation key to look for.
- * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
- */
- getRelation: function( key ) {
- return this._relations[ key ];
- },
-
- /**
- * Get all of the created relations.
- * @return {Backbone.Relation[]}
- */
- getRelations: function() {
- return _.values( this._relations );
- },
-
- /**
- * Retrieve related objects.
- * @param key {string} The relation key to fetch models for.
- * @param [options] {Object} Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
- * @param [refresh=false] {boolean} Fetch existing models from the server as well (in order to update them).
- * @return {jQuery.when[]} An array of request objects
- */
- fetchRelated: function( key, options, refresh ) {
- // Set default `options` for fetch
- options = _.extend( { update: true, remove: false }, options );
-
- var setUrl,
- requests = [],
- rel = this.getRelation( key ),
- idsToFetch = rel && ( rel.keyIds || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
-
- // On `refresh`, add the ids for current models in the relation to `idsToFetch`
- if ( refresh ) {
- var models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
- _.each( models, function( model ) {
- if ( model.id || model.id === 0 ) {
- idsToFetch.push( model.id );
- }
- });
- }
-
- if ( idsToFetch && idsToFetch.length ) {
- // Find (or create) a model for each one that is to be fetched
- var created = [],
- models = _.map( idsToFetch, function( id ) {
- var model = Backbone.Relational.store.find( rel.relatedModel, id );
-
- if ( !model ) {
- var attrs = {};
- attrs[ rel.relatedModel.prototype.idAttribute ] = id;
- model = rel.relatedModel.findOrCreate( attrs, options );
- created.push( model );
- }
-
- return model;
- }, this );
-
- // Try if the 'collection' can provide a url to fetch a set of models in one request.
- if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
- setUrl = rel.related.url( models );
- }
-
- // An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
- // To make sure it can, test if the url we got by supplying a list of models to fetch is different from
- // the one supplied for the default fetch action (without args to 'url').
- if ( setUrl && setUrl !== rel.related.url() ) {
- var opts = _.defaults(
- {
- error: function() {
- var args = arguments;
- _.each( created, function( model ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, args );
- });
- },
- url: setUrl
- },
- options
- );
-
- requests = [ rel.related.fetch( opts ) ];
- }
- else {
- requests = _.map( models, function( model ) {
- var opts = _.defaults(
- {
- error: function() {
- if ( _.contains( created, model ) ) {
- model.trigger( 'destroy', model, model.collection, options );
- options.error && options.error.apply( model, arguments );
- }
- }
- },
- options
- );
- return model.fetch( opts );
- }, this );
- }
- }
-
- return requests;
- },
-
- get: function( attr ) {
- var originalResult = Backbone.Model.prototype.get.call( this, attr );
-
- // Use `originalResult` get if dotNotation not enabled or not required because no dot is in `attr`
- if ( !this.dotNotation || attr.indexOf( '.' ) === -1 ) {
- return originalResult;
- }
-
- // Go through all splits and return the final result
- var splits = attr.split( '.' );
- var result = _.reduce(splits, function( model, split ) {
- if ( !( model instanceof Backbone.Model ) ) {
- throw new Error( 'Attribute must be an instanceof Backbone.Model. Is: ' + model + ', currentSplit: ' + split );
- }
-
- return Backbone.Model.prototype.get.call( model, split );
- }, this );
-
- if ( originalResult !== undefined && result !== undefined ) {
- throw new Error( "Ambiguous result for '" + attr + "'. direct result: " + originalResult + ", dotNotation: " + result );
- }
-
- return originalResult || result;
- },
-
- set: function( key, value, options ) {
- Backbone.Relational.eventQueue.block();
-
- // Duplicate backbone's behavior to allow separate key/value parameters, instead of a single 'attributes' object
- var attributes;
- if ( _.isObject( key ) || key == null ) {
- attributes = key;
- options = value;
- }
- else {
- attributes = {};
- attributes[ key ] = value;
- }
-
- try {
- var id = this.id,
- newId = attributes && this.idAttribute in attributes && attributes[ this.idAttribute ];
-
- // Check if we're not setting a duplicate id before actually calling `set`.
- Backbone.Relational.store.checkId( this, newId );
-
- var result = Backbone.Model.prototype.set.apply( this, arguments );
-
- // Ideal place to set up relations, if this is the first time we're here for this model
- if ( !this._isInitialized && !this.isLocked() ) {
- this.constructor.initializeModelHierarchy();
- Backbone.Relational.store.register( this );
- this.initializeRelations( options );
- }
- // The store should know about an `id` update asap
- else if ( newId && newId !== id ) {
- Backbone.Relational.store.update( this );
- }
-
- if ( attributes ) {
- this.updateRelations( options );
- }
- }
- finally {
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
- }
-
- return result;
- },
-
- unset: function( attribute, options ) {
- Backbone.Relational.eventQueue.block();
-
- var result = Backbone.Model.prototype.unset.apply( this, arguments );
- this.updateRelations( options );
-
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
-
- return result;
- },
-
- clear: function( options ) {
- Backbone.Relational.eventQueue.block();
-
- var result = Backbone.Model.prototype.clear.apply( this, arguments );
- this.updateRelations( options );
-
- // Try to run the global queue holding external events
- Backbone.Relational.eventQueue.unblock();
-
- return result;
- },
-
- clone: function() {
- var attributes = _.clone( this.attributes );
- if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
- attributes[ this.idAttribute ] = null;
- }
-
- _.each( this.getRelations(), function( rel ) {
- delete attributes[ rel.key ];
- });
-
- return new this.constructor( attributes );
- },
-
- /**
- * Convert relations to JSON, omits them when required
- */
- toJSON: function( options ) {
- // If this Model has already been fully serialized in this branch once, return to avoid loops
- if ( this.isLocked() ) {
- return this.id;
- }
-
- this.acquire();
- var json = Backbone.Model.prototype.toJSON.call( this, options );
-
- if ( this.constructor._superModel && !( this.constructor._subModelTypeAttribute in json ) ) {
- json[ this.constructor._subModelTypeAttribute ] = this.constructor._subModelTypeValue;
- }
-
- _.each( this._relations, function( rel ) {
- var related = json[ rel.key ],
- includeInJSON = rel.options.includeInJSON,
- value = null;
-
- if ( includeInJSON === true ) {
- if ( related && _.isFunction( related.toJSON ) ) {
- value = related.toJSON( options );
- }
- }
- else if ( _.isString( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = related.pluck( includeInJSON );
- }
- else if ( related instanceof Backbone.Model ) {
- value = related.get( includeInJSON );
- }
-
- // Add ids for 'unfound' models if includeInJSON is equal to (only) the relatedModel's `idAttribute`
- if ( includeInJSON === rel.relatedModel.prototype.idAttribute ) {
- if ( rel instanceof Backbone.HasMany ) {
- value = value.concat( rel.keyIds );
- }
- else if ( rel instanceof Backbone.HasOne ) {
- value = value || rel.keyId;
- }
- }
- }
- else if ( _.isArray( includeInJSON ) ) {
- if ( related instanceof Backbone.Collection ) {
- value = [];
- related.each( function( model ) {
- var curJson = {};
- _.each( includeInJSON, function( key ) {
- curJson[ key ] = model.get( key );
- });
- value.push( curJson );
- });
- }
- else if ( related instanceof Backbone.Model ) {
- value = {};
- _.each( includeInJSON, function( key ) {
- value[ key ] = related.get( key );
- });
- }
- }
- else {
- delete json[ rel.key ];
- }
-
- if ( includeInJSON ) {
- json[ rel.keyDestination ] = value;
- }
-
- if ( rel.keyDestination !== rel.key ) {
- delete json[ rel.key ];
- }
- });
-
- this.release();
- return json;
- }
- },
- {
- /**
- *
- * @param superModel
- * @returns {Backbone.RelationalModel.constructor}
- */
- setup: function( superModel ) {
- // We don't want to share a relations array with a parent, as this will cause problems with
- // reverse relations.
- this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
-
- this._subModels = {};
- this._superModel = null;
-
- // If this model has 'subModelTypes' itself, remember them in the store
- if ( this.prototype.hasOwnProperty( 'subModelTypes' ) ) {
- Backbone.Relational.store.addSubModels( this.prototype.subModelTypes, this );
- }
- // The 'subModelTypes' property should not be inherited, so reset it.
- else {
- this.prototype.subModelTypes = null;
- }
-
- // Initialize all reverseRelations that belong to this new model.
- _.each( this.prototype.relations || [], function( rel ) {
- if ( !rel.model ) {
- rel.model = this;
- }
-
- if ( rel.reverseRelation && rel.model === this ) {
- var preInitialize = true;
- if ( _.isString( rel.relatedModel ) ) {
- /**
- * The related model might not be defined for two reasons
- * 1. it is related to itself
- * 2. it never gets defined, e.g. a typo
- * 3. the model hasn't been defined yet, but will be later
- * In neither of these cases do we need to pre-initialize reverse relations.
- * However, for 3. (which is, to us, indistinguishable from 2.), we do need to attempt
- * setting up this relation again later, in case the related model is defined later.
- */
- var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
- preInitialize = relatedModel && ( relatedModel.prototype instanceof Backbone.RelationalModel );
- }
-
- if ( preInitialize ) {
- Backbone.Relational.store.initializeRelation( null, rel );
- }
- else if ( _.isString( rel.relatedModel ) ) {
- Backbone.Relational.store.addOrphanRelation( rel );
- }
- }
- }, this );
-
- return this;
- },
-
- /**
- * Create a 'Backbone.Model' instance based on 'attributes'.
- * @param {Object} attributes
- * @param {Object} [options]
- * @return {Backbone.Model}
- */
- build: function( attributes, options ) {
- var model = this;
-
- // 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
- this.initializeModelHierarchy();
-
- // Determine what type of (sub)model should be built if applicable.
- // Lookup the proper subModelType in 'this._subModels'.
- if ( this._subModels && this.prototype.subModelTypeAttribute in attributes ) {
- var subModelTypeAttribute = attributes[ this.prototype.subModelTypeAttribute ];
- var subModelType = this._subModels[ subModelTypeAttribute ];
- if ( subModelType ) {
- model = subModelType;
- }
- }
-
- return new model( attributes, options );
- },
-
- /**
- *
- */
- initializeModelHierarchy: function() {
- // If we're here for the first time, try to determine if this modelType has a 'superModel'.
- if ( _.isUndefined( this._superModel ) || _.isNull( this._superModel ) ) {
- Backbone.Relational.store.setupSuperModel( this );
-
- // If a superModel has been found, copy relations from the _superModel if they haven't been
- // inherited automatically (due to a redefinition of 'relations').
- // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail
- // the isUndefined/isNull check next time.
- if ( this._superModel && this._superModel.prototype.relations ) {
- // Find relations that exist on the `_superModel`, but not yet on this model.
- var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
- return !_.any( this.prototype.relations || [], function( rel ) {
- return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
- }, this );
- }, this );
-
- this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
- }
- else {
- this._superModel = false;
- }
- }
-
- // If we came here through 'build' for a model that has 'subModelTypes', and not all of them have been resolved yet, try to resolve each.
- if ( this.prototype.subModelTypes && _.keys( this.prototype.subModelTypes ).length !== _.keys( this._subModels ).length ) {
- _.each( this.prototype.subModelTypes || [], function( subModelTypeName ) {
- var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
- subModelType && subModelType.initializeModelHierarchy();
- });
- }
- },
-
- /**
- * Find an instance of `this` type in 'Backbone.Relational.store'.
- * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
- * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
- * Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
- * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
- * @param {Object} [options]
- * @param {Boolean} [options.create=true]
- * @param {Boolean} [options.merge=true]
- * @param {Boolean} [options.parse=false]
- * @return {Backbone.RelationalModel}
- */
- findOrCreate: function( attributes, options ) {
- options || ( options = {} );
- var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
- this.prototype.parse( attributes ) : attributes;
-
- // Try to find an instance of 'this' model type in the store
- var model = Backbone.Relational.store.find( this, parsedAttributes );
-
- // If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
- // If not, create an instance (unless 'options.create' is false).
- if ( _.isObject( attributes ) ) {
- if ( model && options.merge !== false ) {
- // Make sure `options.collection` doesn't cascade to nested models
- delete options.collection;
-
- model.set( parsedAttributes, options );
- }
- else if ( !model && options.create !== false ) {
- model = this.build( attributes, options );
- }
- }
-
- return model;
- }
- });
- _.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
-
- /**
- * Override Backbone.Collection._prepareModel, so objects will be built using the correct type
- * if the collection.model has subModels.
- * Attempts to find a model for `attrs` in Backbone.store through `findOrCreate`
- * (which sets the new properties on it if found), or instantiates a new model.
- */
- Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
- Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
- var model;
-
- if ( attrs instanceof Backbone.Model ) {
- if ( !attrs.collection ) {
- attrs.collection = this;
- }
- model = attrs;
- }
- else {
- options || ( options = {} );
- options.collection = this;
-
- if ( typeof this.model.findOrCreate !== 'undefined' ) {
- model = this.model.findOrCreate( attrs, options );
- }
- else {
- model = new this.model( attrs, options );
- }
-
- if ( model && model.isNew() && !model._validate( attrs, options ) ) {
- this.trigger( 'invalid', this, attrs, options );
- model = false;
- }
- }
-
- return model;
- };
-
-
- /**
- * Override Backbone.Collection.set, so we'll create objects from attributes where required,
- * and update the existing models. Also, trigger 'relational:add'.
- */
- var set = Backbone.Collection.prototype.__set = Backbone.Collection.prototype.set;
- Backbone.Collection.prototype.set = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return set.apply( this, arguments );
- }
-
- if ( options && options.parse ) {
- models = this.parse( models, options );
- }
-
- if ( !_.isArray( models ) ) {
- models = models ? [ models ] : [];
- }
-
- var newModels = [],
- toAdd = [];
-
- //console.debug( 'calling add on coll=%o; model=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- if ( !( model instanceof Backbone.Model ) ) {
- model = Backbone.Collection.prototype._prepareModel.call( this, model, options );
- }
-
- if ( model ) {
- toAdd.push( model );
-
- if ( !( this.get( model ) || this.get( model.cid ) ) ) {
- newModels.push( model );
- }
- // If we arrive in `add` while performing a `set` (after a create, so the model gains an `id`),
- // we may get here before `_onModelEvent` has had the chance to update `_byId`.
- else if ( model.id != null ) {
- this._byId[ model.id ] = model;
- }
- }
- }, this );
-
- // Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
- // If `parse` was specified, the collection and contained models have been parsed now.
- set.call( this, toAdd, _.defaults( { parse: false }, options ) );
-
- _.each( newModels, function( model ) {
- // Fire a `relational:add` event for any model in `newModels` that has actually been added to the collection.
- if ( this.get( model ) || this.get( model.cid ) ) {
- this.trigger( 'relational:add', model, this, options );
- }
- }, this );
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.remove' to trigger 'relational:remove'.
- */
- var remove = Backbone.Collection.prototype.__remove = Backbone.Collection.prototype.remove;
- Backbone.Collection.prototype.remove = function( models, options ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return remove.apply( this, arguments );
- }
-
- models = _.isArray( models ) ? models.slice() : [ models ];
- options || ( options = {} );
-
- var toRemove = [];
-
- //console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
- _.each( models, function( model ) {
- model = this.get( model ) || this.get( model.cid );
- model && toRemove.push( model );
- }, this );
-
- if ( toRemove.length ) {
- remove.call( this, toRemove, options );
-
- _.each( toRemove, function( model ) {
- this.trigger('relational:remove', model, this, options);
- }, this );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.reset' to trigger 'relational:reset'.
- */
- var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
- Backbone.Collection.prototype.reset = function( models, options ) {
- options = _.extend( { merge: true }, options );
- reset.call( this, models, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.sort' to trigger 'relational:reset'.
- */
- var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
- Backbone.Collection.prototype.sort = function( options ) {
- sort.call( this, options );
-
- if ( this.model.prototype instanceof Backbone.RelationalModel ) {
- this.trigger( 'relational:reset', this, options );
- }
-
- return this;
- };
-
- /**
- * Override 'Backbone.Collection.trigger' so 'add', 'remove' and 'reset' events are queued until relations
- * are ready.
- */
- var trigger = Backbone.Collection.prototype.__trigger = Backbone.Collection.prototype.trigger;
- Backbone.Collection.prototype.trigger = function( eventName ) {
- // Short-circuit if this Collection doesn't hold RelationalModels
- if ( !( this.model.prototype instanceof Backbone.RelationalModel ) ) {
- return trigger.apply( this, arguments );
- }
-
- if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' ) {
- var dit = this,
- args = arguments;
-
- if ( _.isObject( args[ 3 ] ) ) {
- args = _.toArray( args );
- // the fourth argument is the option object.
- // we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
- args[ 3 ] = _.clone( args[ 3 ] );
- }
-
- Backbone.Relational.eventQueue.add( function() {
- trigger.apply( dit, args );
- });
- }
- else {
- trigger.apply( this, arguments );
- }
-
- return this;
- };
-
- // Override .extend() to automatically call .setup()
- Backbone.RelationalModel.extend = function( protoProps, classProps ) {
- var child = Backbone.Model.extend.apply( this, arguments );
-
- child.setup( this );
-
- return child;
- };
-})();
diff --git a/go/base/static/js/vendor/backbone-rpc-0.1.1.js b/go/base/static/js/vendor/backbone-rpc-0.1.1.js
deleted file mode 100644
index c33c42e..0000000
--- a/go/base/static/js/vendor/backbone-rpc-0.1.1.js
+++ /dev/null
@@ -1,444 +0,0 @@
-/*! Backbone.Rpc - v0.1.1
-------------------------------
-Build @ 2012-11-15
-Documentation and Full License Available at:
-http://asciidisco.github.com/Backbone.Rpc/index.html
-git://github.com/asciidisco/Backbone.Rpc.git
-Copyright (c) 2012 Sebastian Golasch <[email protected]>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-
-Software is furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.*/
-
-// Backbone.Rpc
-// Plugin for using the backbone js library with a remote json-rpc handler
-// instead of the default REST one
-(function (root, define, require, exports, module, factory, undef) {
- 'use strict';
- if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like enviroments that support module.exports,
- // like Node.
- module.exports = factory(require('underscore'), require('backbone'), require('jquery'));
- } else if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['underscore', 'backbone', 'jquery'], function (_, Backbone, $) {
- // Check if
- _ = _ === undef ? root._ : _;
- Backbone = Backbone === undef ? root.Backbone : Backbone;
- $ = $ === undef ? root.$ : $;
- return (root.Backbone = factory(_, Backbone, $));
- });
- } else {
- // Browser globals
- root.returnExportsGlobal = factory(root._, root.Backbone, root.$);
- }
-}(this, this.define, this.require, this.exports, this.module, function (_, Backbone, $, undef) {
- 'use strict';
- var Rpc = function (options) {
- // merge the users options
- this.options = options !== undef ? options : {};
- // check if we have a non std. namespace delimter
- this.namespaceDelimiter = options !== undef && options.namespaceDelimiter !== undef ? options.namespaceDelimiter : this.namespaceDelimiter;
- // check if we have a non std. content-type
- this.contentType = options !== undef && options.contentType !== undef ? options.contentType : this.contentType;
- // fix issue with the loss of this
- _.bindAll(this);
- },
- // store the old Backbone.Model constructor for later use
- oldConst = Backbone.Model.prototype.constructor,
- // store the old Backbone.sync method for later use
- oldSync = Backbone.sync,
- // storage object to keep track of changes from the loaded objects
- storage = {};
-
- // TODO: Document
- Rpc.prototype = {
- // User defined options placeholder
- options: {},
-
- // Default charset
- charset: 'iso-8859-1',
-
- // Default namespace
- namespace: '',
-
- // Default namespace delimiter
- namespaceDelimiter: '/',
-
- // Default content type
- contentType: 'application/json',
-
- // User set url placeholder
- url: null,
-
- // Server response id
- responseID: null,
-
- // TODO: Document
- exceptions: {
- 404: {code: -1, message: '404'},
- 500: {code: -2, message: '500'},
- typeMissmatch: {code: -3, message: 'Type missmatch'},
- badResponseId: {code: -4, message: 'Bad response ID'},
- noResponse: {code: -5, message: 'No response'},
- noDefError: {code: -6, message: 'No error defined'},
- renderError: function (message, code) {
- return {code: (code !== undef ? -7 : code), message: (message ? 'No error defined' : message)};
- }
- },
-
- // TODO: Document
- onSuccess: function (callback, id, data) {
- // check if callback variable is a function
- if (_.isFunction(callback) === true) {
- // check if we have valid response data
- if (data === null || data === undef) {
- this.handleExceptions(this.exceptions.noResponse);
- return this;
- }
-
- // always only one parameter has value and second is null
- if (data !== null && id !== String(data.id)) {
- this.handleExceptions(this.exceptions.badResponseId);
- }
-
- // call the callback function with the result data
- callback.apply(this, [data.result, data.error]);
- } else {
- // fire an error
- this.onError(data);
- }
- },
-
- // TODO: Document
- onError: function (callback, data) {
- // check if we have valid response data
- if (data === null || data === undef) {
- this.handleExceptions(this.exceptions.noResponse);
- return this;
- }
-
- // check if we have an error object
- if (null !== data.error && undef !== data.error) {
- this.handleExceptions(data.error);
- } else {
- this.handleExceptions(this.exceptions.noDefError);
- }
- },
-
- // TODO: Document
- query: function (fn, params, callback) {
- var id = String((new Date()).getTime()),
- ret = null;
- this.responseID = id;
- // generate unique request id (timestamp)
- // check if params and the function name are ok, then...
- if (_.isArray(params) && _.isString(fn)) {
- // send query
- ret = $.ajax({
- contentType : this.contentType + '; charset=' + this.charset,
- type : 'POST',
- dataType : 'json',
- url : this.url,
- data : JSON.stringify({
- jsonrpc : '2.0',
- method : this.namespace + this.namespaceDelimiter + fn,
- id : id,
- params : params
- }),
- statusCode : {
- 404: _.bind(function () { this.handleExceptions(this.exceptions['404']); }, this),
- 500: _.bind(function () { this.handleExceptions(this.exceptions['500']); }, this)
- },
- success: _.bind(function (data, status, response) {
- if (data !== null && data.error !== undef) {
- this.onError(callback, data, status, response);
- } else {
- this.onSuccess(callback, id, data, status, response);
- }
- }, this),
- error: _.bind(function (jXhr, status, response) {
- if (jXhr.status !== 404 && jXhr.status !== 500) {
- this.onError(callback, jXhr, status, response);
- }
- }, this)
- });
- } else {
- ret = this.handleExceptions(this.exceptions.typeMissmatch);
- }
-
- return ret;
- },
-
- // TODO: Document
- checkMethods: function (cb, params, model, method, options, scb, ecb) {
- var definition = null,
- deeperNested = false,
- exec = null,
- valuableDefinition = [],
- changedAttributes = {},
- def = null;
-
- // rewrite method if name is delete
- method = method === 'delete' ? 'remove' : method;
-
- // check if we have a proper method for the model
- if (!_.isArray(model.methods[method]) && !_.isFunction(model.methods[method])) {
- return this.handleExceptions(this.exceptions.typeMissmatch);
- }
-
- // execute function if it´s one, else, assign array
- if (_.isFunction(model.methods[method])) {
- if (!_.isString(storage[model.get('_rpcId')])) {
- _.each(storage[model.get('_rpcId')], function (value, key) {
- if (model.get(key) !== value) {
- changedAttributes[key] = true;
- }
- });
- }
- storage[model.get('_rpcId')] = model.toJSON();
- definition = _.bind(model.methods[method], model)(changedAttributes, options);
- } else {
- definition = model.methods[method];
- }
-
- // check if array is deeper nested
- if (_.isArray(definition[0])) {
- deeperNested = true;
- }
-
- // execute a single call
- if (deeperNested !== true) {
- def = _.clone(definition);
- exec = def.shift();
- if (def.length > 0) {
- _.each(def, function (param) {
- if (param === '') {
- valuableDefinition.push('');
- } else {
- if (model instanceof Backbone.Collection) {
- if (model[param] !== undef) {
- if (_.isFunction(model[param])) {
- valuableDefinition.push(model[param]());
- } else {
- valuableDefinition.push(model[param]);
- }
- } else {
- if (options[param] !== undef) {
- valuableDefinition.push(options[param]);
- }
- }
- } else {
- if (model.get(param) !== undef) {
- valuableDefinition.push(model.get(param));
- } else {
- if (options[param] !== undef) {
- valuableDefinition.push(options[param]);
- }
- }
- }
- }
- });
- } else {
- valuableDefinition = [];
- }
-
- return cb(exec, valuableDefinition, scb, ecb);
- }
-
- // execute nested calls
- _.each(definition, function (localdef) {
- var def = _.clone(localdef);
- exec = null;
- valuableDefinition = [];
- exec = def.shift();
- _.each(def, function (param) {
- valuableDefinition.push(model.get(param));
- });
- return cb(exec, valuableDefinition, scb, ecb);
- });
-
- return null;
- },
-
- // TODO: Document
- invoke: function (method, model, options) {
- var defOpts = {
- success: function (result) {
- model.trigger('called:' + method, model, result);
- // check for a manually success callback
- if (options !== undef && _.isFunction(options.success)) {
- options.success(model, result);
- }
- },
- error: function (model, error) {
- model.trigger('error', model, error);
- model.trigger('error:' + method, model, error);
- // check for a manually success callback
- if (options !== undef && _.isFunction(options.error)) {
- options.error(model, error);
- }
- }
- };
-
- // sync the model
- Backbone.sync(method, model, defOpts);
- return this;
- },
-
- // Default exception handler
- defaultExceptionHandler: function (exception) {
- throw 'Error code: ' + exception.code + ' - message: ' + exception.message;
- },
-
- // Exception handler
- handleExceptions: function (exception) {
- var exceptionHandler = _.isFunction(this.options.exceptionHandler) ? this.options.exceptionHandler : this.defaultExceptionHandler;
- exceptionHandler.call(this, exception);
- return this;
- }
- };
-
- // assign rpc to backbone itself
- Backbone.Rpc = Rpc;
-
- // overwrite backbones model constructor
- Backbone.Model = Backbone.Model.extend({
- // TODO: Document
- constructor: function (model) {
- // check if the model has the rpc property and methods defined
- if (this.rpc !== undef && _.isFunction(this.rpc.invoke) === true && this.methods !== undef) {
- // walk through the methods
- _.each(this.methods, _.bind(function (method, signature) {
- // check if we have a 'non standard' signature
- if ({'read': 1, 'create': 1, 'remove': 1, 'update': 1}[signature] !== 1) {
- // generate the method for the signature
- this[signature] = _.bind(function (options) {
- // invoke the dynamicly created method
- this.rpc.invoke(signature, this, options);
- return this;
- }, this);
- }
- }, this));
- }
-
- // call the original constructor
- oldConst.apply(this, arguments);
- }
- });
-
- // overwrite backbones sync
- Backbone.sync = (function (Rpc) {
- // Generate a new Sync Method for JSON RPC Queuing
- var rpc = null,
- sync = function (method, model, options) {
- // Default success model callback
- var successCb = function (data, error) {
- // check if we have an error object
- if (error !== null && error !== undef) {
- options.error(model, error);
- return this;
- }
-
- // check if the rpc is used in a Backbone.Collection instance
- if (model instanceof Backbone.Collection) {
- // check if we have valid response data
- if (data !== undef && data !== null) {
- // clone the data and tag it to track changes
- if (typeof data[0] === 'object') {
- _.each(data, function (item, key) {
- item._rpcId = _.uniqueId('rpc_');
- data[key] = item;
- storage[item._rpcId] = item;
- });
- } else {
- _.each(data, function (item, key) {
- storage[key] = item;
- });
- }
- }
- }
-
- // clone and tag the data to track changes if we have a Backbone.Model instance
- if (model instanceof Backbone.Model && data !== undef && data !== null) {
- data._rpcId = _.uniqueId('rpc_');
- storage[data._rpcId] = data;
- }
-
- // change data attr to be an empty array, if it´s null or undefined
- if (data === undef || data === null) {
- data = [];
- }
-
- // invoke special return callback parser if defined
- if (model.parsers !== undef && model.parsers[method] !== undef && _.isFunction(model.parsers[method])) {
- model.parsers[method].apply(model, [data]);
- }
-
- // fire the 'real' backbone success callback
- options.success(data);
- },
-
- // define a local error callback that will hand over the data to the backbone error handler
- errorCb = function (data) {
- options.error(model, data);
- };
-
- // check if we have a correct (e.g. Backbone.Rpc) model instance
- if (model.rpc instanceof Rpc) {
- // assign the models JsonRpc instance locally
- rpc = model.rpc;
-
- // First, set the api url
- rpc.url = _.isFunction(model.url) ? model.url() : model.url;
-
- // Second, set the namespace
- if (_.isString(model.namespace) === true) {
- rpc.namespace = model.namespace;
- }
-
- // Third, check the remote method parameter
- if (model.methods === undef) {
- throw 'Backbone.Rpc Error: No Method(s) given!';
- } else {
- // If we have a proper method
- // assign the given paramters (if exist)
- // else an empty object
- if (typeof model.params !== 'object') {
- model.params = {};
- }
- }
-
- // go on and check the rpc methods
- return rpc.checkMethods(rpc.query, model.params, model, method, options, successCb, errorCb);
- } else {
- return sync.previous.apply(model, arguments);
- }
-
- return null;
- };
-
- // Expose the previous Backbone.sync as Backbone.sync.previous in case
- // the caller wishes to switch provider
- sync.previous = oldSync;
-
- return sync;
- }(Rpc));
-
- return Backbone;
-}));
\ No newline at end of file
diff --git a/go/base/static/js/vendor/base64-2.12.js b/go/base/static/js/vendor/base64-2.12.js
deleted file mode 100644
index 44f5d2d..0000000
--- a/go/base/static/js/vendor/base64-2.12.js
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * $Id: base64.js,v 2.12 2013/05/06 07:54:20 dankogai Exp dankogai $
- *
- * Licensed under the MIT license.
- * http://opensource.org/licenses/mit-license
- *
- * References:
- * http://en.wikipedia.org/wiki/Base64
- */
-
-(function(global) {
- 'use strict';
- if (global.Base64) return;
- var version = "2.1.2";
- // if node.js, we use Buffer
- var buffer;
- if (typeof module !== 'undefined' && module.exports) {
- buffer = require('buffer').Buffer;
- }
- // constants
- var b64chars
- = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
- var b64tab = function(bin) {
- var t = {};
- for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;
- return t;
- }(b64chars);
- var fromCharCode = String.fromCharCode;
- // encoder stuff
- var cb_utob = function(c) {
- if (c.length < 2) {
- var cc = c.charCodeAt(0);
- return cc < 0x80 ? c
- : cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6))
- + fromCharCode(0x80 | (cc & 0x3f)))
- : (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f))
- + fromCharCode(0x80 | ((cc >>> 6) & 0x3f))
- + fromCharCode(0x80 | ( cc & 0x3f)));
- } else {
- var cc = 0x10000
- + (c.charCodeAt(0) - 0xD800) * 0x400
- + (c.charCodeAt(1) - 0xDC00);
- return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07))
- + fromCharCode(0x80 | ((cc >>> 12) & 0x3f))
- + fromCharCode(0x80 | ((cc >>> 6) & 0x3f))
- + fromCharCode(0x80 | ( cc & 0x3f)));
- }
- };
- var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
- var utob = function(u) {
- return u.replace(re_utob, cb_utob);
- };
- var cb_encode = function(ccc) {
- var padlen = [0, 2, 1][ccc.length % 3],
- ord = ccc.charCodeAt(0) << 16
- | ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)
- | ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)),
- chars = [
- b64chars.charAt( ord >>> 18),
- b64chars.charAt((ord >>> 12) & 63),
- padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),
- padlen >= 1 ? '=' : b64chars.charAt(ord & 63)
- ];
- return chars.join('');
- };
- var btoa = global.btoa || function(b) {
- return b.replace(/[\s\S]{1,3}/g, cb_encode);
- };
- var _encode = buffer
- ? function (u) { return (new buffer(u)).toString('base64') }
- : function (u) { return btoa(utob(u)) }
- ;
- var encode = function(u, urisafe) {
- return !urisafe
- ? _encode(u)
- : _encode(u).replace(/[+\/]/g, function(m0) {
- return m0 == '+' ? '-' : '_';
- }).replace(/=/g, '');
- };
- var encodeURI = function(u) { return encode(u, true) };
- // decoder stuff
- var re_btou = new RegExp([
- '[\xC0-\xDF][\x80-\xBF]',
- '[\xE0-\xEF][\x80-\xBF]{2}',
- '[\xF0-\xF7][\x80-\xBF]{3}'
- ].join('|'), 'g');
- var cb_btou = function(cccc) {
- switch(cccc.length) {
- case 4:
- var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
- | ((0x3f & cccc.charCodeAt(1)) << 12)
- | ((0x3f & cccc.charCodeAt(2)) << 6)
- | (0x3f & cccc.charCodeAt(3)),
- offset = cp - 0x10000;
- return (fromCharCode((offset >>> 10) + 0xD800)
- + fromCharCode((offset & 0x3FF) + 0xDC00));
- case 3:
- return fromCharCode(
- ((0x0f & cccc.charCodeAt(0)) << 12)
- | ((0x3f & cccc.charCodeAt(1)) << 6)
- | (0x3f & cccc.charCodeAt(2))
- );
- default:
- return fromCharCode(
- ((0x1f & cccc.charCodeAt(0)) << 6)
- | (0x3f & cccc.charCodeAt(1))
- );
- }
- };
- var btou = function(b) {
- return b.replace(re_btou, cb_btou);
- };
- var cb_decode = function(cccc) {
- var len = cccc.length,
- padlen = len % 4,
- n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0)
- | (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0)
- | (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0)
- | (len > 3 ? b64tab[cccc.charAt(3)] : 0),
- chars = [
- fromCharCode( n >>> 16),
- fromCharCode((n >>> 8) & 0xff),
- fromCharCode( n & 0xff)
- ];
- chars.length -= [0, 0, 2, 1][padlen];
- return chars.join('');
- };
- var atob = global.atob || function(a){
- return a.replace(/[\s\S]{1,4}/g, cb_decode);
- };
- var _decode = buffer
- ? function(a) { return (new buffer(a, 'base64')).toString() }
- : function(a) { return btou(atob(a)) };
- var decode = function(a){
- return _decode(
- a.replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' })
- .replace(/[^A-Za-z0-9\+\/]/g, '')
- );
- };
- // export Base64
- global.Base64 = {
- VERSION: version,
- atob: atob,
- btoa: btoa,
- fromBase64: decode,
- toBase64: encode,
- utob: utob,
- encode: encode,
- encodeURI: encodeURI,
- btou: btou,
- decode: decode
- };
- // if ES5 is available, make Base64.extendString() available
- if (typeof Object.defineProperty === 'function') {
- var noEnum = function(v){
- return {value:v,enumerable:false,writable:true,configurable:true};
- };
- global.Base64.extendString = function () {
- Object.defineProperty(
- String.prototype, 'fromBase64', noEnum(function () {
- return decode(this)
- }));
- Object.defineProperty(
- String.prototype, 'toBase64', noEnum(function (urisafe) {
- return encode(this, urisafe)
- }));
- Object.defineProperty(
- String.prototype, 'toBase64URI', noEnum(function () {
- return encode(this, true)
- }));
- };
- }
- // that's it!
-})(this);
diff --git a/go/base/static/js/vendor/bootbox.js b/go/base/static/js/vendor/bootbox.js
deleted file mode 100644
index 99aa195..0000000
--- a/go/base/static/js/vendor/bootbox.js
+++ /dev/null
@@ -1,641 +0,0 @@
-/**
- * bootbox.js v3.2.0
- *
- * http://bootboxjs.com/license.txt
- */
-var bootbox = window.bootbox || (function(document, $) {
- /*jshint scripturl:true sub:true */
-
- var _locale = 'en',
- _defaultLocale = 'en',
- _animate = true,
- _backdrop = 'static',
- _defaultHref = 'javascript:;',
- _classes = '',
- _btnClasses = {},
- _icons = {},
- /* last var should always be the public object we'll return */
- that = {};
-
-
- /**
- * public API
- */
- that.setLocale = function(locale) {
- for (var i in _locales) {
- if (i == locale) {
- _locale = locale;
- return;
- }
- }
- throw new Error('Invalid locale: '+locale);
- };
-
- that.addLocale = function(locale, translations) {
- if (typeof _locales[locale] === 'undefined') {
- _locales[locale] = {};
- }
- for (var str in translations) {
- _locales[locale][str] = translations[str];
- }
- };
-
- that.setIcons = function(icons) {
- _icons = icons;
- if (typeof _icons !== 'object' || _icons === null) {
- _icons = {};
- }
- };
-
- that.setBtnClasses = function(btnClasses) {
- _btnClasses = btnClasses;
- if (typeof _btnClasses !== 'object' || _btnClasses === null) {
- _btnClasses = {};
- }
- };
-
- that.alert = function(/*str, label, cb*/) {
- var str = "",
- label = _translate('OK'),
- cb = null;
-
- switch (arguments.length) {
- case 1:
- // no callback, default button label
- str = arguments[0];
- break;
- case 2:
- // callback *or* custom button label dependent on type
- str = arguments[0];
- if (typeof arguments[1] == 'function') {
- cb = arguments[1];
- } else {
- label = arguments[1];
- }
- break;
- case 3:
- // callback and custom button label
- str = arguments[0];
- label = arguments[1];
- cb = arguments[2];
- break;
- default:
- throw new Error("Incorrect number of arguments: expected 1-3");
- }
-
- return that.dialog(str, {
- // only button (ok)
- "label" : label,
- "icon" : _icons.OK,
- "class" : _btnClasses.OK,
- "callback": cb
- }, {
- // ensure that the escape key works; either invoking the user's
- // callback or true to just close the dialog
- "onEscape": cb || true
- });
- };
-
- that.confirm = function(/*str, labelCancel, labelOk, cb*/) {
- var str = "",
- labelCancel = _translate('CANCEL'),
- labelOk = _translate('CONFIRM'),
- cb = null;
-
- switch (arguments.length) {
- case 1:
- str = arguments[0];
- break;
- case 2:
- str = arguments[0];
- if (typeof arguments[1] == 'function') {
- cb = arguments[1];
- } else {
- labelCancel = arguments[1];
- }
- break;
- case 3:
- str = arguments[0];
- labelCancel = arguments[1];
- if (typeof arguments[2] == 'function') {
- cb = arguments[2];
- } else {
- labelOk = arguments[2];
- }
- break;
- case 4:
- str = arguments[0];
- labelCancel = arguments[1];
- labelOk = arguments[2];
- cb = arguments[3];
- break;
- default:
- throw new Error("Incorrect number of arguments: expected 1-4");
- }
-
- var cancelCallback = function() {
- if (typeof cb === 'function') {
- return cb(false);
- }
- };
-
- var confirmCallback = function() {
- if (typeof cb === 'function') {
- return cb(true);
- }
- };
-
- return that.dialog(str, [{
- // first button (cancel)
- "label" : labelCancel,
- "icon" : _icons.CANCEL,
- "class" : _btnClasses.CANCEL,
- "callback": cancelCallback
- }, {
- // second button (confirm)
- "label" : labelOk,
- "icon" : _icons.CONFIRM,
- "class" : _btnClasses.CONFIRM,
- "callback": confirmCallback
- }], {
- // escape key bindings
- "onEscape": cancelCallback
- });
- };
-
- that.prompt = function(/*str, labelCancel, labelOk, cb, defaultVal*/) {
- var str = "",
- labelCancel = _translate('CANCEL'),
- labelOk = _translate('CONFIRM'),
- cb = null,
- defaultVal = "";
-
- switch (arguments.length) {
- case 1:
- str = arguments[0];
- break;
- case 2:
- str = arguments[0];
- if (typeof arguments[1] == 'function') {
- cb = arguments[1];
- } else {
- labelCancel = arguments[1];
- }
- break;
- case 3:
- str = arguments[0];
- labelCancel = arguments[1];
- if (typeof arguments[2] == 'function') {
- cb = arguments[2];
- } else {
- labelOk = arguments[2];
- }
- break;
- case 4:
- str = arguments[0];
- labelCancel = arguments[1];
- labelOk = arguments[2];
- cb = arguments[3];
- break;
- case 5:
- str = arguments[0];
- labelCancel = arguments[1];
- labelOk = arguments[2];
- cb = arguments[3];
- defaultVal = arguments[4];
- break;
- default:
- throw new Error("Incorrect number of arguments: expected 1-5");
- }
-
- var header = str;
-
- // let's keep a reference to the form object for later
- var form = $("<form></form>");
- form.append("<input autocomplete=off type=text value='" + defaultVal + "' />");
-
- var cancelCallback = function() {
- if (typeof cb === 'function') {
- // yep, native prompts dismiss with null, whereas native
- // confirms dismiss with false...
- return cb(null);
- }
- };
-
- var confirmCallback = function() {
- if (typeof cb === 'function') {
- return cb(form.find("input[type=text]").val());
- }
- };
-
- var div = that.dialog(form, [{
- // first button (cancel)
- "label" : labelCancel,
- "icon" : _icons.CANCEL,
- "class" : _btnClasses.CANCEL,
- "callback": cancelCallback
- }, {
- // second button (confirm)
- "label" : labelOk,
- "icon" : _icons.CONFIRM,
- "class" : _btnClasses.CONFIRM,
- "callback": confirmCallback
- }], {
- // prompts need a few extra options
- "header" : header,
- // explicitly tell dialog NOT to show the dialog...
- "show" : false,
- "onEscape": cancelCallback
- });
-
- // ... the reason the prompt needs to be hidden is because we need
- // to bind our own "shown" handler, after creating the modal but
- // before any show(n) events are triggered
- // @see https://github.com/makeusabrew/bootbox/issues/69
-
- div.on("shown", function() {
- form.find("input[type=text]").focus();
-
- // ensure that submitting the form (e.g. with the enter key)
- // replicates the behaviour of a normal prompt()
- form.on("submit", function(e) {
- e.preventDefault();
- div.find(".btn-primary").click();
- });
- });
-
- div.modal("show");
-
- return div;
- };
-
- that.dialog = function(str, handlers, options) {
- var buttons = "",
- callbacks = [];
-
- if (!options) {
- options = {};
- }
-
- // check for single object and convert to array if necessary
- if (typeof handlers === 'undefined') {
- handlers = [];
- } else if (typeof handlers.length == 'undefined') {
- handlers = [handlers];
- }
-
- var i = handlers.length;
- while (i--) {
- var label = null,
- href = null,
- _class = null,
- icon = '',
- callback = null;
-
- if (typeof handlers[i]['label'] == 'undefined' &&
- typeof handlers[i]['class'] == 'undefined' &&
- typeof handlers[i]['callback'] == 'undefined') {
- // if we've got nothing we expect, check for condensed format
-
- var propCount = 0, // condensed will only match if this == 1
- property = null; // save the last property we found
-
- // be nicer to count the properties without this, but don't think it's possible...
- for (var j in handlers[i]) {
- property = j;
- if (++propCount > 1) {
- // forget it, too many properties
- break;
- }
- }
-
- if (propCount == 1 && typeof handlers[i][j] == 'function') {
- // matches condensed format of label -> function
- handlers[i]['label'] = property;
- handlers[i]['callback'] = handlers[i][j];
- }
- }
-
- if (typeof handlers[i]['callback']== 'function') {
- callback = handlers[i]['callback'];
- }
-
- if (handlers[i]['class']) {
- _class = handlers[i]['class'];
- } else if (i == handlers.length -1 && handlers.length <= 2) {
- // always add a primary to the main option in a two-button dialog
- _class = 'btn-primary';
- }
-
- if (handlers[i]['label']) {
- label = handlers[i]['label'];
- } else {
- label = "Option "+(i+1);
- }
-
- if (handlers[i]['icon']) {
- icon = "<i class='"+handlers[i]['icon']+"'></i> ";
- }
-
- if (handlers[i]['href']) {
- href = handlers[i]['href'];
- }
- else {
- href = _defaultHref;
- }
-
- buttons = "<a data-handler='"+i+"' class='btn "+_class+"' href='" + href + "'>"+icon+""+label+"</a>" + buttons;
-
- callbacks[i] = callback;
- }
-
- // @see https://github.com/makeusabrew/bootbox/issues/46#issuecomment-8235302
- // and https://github.com/twitter/bootstrap/issues/4474
- // for an explanation of the inline overflow: hidden
- // @see https://github.com/twitter/bootstrap/issues/4854
- // for an explanation of tabIndex=-1
-
- var parts = ["<div class='bootbox modal' tabindex='-1' style='overflow:hidden;'>"];
-
- if (options['header']) {
- var closeButton = '';
- if (typeof options['headerCloseButton'] == 'undefined' || options['headerCloseButton']) {
- closeButton = "<a href='"+_defaultHref+"' class='close'>&times;</a>";
- }
-
- parts.push("<div class='modal-header'>"+closeButton+"<h3>"+options['header']+"</h3></div>");
- }
-
- // push an empty body into which we'll inject the proper content later
- parts.push("<div class='modal-body'></div>");
-
- if (buttons) {
- parts.push("<div class='modal-footer'>"+buttons+"</div>");
- }
-
- parts.push("</div>");
-
- var div = $(parts.join("\n"));
-
- // check whether we should fade in/out
- var shouldFade = (typeof options.animate === 'undefined') ? _animate : options.animate;
-
- if (shouldFade) {
- div.addClass("fade");
- }
-
- var optionalClasses = (typeof options.classes === 'undefined') ? _classes : options.classes;
- if (optionalClasses) {
- div.addClass(optionalClasses);
- }
-
- // now we've built up the div properly we can inject the content whether it was a string or a jQuery object
- div.find(".modal-body").html(str);
-
- function onCancel(source) {
- // for now source is unused, but it will be in future
- var hideModal = null;
- if (typeof options.onEscape === 'function') {
- // @see https://github.com/makeusabrew/bootbox/issues/91
- hideModal = options.onEscape();
- }
-
- if (hideModal !== false) {
- div.modal('hide');
- }
- }
-
- // hook into the modal's keyup trigger to check for the escape key
- div.on('keyup.dismiss.modal', function(e) {
- // any truthy value passed to onEscape will dismiss the dialog
- // as long as the onEscape function (if defined) doesn't prevent it
- if (e.which === 27 && options.onEscape) {
- onCancel('escape');
- }
- });
-
- // handle close buttons too
- div.on('click', 'a.close', function(e) {
- e.preventDefault();
- onCancel('close');
- });
-
- // well, *if* we have a primary - give the first dom element focus
- div.on('shown', function() {
- div.find("a.btn-primary:first").focus();
- });
-
- div.on('hidden', function() {
- div.remove();
- });
-
- // wire up button handlers
- div.on('click', '.modal-footer a', function(e) {
-
- var handler = $(this).data("handler"),
- cb = callbacks[handler],
- hideModal = null;
-
- // sort of @see https://github.com/makeusabrew/bootbox/pull/68 - heavily adapted
- // if we've got a custom href attribute, all bets are off
- if (typeof handler !== 'undefined' &&
- typeof handlers[handler]['href'] !== 'undefined') {
-
- return;
- }
-
- e.preventDefault();
-
- if (typeof cb === 'function') {
- hideModal = cb();
- }
-
- // the only way hideModal *will* be false is if a callback exists and
- // returns it as a value. in those situations, don't hide the dialog
- // @see https://github.com/makeusabrew/bootbox/pull/25
- if (hideModal !== false) {
- div.modal("hide");
- }
- });
-
- // stick the modal right at the bottom of the main body out of the way
- $("body").append(div);
-
- div.modal({
- // unless explicitly overridden take whatever our default backdrop value is
- backdrop : (typeof options.backdrop === 'undefined') ? _backdrop : options.backdrop,
- // ignore bootstrap's keyboard options; we'll handle this ourselves (more fine-grained control)
- keyboard : false,
- // @ see https://github.com/makeusabrew/bootbox/issues/69
- // we *never* want the modal to be shown before we can bind stuff to it
- // this method can also take a 'show' option, but we'll only use that
- // later if we need to
- show : false
- });
-
- // @see https://github.com/makeusabrew/bootbox/issues/64
- // @see https://github.com/makeusabrew/bootbox/issues/60
- // ...caused by...
- // @see https://github.com/twitter/bootstrap/issues/4781
- div.on("show", function(e) {
- $(document).off("focusin.modal");
- });
-
- if (typeof options.show === 'undefined' || options.show === true) {
- div.modal("show");
- }
-
- return div;
- };
-
- /**
- * #modal is deprecated in v3; it can still be used but no guarantees are
- * made - have never been truly convinced of its merit but perhaps just
- * needs a tidyup and some TLC
- */
- that.modal = function(/*str, label, options*/) {
- var str;
- var label;
- var options;
-
- var defaultOptions = {
- "onEscape": null,
- "keyboard": true,
- "backdrop": _backdrop
- };
-
- switch (arguments.length) {
- case 1:
- str = arguments[0];
- break;
- case 2:
- str = arguments[0];
- if (typeof arguments[1] == 'object') {
- options = arguments[1];
- } else {
- label = arguments[1];
- }
- break;
- case 3:
- str = arguments[0];
- label = arguments[1];
- options = arguments[2];
- break;
- default:
- throw new Error("Incorrect number of arguments: expected 1-3");
- }
-
- defaultOptions['header'] = label;
-
- if (typeof options == 'object') {
- options = $.extend(defaultOptions, options);
- } else {
- options = defaultOptions;
- }
-
- return that.dialog(str, [], options);
- };
-
-
- that.hideAll = function() {
- $(".bootbox").modal("hide");
- };
-
- that.animate = function(animate) {
- _animate = animate;
- };
-
- that.backdrop = function(backdrop) {
- _backdrop = backdrop;
- };
-
- that.classes = function(classes) {
- _classes = classes;
- };
-
- /**
- * private API
- */
-
- /**
- * standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
- * unlikely to be required. If this gets too large it can be split out into separate JS files.
- */
- var _locales = {
- 'br' : {
- OK : 'OK',
- CANCEL : 'Cancelar',
- CONFIRM : 'Sim'
- },
- 'da' : {
- OK : 'OK',
- CANCEL : 'Annuller',
- CONFIRM : 'Accepter'
- },
- 'de' : {
- OK : 'OK',
- CANCEL : 'Abbrechen',
- CONFIRM : 'Akzeptieren'
- },
- 'en' : {
- OK : 'OK',
- CANCEL : 'Cancel',
- CONFIRM : 'OK'
- },
- 'es' : {
- OK : 'OK',
- CANCEL : 'Cancelar',
- CONFIRM : 'Aceptar'
- },
- 'fr' : {
- OK : 'OK',
- CANCEL : 'Annuler',
- CONFIRM : 'D\'accord'
- },
- 'it' : {
- OK : 'OK',
- CANCEL : 'Annulla',
- CONFIRM : 'Conferma'
- },
- 'nl' : {
- OK : 'OK',
- CANCEL : 'Annuleren',
- CONFIRM : 'Accepteren'
- },
- 'pl' : {
- OK : 'OK',
- CANCEL : 'Anuluj',
- CONFIRM : 'Potwierdź'
- },
- 'ru' : {
- OK : 'OK',
- CANCEL : 'Отмена',
- CONFIRM : 'Применить'
- },
- };
-
- function _translate(str, locale) {
- // we assume if no target locale is probided then we should take it from current setting
- if (typeof locale === 'undefined') {
- locale = _locale;
- }
- if (typeof _locales[locale][str] === 'string') {
- return _locales[locale][str];
- }
-
- // if we couldn't find a lookup then try and fallback to a default translation
-
- if (locale != _defaultLocale) {
- return _translate(str, _defaultLocale);
- }
-
- // if we can't do anything then bail out with whatever string was passed in - last resort
- return str;
- }
-
- return that;
-
-}(document, window.jQuery));
-
-// @see https://github.com/makeusabrew/bootbox/issues/71
-window.bootbox = bootbox;
\ No newline at end of file
diff --git a/go/base/static/js/vendor/jquery-1.9.1.js b/go/base/static/js/vendor/jquery-1.9.1.js
deleted file mode 100644
index e2c203f..0000000
--- a/go/base/static/js/vendor/jquery-1.9.1.js
+++ /dev/null
@@ -1,9597 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.9.1
- * http://jquery.com/
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- *
- * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2013-2-4
- */
-(function( window, undefined ) {
-
-// Can't do this because several apps including ASP.NET trace
-// the stack via arguments.caller.callee and Firefox dies if
-// you try to trace through "use strict" call chains. (#13335)
-// Support: Firefox 18+
-//"use strict";
-var
- // The deferred used on DOM ready
- readyList,
-
- // A central reference to the root jQuery(document)
- rootjQuery,
-
- // Support: IE<9
- // For `typeof node.method` instead of `node.method !== undefined`
- core_strundefined = typeof undefined,
-
- // Use the correct document accordingly with window argument (sandbox)
- document = window.document,
- location = window.location,
-
- // Map over jQuery in case of overwrite
- _jQuery = window.jQuery,
-
- // Map over the $ in case of overwrite
- _$ = window.$,
-
- // [[Class]] -> type pairs
- class2type = {},
-
- // List of deleted data cache ids, so we can reuse them
- core_deletedIds = [],
-
- core_version = "1.9.1",
-
- // Save a reference to some core methods
- core_concat = core_deletedIds.concat,
- core_push = core_deletedIds.push,
- core_slice = core_deletedIds.slice,
- core_indexOf = core_deletedIds.indexOf,
- core_toString = class2type.toString,
- core_hasOwn = class2type.hasOwnProperty,
- core_trim = core_version.trim,
-
- // Define a local copy of jQuery
- jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
-
- // Used for matching numbers
- core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
-
- // Used for splitting on whitespace
- core_rnotwhite = /\S+/g,
-
- // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
- rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
- // A simple way to check for HTML strings
- // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
- // Strict HTML recognition (#11290: must start with <)
- rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
-
- // Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
-
- // JSON RegExp
- rvalidchars = /^[\],:{}\s]*$/,
- rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
- rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
- rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
-
- // Matches dashed string for camelizing
- rmsPrefix = /^-ms-/,
- rdashAlpha = /-([\da-z])/gi,
-
- // Used by jQuery.camelCase as callback to replace()
- fcamelCase = function( all, letter ) {
- return letter.toUpperCase();
- },
-
- // The ready event handler
- completed = function( event ) {
-
- // readyState === "complete" is good enough for us to call the dom ready in oldIE
- if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
- detach();
- jQuery.ready();
- }
- },
- // Clean-up method for dom ready events
- detach = function() {
- if ( document.addEventListener ) {
- document.removeEventListener( "DOMContentLoaded", completed, false );
- window.removeEventListener( "load", completed, false );
-
- } else {
- document.detachEvent( "onreadystatechange", completed );
- window.detachEvent( "onload", completed );
- }
- };
-
-jQuery.fn = jQuery.prototype = {
- // The current version of jQuery being used
- jquery: core_version,
-
- constructor: jQuery,
- init: function( selector, context, rootjQuery ) {
- var match, elem;
-
- // HANDLE: $(""), $(null), $(undefined), $(false)
- if ( !selector ) {
- return this;
- }
-
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
-
- } else {
- match = rquickExpr.exec( selector );
- }
-
- // Match html or make sure no context is specified for #id
- if ( match && (match[1] || !context) ) {
-
- // HANDLE: $(html) -> $(array)
- if ( match[1] ) {
- context = context instanceof jQuery ? context[0] : context;
-
- // scripts is true for back-compat
- jQuery.merge( this, jQuery.parseHTML(
- match[1],
- context && context.nodeType ? context.ownerDocument || context : document,
- true
- ) );
-
- // HANDLE: $(html, props)
- if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
- for ( match in context ) {
- // Properties of context are called as methods if possible
- if ( jQuery.isFunction( this[ match ] ) ) {
- this[ match ]( context[ match ] );
-
- // ...and otherwise set as attributes
- } else {
- this.attr( match, context[ match ] );
- }
- }
- }
-
- return this;
-
- // HANDLE: $(#id)
- } else {
- elem = document.getElementById( match[2] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
-
- // Otherwise, we inject the element directly into the jQuery object
- this.length = 1;
- this[0] = elem;
- }
-
- this.context = document;
- this.selector = selector;
- return this;
- }
-
- // HANDLE: $(expr, $(...))
- } else if ( !context || context.jquery ) {
- return ( context || rootjQuery ).find( selector );
-
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- return this.constructor( context ).find( selector );
- }
-
- // HANDLE: $(DOMElement)
- } else if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
-
- // HANDLE: $(function)
- // Shortcut for document ready
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
-
- if ( selector.selector !== undefined ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
-
- return jQuery.makeArray( selector, this );
- },
-
- // Start with an empty selector
- selector: "",
-
- // The default length of a jQuery object is 0
- length: 0,
-
- // The number of elements contained in the matched element set
- size: function() {
- return this.length;
- },
-
- toArray: function() {
- return core_slice.call( this );
- },
-
- // Get the Nth element in the matched element set OR
- // Get the whole matched element set as a clean array
- get: function( num ) {
- return num == null ?
-
- // Return a 'clean' array
- this.toArray() :
-
- // Return just the object
- ( num < 0 ? this[ this.length + num ] : this[ num ] );
- },
-
- // Take an array of elements and push it onto the stack
- // (returning the new matched element set)
- pushStack: function( elems ) {
-
- // Build a new jQuery matched element set
- var ret = jQuery.merge( this.constructor(), elems );
-
- // Add the old object onto the stack (as a reference)
- ret.prevObject = this;
- ret.context = this.context;
-
- // Return the newly-formed element set
- return ret;
- },
-
- // Execute a callback for every element in the matched set.
- // (You can seed the arguments with an array of args, but this is
- // only used internally.)
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- },
-
- ready: function( fn ) {
- // Add the callback
- jQuery.ready.promise().done( fn );
-
- return this;
- },
-
- slice: function() {
- return this.pushStack( core_slice.apply( this, arguments ) );
- },
-
- first: function() {
- return this.eq( 0 );
- },
-
- last: function() {
- return this.eq( -1 );
- },
-
- eq: function( i ) {
- var len = this.length,
- j = +i + ( i < 0 ? len : 0 );
- return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
- },
-
- map: function( callback ) {
- return this.pushStack( jQuery.map(this, function( elem, i ) {
- return callback.call( elem, i, elem );
- }));
- },
-
- end: function() {
- return this.prevObject || this.constructor(null);
- },
-
- // For internal use only.
- // Behaves like an Array's method, not like a jQuery method.
- push: core_push,
- sort: [].sort,
- splice: [].splice
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.fn.init.prototype = jQuery.fn;
-
-jQuery.extend = jQuery.fn.extend = function() {
- var src, copyIsArray, copy, name, options, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
-
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
-
- // extend jQuery itself if only one argument is passed
- if ( length === i ) {
- target = this;
- --i;
- }
-
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
-
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
-
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
-
- } else {
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
-
- // Never move original objects, clone them
- target[ name ] = jQuery.extend( deep, clone, copy );
-
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
-
- // Return the modified object
- return target;
-};
-
-jQuery.extend({
- noConflict: function( deep ) {
- if ( window.$ === jQuery ) {
- window.$ = _$;
- }
-
- if ( deep && window.jQuery === jQuery ) {
- window.jQuery = _jQuery;
- }
-
- return jQuery;
- },
-
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // A counter to track how many items to wait for before
- // the ready event fires. See #6781
- readyWait: 1,
-
- // Hold (or release) the ready event
- holdReady: function( hold ) {
- if ( hold ) {
- jQuery.readyWait++;
- } else {
- jQuery.ready( true );
- }
- },
-
- // Handle when the DOM is ready
- ready: function( wait ) {
-
- // Abort if there are pending holds or we're already ready
- if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
- return;
- }
-
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger("ready").off("ready");
- }
- },
-
- // See test/unit/core.js for details concerning isFunction.
- // Since version 1.3, DOM methods and functions like alert
- // aren't supported. They return false on IE (#2968).
- isFunction: function( obj ) {
- return jQuery.type(obj) === "function";
- },
-
- isArray: Array.isArray || function( obj ) {
- return jQuery.type(obj) === "array";
- },
-
- isWindow: function( obj ) {
- return obj != null && obj == obj.window;
- },
-
- isNumeric: function( obj ) {
- return !isNaN( parseFloat(obj) ) && isFinite( obj );
- },
-
- type: function( obj ) {
- if ( obj == null ) {
- return String( obj );
- }
- return typeof obj === "object" || typeof obj === "function" ?
- class2type[ core_toString.call(obj) ] || "object" :
- typeof obj;
- },
-
- isPlainObject: function( obj ) {
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- try {
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !core_hasOwn.call(obj, "constructor") &&
- !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
- return false;
- }
- } catch ( e ) {
- // IE8,9 Will throw exceptions on certain host objects #9897
- return false;
- }
-
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
-
- var key;
- for ( key in obj ) {}
-
- return key === undefined || core_hasOwn.call( obj, key );
- },
-
- isEmptyObject: function( obj ) {
- var name;
- for ( name in obj ) {
- return false;
- }
- return true;
- },
-
- error: function( msg ) {
- throw new Error( msg );
- },
-
- // data: string of html
- // context (optional): If specified, the fragment will be created in this context, defaults to document
- // keepScripts (optional): If true, will include scripts passed in the html string
- parseHTML: function( data, context, keepScripts ) {
- if ( !data || typeof data !== "string" ) {
- return null;
- }
- if ( typeof context === "boolean" ) {
- keepScripts = context;
- context = false;
- }
- context = context || document;
-
- var parsed = rsingleTag.exec( data ),
- scripts = !keepScripts && [];
-
- // Single tag
- if ( parsed ) {
- return [ context.createElement( parsed[1] ) ];
- }
-
- parsed = jQuery.buildFragment( [ data ], context, scripts );
- if ( scripts ) {
- jQuery( scripts ).remove();
- }
- return jQuery.merge( [], parsed.childNodes );
- },
-
- parseJSON: function( data ) {
- // Attempt to parse using the native JSON parser first
- if ( window.JSON && window.JSON.parse ) {
- return window.JSON.parse( data );
- }
-
- if ( data === null ) {
- return data;
- }
-
- if ( typeof data === "string" ) {
-
- // Make sure leading/trailing whitespace is removed (IE can't handle it)
- data = jQuery.trim( data );
-
- if ( data ) {
- // Make sure the incoming data is actual JSON
- // Logic borrowed from http://json.org/json2.js
- if ( rvalidchars.test( data.replace( rvalidescape, "@" )
- .replace( rvalidtokens, "]" )
- .replace( rvalidbraces, "")) ) {
-
- return ( new Function( "return " + data ) )();
- }
- }
- }
-
- jQuery.error( "Invalid JSON: " + data );
- },
-
- // Cross-browser xml parsing
- parseXML: function( data ) {
- var xml, tmp;
- if ( !data || typeof data !== "string" ) {
- return null;
- }
- try {
- if ( window.DOMParser ) { // Standard
- tmp = new DOMParser();
- xml = tmp.parseFromString( data , "text/xml" );
- } else { // IE
- xml = new ActiveXObject( "Microsoft.XMLDOM" );
- xml.async = "false";
- xml.loadXML( data );
- }
- } catch( e ) {
- xml = undefined;
- }
- if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
- jQuery.error( "Invalid XML: " + data );
- }
- return xml;
- },
-
- noop: function() {},
-
- // Evaluates a script in a global context
- // Workarounds based on findings by Jim Driscoll
- // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
- globalEval: function( data ) {
- if ( data && jQuery.trim( data ) ) {
- // We use execScript on Internet Explorer
- // We use an anonymous function so that context is window
- // rather than jQuery in Firefox
- ( window.execScript || function( data ) {
- window[ "eval" ].call( window, data );
- } )( data );
- }
- },
-
- // Convert dashed to camelCase; used by the css and data modules
- // Microsoft forgot to hump their vendor prefix (#9572)
- camelCase: function( string ) {
- return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
- },
-
- nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
- },
-
- // args is for internal usage only
- each: function( obj, callback, args ) {
- var value,
- i = 0,
- length = obj.length,
- isArray = isArraylike( obj );
-
- if ( args ) {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- }
- }
-
- return obj;
- },
-
- // Use native String.trim function wherever possible
- trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
- function( text ) {
- return text == null ?
- "" :
- core_trim.call( text );
- } :
-
- // Otherwise use our own trimming functionality
- function( text ) {
- return text == null ?
- "" :
- ( text + "" ).replace( rtrim, "" );
- },
-
- // results is for internal usage only
- makeArray: function( arr, results ) {
- var ret = results || [];
-
- if ( arr != null ) {
- if ( isArraylike( Object(arr) ) ) {
- jQuery.merge( ret,
- typeof arr === "string" ?
- [ arr ] : arr
- );
- } else {
- core_push.call( ret, arr );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, arr, i ) {
- var len;
-
- if ( arr ) {
- if ( core_indexOf ) {
- return core_indexOf.call( arr, elem, i );
- }
-
- len = arr.length;
- i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
-
- for ( ; i < len; i++ ) {
- // Skip accessing in sparse arrays
- if ( i in arr && arr[ i ] === elem ) {
- return i;
- }
- }
- }
-
- return -1;
- },
-
- merge: function( first, second ) {
- var l = second.length,
- i = first.length,
- j = 0;
-
- if ( typeof l === "number" ) {
- for ( ; j < l; j++ ) {
- first[ i++ ] = second[ j ];
- }
- } else {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, inv ) {
- var retVal,
- ret = [],
- i = 0,
- length = elems.length;
- inv = !!inv;
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( ; i < length; i++ ) {
- retVal = !!callback( elems[ i ], i );
- if ( inv !== retVal ) {
- ret.push( elems[ i ] );
- }
- }
-
- return ret;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var value,
- i = 0,
- length = elems.length,
- isArray = isArraylike( elems ),
- ret = [];
-
- // Go through the array, translating each of the items to their
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
-
- // Go through every key on the object,
- } else {
- for ( i in elems ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
- }
-
- // Flatten any nested arrays
- return core_concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- // Bind a function to a context, optionally partially applying any
- // arguments.
- proxy: function( fn, context ) {
- var args, proxy, tmp;
-
- if ( typeof context === "string" ) {
- tmp = fn[ context ];
- context = fn;
- fn = tmp;
- }
-
- // Quick check to determine if target is callable, in the spec
- // this throws a TypeError, but we will just return undefined.
- if ( !jQuery.isFunction( fn ) ) {
- return undefined;
- }
-
- // Simulated bind
- args = core_slice.call( arguments, 2 );
- proxy = function() {
- return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
- };
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
- return proxy;
- },
-
- // Multifunctional method to get and set values of a collection
- // The value/s can optionally be executed if it's a function
- access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
- var i = 0,
- length = elems.length,
- bulk = key == null;
-
- // Sets many values
- if ( jQuery.type( key ) === "object" ) {
- chainable = true;
- for ( i in key ) {
- jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
- }
-
- // Sets one value
- } else if ( value !== undefined ) {
- chainable = true;
-
- if ( !jQuery.isFunction( value ) ) {
- raw = true;
- }
-
- if ( bulk ) {
- // Bulk operations run against the entire set
- if ( raw ) {
- fn.call( elems, value );
- fn = null;
-
- // ...except when executing function values
- } else {
- bulk = fn;
- fn = function( elem, key, value ) {
- return bulk.call( jQuery( elem ), value );
- };
- }
- }
-
- if ( fn ) {
- for ( ; i < length; i++ ) {
- fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
- }
- }
- }
-
- return chainable ?
- elems :
-
- // Gets
- bulk ?
- fn.call( elems ) :
- length ? fn( elems[0], key ) : emptyGet;
- },
-
- now: function() {
- return ( new Date() ).getTime();
- }
-});
-
-jQuery.ready.promise = function( obj ) {
- if ( !readyList ) {
-
- readyList = jQuery.Deferred();
-
- // Catch cases where $(document).ready() is called after the browser event has already occurred.
- // we once tried to use readyState "interactive" here, but it caused issues like the one
- // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- setTimeout( jQuery.ready );
-
- // Standards-based browsers support DOMContentLoaded
- } else if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", completed, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", completed, false );
-
- // If IE event model is used
- } else {
- // Ensure firing before onload, maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", completed );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", completed );
-
- // If IE and not a frame
- // continually check to see if the document is ready
- var top = false;
-
- try {
- top = window.frameElement == null && document.documentElement;
- } catch(e) {}
-
- if ( top && top.doScroll ) {
- (function doScrollCheck() {
- if ( !jQuery.isReady ) {
-
- try {
- // Use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- top.doScroll("left");
- } catch(e) {
- return setTimeout( doScrollCheck, 50 );
- }
-
- // detach all dom ready events
- detach();
-
- // and execute any waiting functions
- jQuery.ready();
- }
- })();
- }
- }
- }
- return readyList.promise( obj );
-};
-
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
-
-function isArraylike( obj ) {
- var length = obj.length,
- type = jQuery.type( obj );
-
- if ( jQuery.isWindow( obj ) ) {
- return false;
- }
-
- if ( obj.nodeType === 1 && length ) {
- return true;
- }
-
- return type === "array" || type !== "function" &&
- ( length === 0 ||
- typeof length === "number" && length > 0 && ( length - 1 ) in obj );
-}
-
-// All jQuery objects should point back to these
-rootjQuery = jQuery(document);
-// String to Object options format cache
-var optionsCache = {};
-
-// Convert String-formatted options into Object-formatted ones and store in cache
-function createOptions( options ) {
- var object = optionsCache[ options ] = {};
- jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
- object[ flag ] = true;
- });
- return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- * options: an optional list of space-separated options that will change how
- * the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- * once: will ensure the callback list can only be fired once (like a Deferred)
- *
- * memory: will keep track of previous values and will call any callback added
- * after the list has been fired right away with the latest "memorized"
- * values (like a Deferred)
- *
- * unique: will ensure a callback can only be added once (no duplicate in the list)
- *
- * stopOnFalse: interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
- // Convert options from String-formatted to Object-formatted if needed
- // (we check in cache first)
- options = typeof options === "string" ?
- ( optionsCache[ options ] || createOptions( options ) ) :
- jQuery.extend( {}, options );
-
- var // Flag to know if list is currently firing
- firing,
- // Last fire value (for non-forgettable lists)
- memory,
- // Flag to know if list was already fired
- fired,
- // End of the loop when firing
- firingLength,
- // Index of currently firing callback (modified by remove if needed)
- firingIndex,
- // First callback to fire (used internally by add and fireWith)
- firingStart,
- // Actual callback list
- list = [],
- // Stack of fire calls for repeatable lists
- stack = !options.once && [],
- // Fire callbacks
- fire = function( data ) {
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
- memory = false; // To prevent further calls using add
- break;
- }
- }
- firing = false;
- if ( list ) {
- if ( stack ) {
- if ( stack.length ) {
- fire( stack.shift() );
- }
- } else if ( memory ) {
- list = [];
- } else {
- self.disable();
- }
- }
- },
- // Actual Callbacks object
- self = {
- // Add a callback or a collection of callbacks to the list
- add: function() {
- if ( list ) {
- // First, we save the current length
- var start = list.length;
- (function add( args ) {
- jQuery.each( args, function( _, arg ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
- if ( !options.unique || !self.has( arg ) ) {
- list.push( arg );
- }
- } else if ( arg && arg.length && type !== "string" ) {
- // Inspect recursively
- add( arg );
- }
- });
- })( arguments );
- // Do we need to add the callbacks to the
- // current firing batch?
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- } else if ( memory ) {
- firingStart = start;
- fire( memory );
- }
- }
- return this;
- },
- // Remove a callback from the list
- remove: function() {
- if ( list ) {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
- // Handle firing indexes
- if ( firing ) {
- if ( index <= firingLength ) {
- firingLength--;
- }
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
- }
- });
- }
- return this;
- },
- // Check if a given callback is in the list.
- // If no argument is given, return whether or not list has callbacks attached.
- has: function( fn ) {
- return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
- },
- // Remove all callbacks from the list
- empty: function() {
- list = [];
- return this;
- },
- // Have the list do nothing anymore
- disable: function() {
- list = stack = memory = undefined;
- return this;
- },
- // Is it disabled?
- disabled: function() {
- return !list;
- },
- // Lock the list in its current state
- lock: function() {
- stack = undefined;
- if ( !memory ) {
- self.disable();
- }
- return this;
- },
- // Is it locked?
- locked: function() {
- return !stack;
- },
- // Call all callbacks with the given context and arguments
- fireWith: function( context, args ) {
- args = args || [];
- args = [ context, args.slice ? args.slice() : args ];
- if ( list && ( !fired || stack ) ) {
- if ( firing ) {
- stack.push( args );
- } else {
- fire( args );
- }
- }
- return this;
- },
- // Call all the callbacks with the given arguments
- fire: function() {
- self.fireWith( this, arguments );
- return this;
- },
- // To know if the callbacks have already been called at least once
- fired: function() {
- return !!fired;
- }
- };
-
- return self;
-};
-jQuery.extend({
-
- Deferred: function( func ) {
- var tuples = [
- // action, add listener, listener list, final state
- [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
- [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
- [ "notify", "progress", jQuery.Callbacks("memory") ]
- ],
- state = "pending",
- promise = {
- state: function() {
- return state;
- },
- always: function() {
- deferred.done( arguments ).fail( arguments );
- return this;
- },
- then: function( /* fnDone, fnFail, fnProgress */ ) {
- var fns = arguments;
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( tuples, function( i, tuple ) {
- var action = tuple[ 0 ],
- fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
- // deferred[ done | fail | progress ] for forwarding actions to newDefer
- deferred[ tuple[1] ](function() {
- var returned = fn && fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise()
- .done( newDefer.resolve )
- .fail( newDefer.reject )
- .progress( newDefer.notify );
- } else {
- newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
- }
- });
- });
- fns = null;
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- return obj != null ? jQuery.extend( obj, promise ) : promise;
- }
- },
- deferred = {};
-
- // Keep pipe for back-compat
- promise.pipe = promise.then;
-
- // Add list-specific methods
- jQuery.each( tuples, function( i, tuple ) {
- var list = tuple[ 2 ],
- stateString = tuple[ 3 ];
-
- // promise[ done | fail | progress ] = list.add
- promise[ tuple[1] ] = list.add;
-
- // Handle state
- if ( stateString ) {
- list.add(function() {
- // state = [ resolved | rejected ]
- state = stateString;
-
- // [ reject_list | resolve_list ].disable; progress_list.lock
- }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
- }
-
- // deferred[ resolve | reject | notify ]
- deferred[ tuple[0] ] = function() {
- deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
- return this;
- };
- deferred[ tuple[0] + "With" ] = list.fireWith;
- });
-
- // Make the deferred a promise
- promise.promise( deferred );
-
- // Call given func if any
- if ( func ) {
- func.call( deferred, deferred );
- }
-
- // All done!
- return deferred;
- },
-
- // Deferred helper
- when: function( subordinate /* , ..., subordinateN */ ) {
- var i = 0,
- resolveValues = core_slice.call( arguments ),
- length = resolveValues.length,
-
- // the count of uncompleted subordinates
- remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
-
- // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
- deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
-
- // Update function for both resolve and progress values
- updateFunc = function( i, contexts, values ) {
- return function( value ) {
- contexts[ i ] = this;
- values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
- if( values === progressValues ) {
- deferred.notifyWith( contexts, values );
- } else if ( !( --remaining ) ) {
- deferred.resolveWith( contexts, values );
- }
- };
- },
-
- progressValues, progressContexts, resolveContexts;
-
- // add listeners to Deferred subordinates; treat others as resolved
- if ( length > 1 ) {
- progressValues = new Array( length );
- progressContexts = new Array( length );
- resolveContexts = new Array( length );
- for ( ; i < length; i++ ) {
- if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
- resolveValues[ i ].promise()
- .done( updateFunc( i, resolveContexts, resolveValues ) )
- .fail( deferred.reject )
- .progress( updateFunc( i, progressContexts, progressValues ) );
- } else {
- --remaining;
- }
- }
- }
-
- // if we're not waiting on anything, resolve the master
- if ( !remaining ) {
- deferred.resolveWith( resolveContexts, resolveValues );
- }
-
- return deferred.promise();
- }
-});
-jQuery.support = (function() {
-
- var support, all, a,
- input, select, fragment,
- opt, eventName, isSupported, i,
- div = document.createElement("div");
-
- // Setup
- div.setAttribute( "className", "t" );
- div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
-
- // Support tests won't run in some limited or non-browser environments
- all = div.getElementsByTagName("*");
- a = div.getElementsByTagName("a")[ 0 ];
- if ( !all || !a || !all.length ) {
- return {};
- }
-
- // First batch of tests
- select = document.createElement("select");
- opt = select.appendChild( document.createElement("option") );
- input = div.getElementsByTagName("input")[ 0 ];
-
- a.style.cssText = "top:1px;float:left;opacity:.5";
- support = {
- // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
- getSetAttribute: div.className !== "t",
-
- // IE strips leading whitespace when .innerHTML is used
- leadingWhitespace: div.firstChild.nodeType === 3,
-
- // Make sure that tbody elements aren't automatically inserted
- // IE will insert them into empty tables
- tbody: !div.getElementsByTagName("tbody").length,
-
- // Make sure that link elements get serialized correctly by innerHTML
- // This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName("link").length,
-
- // Get the style information from getAttribute
- // (IE uses .cssText instead)
- style: /top/.test( a.getAttribute("style") ),
-
- // Make sure that URLs aren't manipulated
- // (IE normalizes it by default)
- hrefNormalized: a.getAttribute("href") === "/a",
-
- // Make sure that element opacity exists
- // (IE uses filter instead)
- // Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.5/.test( a.style.opacity ),
-
- // Verify style float existence
- // (IE uses styleFloat instead of cssFloat)
- cssFloat: !!a.style.cssFloat,
-
- // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
- checkOn: !!input.value,
-
- // Make sure that a selected-by-default option has a working selected property.
- // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
- optSelected: opt.selected,
-
- // Tests for enctype support on a form (#6743)
- enctype: !!document.createElement("form").enctype,
-
- // Makes sure cloning an html5 element does not cause problems
- // Where outerHTML is undefined, this still works
- html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
-
- // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
- boxModel: document.compatMode === "CSS1Compat",
-
- // Will be defined later
- deleteExpando: true,
- noCloneEvent: true,
- inlineBlockNeedsLayout: false,
- shrinkWrapBlocks: false,
- reliableMarginRight: true,
- boxSizingReliable: true,
- pixelPosition: false
- };
-
- // Make sure checked status is properly cloned
- input.checked = true;
- support.noCloneChecked = input.cloneNode( true ).checked;
-
- // Make sure that the options inside disabled selects aren't marked as disabled
- // (WebKit marks them as disabled)
- select.disabled = true;
- support.optDisabled = !opt.disabled;
-
- // Support: IE<9
- try {
- delete div.test;
- } catch( e ) {
- support.deleteExpando = false;
- }
-
- // Check if we can trust getAttribute("value")
- input = document.createElement("input");
- input.setAttribute( "value", "" );
- support.input = input.getAttribute( "value" ) === "";
-
- // Check if an input maintains its value after becoming a radio
- input.value = "t";
- input.setAttribute( "type", "radio" );
- support.radioValue = input.value === "t";
-
- // #11217 - WebKit loses check when the name is after the checked attribute
- input.setAttribute( "checked", "t" );
- input.setAttribute( "name", "t" );
-
- fragment = document.createDocumentFragment();
- fragment.appendChild( input );
-
- // Check if a disconnected checkbox will retain its checked
- // value of true after appended to the DOM (IE6/7)
- support.appendChecked = input.checked;
-
- // WebKit doesn't clone checked state correctly in fragments
- support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- // Support: IE<9
- // Opera does not clone events (and typeof div.attachEvent === undefined).
- // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
- if ( div.attachEvent ) {
- div.attachEvent( "onclick", function() {
- support.noCloneEvent = false;
- });
-
- div.cloneNode( true ).click();
- }
-
- // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
- // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
- for ( i in { submit: true, change: true, focusin: true }) {
- div.setAttribute( eventName = "on" + i, "t" );
-
- support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
- }
-
- div.style.backgroundClip = "content-box";
- div.cloneNode( true ).style.backgroundClip = "";
- support.clearCloneStyle = div.style.backgroundClip === "content-box";
-
- // Run tests that need a body at doc ready
- jQuery(function() {
- var container, marginDiv, tds,
- divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
- body = document.getElementsByTagName("body")[0];
-
- if ( !body ) {
- // Return for frameset docs that don't have a body
- return;
- }
-
- container = document.createElement("div");
- container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
-
- body.appendChild( container ).appendChild( div );
-
- // Support: IE8
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
- tds = div.getElementsByTagName("td");
- tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
-
- // Support: IE8
- // Check if empty table cells still have offsetWidth/Height
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
-
- // Check box-sizing and margin behavior
- div.innerHTML = "";
- div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
- support.boxSizing = ( div.offsetWidth === 4 );
- support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
-
- // Use window.getComputedStyle because jsdom on node.js will break without it.
- if ( window.getComputedStyle ) {
- support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
- support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
-
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. (#3333)
- // Fails in WebKit before Feb 2011 nightlies
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- marginDiv = div.appendChild( document.createElement("div") );
- marginDiv.style.cssText = div.style.cssText = divReset;
- marginDiv.style.marginRight = marginDiv.style.width = "0";
- div.style.width = "1px";
-
- support.reliableMarginRight =
- !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
- }
-
- if ( typeof div.style.zoom !== core_strundefined ) {
- // Support: IE<8
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- div.innerHTML = "";
- div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
-
- // Support: IE6
- // Check if elements with layout shrink-wrap their children
- div.style.display = "block";
- div.innerHTML = "<div></div>";
- div.firstChild.style.width = "5px";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
-
- if ( support.inlineBlockNeedsLayout ) {
- // Prevent IE 6 from affecting layout for positioned elements #11048
- // Prevent IE from shrinking the body in IE 7 mode #12869
- // Support: IE<8
- body.style.zoom = 1;
- }
- }
-
- body.removeChild( container );
-
- // Null elements to avoid leaks in IE
- container = div = tds = marginDiv = null;
- });
-
- // Null elements to avoid leaks in IE
- all = select = fragment = opt = a = input = null;
-
- return support;
-})();
-
-var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
- rmultiDash = /([A-Z])/g;
-
-function internalData( elem, name, data, pvt /* Internal Use Only */ ){
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache, ret,
- internalKey = jQuery.expando,
- getByName = typeof name === "string",
-
- // We have to handle DOM nodes and JS objects differently because IE6-7
- // can't GC object references properly across the DOM-JS boundary
- isNode = elem.nodeType,
-
- // Only DOM nodes need the global jQuery cache; JS object data is
- // attached directly to the object so GC can occur automatically
- cache = isNode ? jQuery.cache : elem,
-
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
-
- // Avoid doing any more work than we need to when trying to get data on an
- // object that has no data at all
- if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
- return;
- }
-
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
- } else {
- id = internalKey;
- }
- }
-
- if ( !cache[ id ] ) {
- cache[ id ] = {};
-
- // Avoids exposing jQuery metadata on plain JS objects when the object
- // is serialized using JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
- }
-
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ] = jQuery.extend( cache[ id ], name );
- } else {
- cache[ id ].data = jQuery.extend( cache[ id ].data, name );
- }
- }
-
- thisCache = cache[ id ];
-
- // jQuery data() is stored in a separate object inside the object's internal data
- // cache in order to avoid key collisions between internal data and user-defined
- // data.
- if ( !pvt ) {
- if ( !thisCache.data ) {
- thisCache.data = {};
- }
-
- thisCache = thisCache.data;
- }
-
- if ( data !== undefined ) {
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
-
- // Check for both converted-to-camel and non-converted data property names
- // If a data property was specified
- if ( getByName ) {
-
- // First Try to find as-is property data
- ret = thisCache[ name ];
-
- // Test for null|undefined property data
- if ( ret == null ) {
-
- // Try to find the camelCased property
- ret = thisCache[ jQuery.camelCase( name ) ];
- }
- } else {
- ret = thisCache;
- }
-
- return ret;
-}
-
-function internalRemoveData( elem, name, pvt ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var i, l, thisCache,
- isNode = elem.nodeType,
-
- // See jQuery.data for more information
- cache = isNode ? jQuery.cache : elem,
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
-
- // If there is already no cache entry for this object, there is no
- // purpose in continuing
- if ( !cache[ id ] ) {
- return;
- }
-
- if ( name ) {
-
- thisCache = pvt ? cache[ id ] : cache[ id ].data;
-
- if ( thisCache ) {
-
- // Support array or space separated string names for data keys
- if ( !jQuery.isArray( name ) ) {
-
- // try the string as a key before any manipulation
- if ( name in thisCache ) {
- name = [ name ];
- } else {
-
- // split the camel cased version by spaces unless a key with the spaces exists
- name = jQuery.camelCase( name );
- if ( name in thisCache ) {
- name = [ name ];
- } else {
- name = name.split(" ");
- }
- }
- } else {
- // If "name" is an array of keys...
- // When data is initially created, via ("key", "val") signature,
- // keys will be converted to camelCase.
- // Since there is no way to tell _how_ a key was added, remove
- // both plain key and camelCase key. #12786
- // This will only penalize the array argument path.
- name = name.concat( jQuery.map( name, jQuery.camelCase ) );
- }
-
- for ( i = 0, l = name.length; i < l; i++ ) {
- delete thisCache[ name[i] ];
- }
-
- // If there is no data left in the cache, we want to continue
- // and let the cache object itself get destroyed
- if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
- return;
- }
- }
- }
-
- // See jQuery.data for more information
- if ( !pvt ) {
- delete cache[ id ].data;
-
- // Don't destroy the parent cache unless the internal data object
- // had been the only thing left in it
- if ( !isEmptyDataObject( cache[ id ] ) ) {
- return;
- }
- }
-
- // Destroy the cache
- if ( isNode ) {
- jQuery.cleanData( [ elem ], true );
-
- // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
- } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
- delete cache[ id ];
-
- // When all else fails, null
- } else {
- cache[ id ] = null;
- }
-}
-
-jQuery.extend({
- cache: {},
-
- // Unique for each copy of jQuery on the page
- // Non-digits removed to match rinlinejQuery
- expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
-
- // The following elements throw uncatchable exceptions if you
- // attempt to add expando properties to them.
- noData: {
- "embed": true,
- // Ban all objects except for Flash (which handle expandos)
- "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
- "applet": true
- },
-
- hasData: function( elem ) {
- elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
- return !!elem && !isEmptyDataObject( elem );
- },
-
- data: function( elem, name, data ) {
- return internalData( elem, name, data );
- },
-
- removeData: function( elem, name ) {
- return internalRemoveData( elem, name );
- },
-
- // For internal use only.
- _data: function( elem, name, data ) {
- return internalData( elem, name, data, true );
- },
-
- _removeData: function( elem, name ) {
- return internalRemoveData( elem, name, true );
- },
-
- // A method for determining if a DOM node can handle the data expando
- acceptData: function( elem ) {
- // Do not set data on non-element because it will not be cleared (#8335).
- if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
- return false;
- }
-
- var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
-
- // nodes accept data unless otherwise specified; rejection can be conditional
- return !noData || noData !== true && elem.getAttribute("classid") === noData;
- }
-});
-
-jQuery.fn.extend({
- data: function( key, value ) {
- var attrs, name,
- elem = this[0],
- i = 0,
- data = null;
-
- // Gets all values
- if ( key === undefined ) {
- if ( this.length ) {
- data = jQuery.data( elem );
-
- if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
- attrs = elem.attributes;
- for ( ; i < attrs.length; i++ ) {
- name = attrs[i].name;
-
- if ( !name.indexOf( "data-" ) ) {
- name = jQuery.camelCase( name.slice(5) );
-
- dataAttr( elem, name, data[ name ] );
- }
- }
- jQuery._data( elem, "parsedAttrs", true );
- }
- }
-
- return data;
- }
-
- // Sets multiple values
- if ( typeof key === "object" ) {
- return this.each(function() {
- jQuery.data( this, key );
- });
- }
-
- return jQuery.access( this, function( value ) {
-
- if ( value === undefined ) {
- // Try to fetch any internally stored data first
- return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
- }
-
- this.each(function() {
- jQuery.data( this, key, value );
- });
- }, null, value, arguments.length > 1, null, true );
- },
-
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
-});
-
-function dataAttr( elem, key, data ) {
- // If nothing was found internally, try to fetch any
- // data from the HTML5 data-* attribute
- if ( data === undefined && elem.nodeType === 1 ) {
-
- var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
-
- data = elem.getAttribute( name );
-
- if ( typeof data === "string" ) {
- try {
- data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- // Only convert to a number if it doesn't change the string
- +data + "" === data ? +data :
- rbrace.test( data ) ? jQuery.parseJSON( data ) :
- data;
- } catch( e ) {}
-
- // Make sure we set the data so it isn't changed later
- jQuery.data( elem, key, data );
-
- } else {
- data = undefined;
- }
- }
-
- return data;
-}
-
-// checks a cache object for emptiness
-function isEmptyDataObject( obj ) {
- var name;
- for ( name in obj ) {
-
- // if the public data object is empty, the private is still empty
- if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
- continue;
- }
- if ( name !== "toJSON" ) {
- return false;
- }
- }
-
- return true;
-}
-jQuery.extend({
- queue: function( elem, type, data ) {
- var queue;
-
- if ( elem ) {
- type = ( type || "fx" ) + "queue";
- queue = jQuery._data( elem, type );
-
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( data ) {
- if ( !queue || jQuery.isArray(data) ) {
- queue = jQuery._data( elem, type, jQuery.makeArray(data) );
- } else {
- queue.push( data );
- }
- }
- return queue || [];
- }
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ),
- startLength = queue.length,
- fn = queue.shift(),
- hooks = jQuery._queueHooks( elem, type ),
- next = function() {
- jQuery.dequeue( elem, type );
- };
-
- // If the fx queue is dequeued, always remove the progress sentinel
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- startLength--;
- }
-
- hooks.cur = fn;
- if ( fn ) {
-
- // Add a progress sentinel to prevent the fx queue from being
- // automatically dequeued
- if ( type === "fx" ) {
- queue.unshift( "inprogress" );
- }
-
- // clear up the last queue stop function
- delete hooks.stop;
- fn.call( elem, next, hooks );
- }
-
- if ( !startLength && hooks ) {
- hooks.empty.fire();
- }
- },
-
- // not intended for public consumption - generates a queueHooks object, or returns the current one
- _queueHooks: function( elem, type ) {
- var key = type + "queueHooks";
- return jQuery._data( elem, key ) || jQuery._data( elem, key, {
- empty: jQuery.Callbacks("once memory").add(function() {
- jQuery._removeData( elem, type + "queue" );
- jQuery._removeData( elem, key );
- })
- });
- }
-});
-
-jQuery.fn.extend({
- queue: function( type, data ) {
- var setter = 2;
-
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- setter--;
- }
-
- if ( arguments.length < setter ) {
- return jQuery.queue( this[0], type );
- }
-
- return data === undefined ?
- this :
- this.each(function() {
- var queue = jQuery.queue( this, type, data );
-
- // ensure a hooks for this queue
- jQuery._queueHooks( this, type );
-
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- dequeue: function( type ) {
- return this.each(function() {
- jQuery.dequeue( this, type );
- });
- },
- // Based off of the plugin by Clint Helfers, with permission.
- // http://blindsignals.com/index.php/2009/07/jquery-delay/
- delay: function( time, type ) {
- time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
- type = type || "fx";
-
- return this.queue( type, function( next, hooks ) {
- var timeout = setTimeout( next, time );
- hooks.stop = function() {
- clearTimeout( timeout );
- };
- });
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
- // Get a promise resolved when queues of a certain type
- // are emptied (fx is the type by default)
- promise: function( type, obj ) {
- var tmp,
- count = 1,
- defer = jQuery.Deferred(),
- elements = this,
- i = this.length,
- resolve = function() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- };
-
- if ( typeof type !== "string" ) {
- obj = type;
- type = undefined;
- }
- type = type || "fx";
-
- while( i-- ) {
- tmp = jQuery._data( elements[ i ], type + "queueHooks" );
- if ( tmp && tmp.empty ) {
- count++;
- tmp.empty.add( resolve );
- }
- }
- resolve();
- return defer.promise( obj );
- }
-});
-var nodeHook, boolHook,
- rclass = /[\t\r\n]/g,
- rreturn = /\r/g,
- rfocusable = /^(?:input|select|textarea|button|object)$/i,
- rclickable = /^(?:a|area)$/i,
- rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
- ruseDefault = /^(?:checked|selected)$/i,
- getSetAttribute = jQuery.support.getSetAttribute,
- getSetInput = jQuery.support.input;
-
-jQuery.fn.extend({
- attr: function( name, value ) {
- return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
- },
-
- removeAttr: function( name ) {
- return this.each(function() {
- jQuery.removeAttr( this, name );
- });
- },
-
- prop: function( name, value ) {
- return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
- },
-
- removeProp: function( name ) {
- name = jQuery.propFix[ name ] || name;
- return this.each(function() {
- // try/catch handles cases where IE balks (such as removing a property on window)
- try {
- this[ name ] = undefined;
- delete this[ name ];
- } catch( e ) {}
- });
- },
-
- addClass: function( value ) {
- var classes, elem, cur, clazz, j,
- i = 0,
- len = this.length,
- proceed = typeof value === "string" && value;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).addClass( value.call( this, j, this.className ) );
- });
- }
-
- if ( proceed ) {
- // The disjunction here is for better compressibility (see removeClass)
- classes = ( value || "" ).match( core_rnotwhite ) || [];
-
- for ( ; i < len; i++ ) {
- elem = this[ i ];
- cur = elem.nodeType === 1 && ( elem.className ?
- ( " " + elem.className + " " ).replace( rclass, " " ) :
- " "
- );
-
- if ( cur ) {
- j = 0;
- while ( (clazz = classes[j++]) ) {
- if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
- cur += clazz + " ";
- }
- }
- elem.className = jQuery.trim( cur );
-
- }
- }
- }
-
- return this;
- },
-
- removeClass: function( value ) {
- var classes, elem, cur, clazz, j,
- i = 0,
- len = this.length,
- proceed = arguments.length === 0 || typeof value === "string" && value;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).removeClass( value.call( this, j, this.className ) );
- });
- }
- if ( proceed ) {
- classes = ( value || "" ).match( core_rnotwhite ) || [];
-
- for ( ; i < len; i++ ) {
- elem = this[ i ];
- // This expression is here for better compressibility (see addClass)
- cur = elem.nodeType === 1 && ( elem.className ?
- ( " " + elem.className + " " ).replace( rclass, " " ) :
- ""
- );
-
- if ( cur ) {
- j = 0;
- while ( (clazz = classes[j++]) ) {
- // Remove *all* instances
- while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
- cur = cur.replace( " " + clazz + " ", " " );
- }
- }
- elem.className = value ? jQuery.trim( cur ) : "";
- }
- }
- }
-
- return this;
- },
-
- toggleClass: function( value, stateVal ) {
- var type = typeof value,
- isBool = typeof stateVal === "boolean";
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( i ) {
- jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
- });
- }
-
- return this.each(function() {
- if ( type === "string" ) {
- // toggle individual class names
- var className,
- i = 0,
- self = jQuery( this ),
- state = stateVal,
- classNames = value.match( core_rnotwhite ) || [];
-
- while ( (className = classNames[ i++ ]) ) {
- // check each className given, space separated list
- state = isBool ? state : !self.hasClass( className );
- self[ state ? "addClass" : "removeClass" ]( className );
- }
-
- // Toggle whole class name
- } else if ( type === core_strundefined || type === "boolean" ) {
- if ( this.className ) {
- // store className if set
- jQuery._data( this, "__className__", this.className );
- }
-
- // If the element has a class name or if we're passed "false",
- // then remove the whole classname (if there was one, the above saved it).
- // Otherwise bring back whatever was previously saved (if anything),
- // falling back to the empty string if nothing was stored.
- this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
- }
- });
- },
-
- hasClass: function( selector ) {
- var className = " " + selector + " ",
- i = 0,
- l = this.length;
- for ( ; i < l; i++ ) {
- if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
- return true;
- }
- }
-
- return false;
- },
-
- val: function( value ) {
- var ret, hooks, isFunction,
- elem = this[0];
-
- if ( !arguments.length ) {
- if ( elem ) {
- hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
-
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
- return ret;
- }
-
- ret = elem.value;
-
- return typeof ret === "string" ?
- // handle most common string cases
- ret.replace(rreturn, "") :
- // handle cases where value is null/undef or number
- ret == null ? "" : ret;
- }
-
- return;
- }
-
- isFunction = jQuery.isFunction( value );
-
- return this.each(function( i ) {
- var val,
- self = jQuery(this);
-
- if ( this.nodeType !== 1 ) {
- return;
- }
-
- if ( isFunction ) {
- val = value.call( this, i, self.val() );
- } else {
- val = value;
- }
-
- // Treat null/undefined as ""; convert numbers to string
- if ( val == null ) {
- val = "";
- } else if ( typeof val === "number" ) {
- val += "";
- } else if ( jQuery.isArray( val ) ) {
- val = jQuery.map(val, function ( value ) {
- return value == null ? "" : value + "";
- });
- }
-
- hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
-
- // If set returns undefined, fall back to normal setting
- if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
- this.value = val;
- }
- });
- }
-});
-
-jQuery.extend({
- valHooks: {
- option: {
- get: function( elem ) {
- // attributes.value is undefined in Blackberry 4.7 but
- // uses .value. See #6932
- var val = elem.attributes.value;
- return !val || val.specified ? elem.value : elem.text;
- }
- },
- select: {
- get: function( elem ) {
- var value, option,
- options = elem.options,
- index = elem.selectedIndex,
- one = elem.type === "select-one" || index < 0,
- values = one ? null : [],
- max = one ? index + 1 : options.length,
- i = index < 0 ?
- max :
- one ? index : 0;
-
- // Loop through all the selected options
- for ( ; i < max; i++ ) {
- option = options[ i ];
-
- // oldIE doesn't update selected after form reset (#2551)
- if ( ( option.selected || i === index ) &&
- // Don't return options that are disabled or in a disabled optgroup
- ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
- ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
-
- // Get the specific value for the option
- value = jQuery( option ).val();
-
- // We don't need an array for one selects
- if ( one ) {
- return value;
- }
-
- // Multi-Selects return an array
- values.push( value );
- }
- }
-
- return values;
- },
-
- set: function( elem, value ) {
- var values = jQuery.makeArray( value );
-
- jQuery(elem).find("option").each(function() {
- this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
- });
-
- if ( !values.length ) {
- elem.selectedIndex = -1;
- }
- return values;
- }
- }
- },
-
- attr: function( elem, name, value ) {
- var hooks, notxml, ret,
- nType = elem.nodeType;
-
- // don't get/set attributes on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
-
- // Fallback to prop when attributes are not supported
- if ( typeof elem.getAttribute === core_strundefined ) {
- return jQuery.prop( elem, name, value );
- }
-
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- // All attributes are lowercase
- // Grab necessary hook if one is defined
- if ( notxml ) {
- name = name.toLowerCase();
- hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
- }
-
- if ( value !== undefined ) {
-
- if ( value === null ) {
- jQuery.removeAttr( elem, name );
-
- } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- elem.setAttribute( name, value + "" );
- return value;
- }
-
- } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
-
- // In IE9+, Flash objects don't have .getAttribute (#12945)
- // Support: IE9+
- if ( typeof elem.getAttribute !== core_strundefined ) {
- ret = elem.getAttribute( name );
- }
-
- // Non-existent attributes return null, we normalize to undefined
- return ret == null ?
- undefined :
- ret;
- }
- },
-
- removeAttr: function( elem, value ) {
- var name, propName,
- i = 0,
- attrNames = value && value.match( core_rnotwhite );
-
- if ( attrNames && elem.nodeType === 1 ) {
- while ( (name = attrNames[i++]) ) {
- propName = jQuery.propFix[ name ] || name;
-
- // Boolean attributes get special treatment (#10870)
- if ( rboolean.test( name ) ) {
- // Set corresponding property to false for boolean attributes
- // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
- if ( !getSetAttribute && ruseDefault.test( name ) ) {
- elem[ jQuery.camelCase( "default-" + name ) ] =
- elem[ propName ] = false;
- } else {
- elem[ propName ] = false;
- }
-
- // See #9699 for explanation of this approach (setting first, then removal)
- } else {
- jQuery.attr( elem, name, "" );
- }
-
- elem.removeAttribute( getSetAttribute ? name : propName );
- }
- }
- },
-
- attrHooks: {
- type: {
- set: function( elem, value ) {
- if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
- // Setting the type on a radio button after the value resets the value in IE6-9
- // Reset value to default in case type is set after value during creation
- var val = elem.value;
- elem.setAttribute( "type", value );
- if ( val ) {
- elem.value = val;
- }
- return value;
- }
- }
- }
- },
-
- propFix: {
- tabindex: "tabIndex",
- readonly: "readOnly",
- "for": "htmlFor",
- "class": "className",
- maxlength: "maxLength",
- cellspacing: "cellSpacing",
- cellpadding: "cellPadding",
- rowspan: "rowSpan",
- colspan: "colSpan",
- usemap: "useMap",
- frameborder: "frameBorder",
- contenteditable: "contentEditable"
- },
-
- prop: function( elem, name, value ) {
- var ret, hooks, notxml,
- nType = elem.nodeType;
-
- // don't get/set properties on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
-
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- if ( notxml ) {
- // Fix name and attach hooks
- name = jQuery.propFix[ name ] || name;
- hooks = jQuery.propHooks[ name ];
- }
-
- if ( value !== undefined ) {
- if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- return ( elem[ name ] = value );
- }
-
- } else {
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
- return elem[ name ];
- }
- }
- },
-
- propHooks: {
- tabIndex: {
- get: function( elem ) {
- // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
- // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
- var attributeNode = elem.getAttributeNode("tabindex");
-
- return attributeNode && attributeNode.specified ?
- parseInt( attributeNode.value, 10 ) :
- rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
- 0 :
- undefined;
- }
- }
- }
-});
-
-// Hook for boolean attributes
-boolHook = {
- get: function( elem, name ) {
- var
- // Use .prop to determine if this attribute is understood as boolean
- prop = jQuery.prop( elem, name ),
-
- // Fetch it accordingly
- attr = typeof prop === "boolean" && elem.getAttribute( name ),
- detail = typeof prop === "boolean" ?
-
- getSetInput && getSetAttribute ?
- attr != null :
- // oldIE fabricates an empty string for missing boolean attributes
- // and conflates checked/selected into attroperties
- ruseDefault.test( name ) ?
- elem[ jQuery.camelCase( "default-" + name ) ] :
- !!attr :
-
- // fetch an attribute node for properties not recognized as boolean
- elem.getAttributeNode( name );
-
- return detail && detail.value !== false ?
- name.toLowerCase() :
- undefined;
- },
- set: function( elem, value, name ) {
- if ( value === false ) {
- // Remove boolean attributes when set to false
- jQuery.removeAttr( elem, name );
- } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
- // IE<8 needs the *property* name
- elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
-
- // Use defaultChecked and defaultSelected for oldIE
- } else {
- elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
- }
-
- return name;
- }
-};
-
-// fix oldIE value attroperty
-if ( !getSetInput || !getSetAttribute ) {
- jQuery.attrHooks.value = {
- get: function( elem, name ) {
- var ret = elem.getAttributeNode( name );
- return jQuery.nodeName( elem, "input" ) ?
-
- // Ignore the value *property* by using defaultValue
- elem.defaultValue :
-
- ret && ret.specified ? ret.value : undefined;
- },
- set: function( elem, value, name ) {
- if ( jQuery.nodeName( elem, "input" ) ) {
- // Does not return so that setAttribute is also used
- elem.defaultValue = value;
- } else {
- // Use nodeHook if defined (#1954); otherwise setAttribute is fine
- return nodeHook && nodeHook.set( elem, value, name );
- }
- }
- };
-}
-
-// IE6/7 do not support getting/setting some attributes with get/setAttribute
-if ( !getSetAttribute ) {
-
- // Use this for any attribute in IE6/7
- // This fixes almost every IE6/7 issue
- nodeHook = jQuery.valHooks.button = {
- get: function( elem, name ) {
- var ret = elem.getAttributeNode( name );
- return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
- ret.value :
- undefined;
- },
- set: function( elem, value, name ) {
- // Set the existing or create a new attribute node
- var ret = elem.getAttributeNode( name );
- if ( !ret ) {
- elem.setAttributeNode(
- (ret = elem.ownerDocument.createAttribute( name ))
- );
- }
-
- ret.value = value += "";
-
- // Break association with cloned elements by also using setAttribute (#9646)
- return name === "value" || value === elem.getAttribute( name ) ?
- value :
- undefined;
- }
- };
-
- // Set contenteditable to false on removals(#10429)
- // Setting to empty string throws an error as an invalid value
- jQuery.attrHooks.contenteditable = {
- get: nodeHook.get,
- set: function( elem, value, name ) {
- nodeHook.set( elem, value === "" ? false : value, name );
- }
- };
-
- // Set width and height to auto instead of 0 on empty string( Bug #8150 )
- // This is for removals
- jQuery.each([ "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- set: function( elem, value ) {
- if ( value === "" ) {
- elem.setAttribute( name, "auto" );
- return value;
- }
- }
- });
- });
-}
-
-
-// Some attributes require a special call on IE
-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
-if ( !jQuery.support.hrefNormalized ) {
- jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- get: function( elem ) {
- var ret = elem.getAttribute( name, 2 );
- return ret == null ? undefined : ret;
- }
- });
- });
-
- // href/src property should get the full normalized URL (#10299/#12915)
- jQuery.each([ "href", "src" ], function( i, name ) {
- jQuery.propHooks[ name ] = {
- get: function( elem ) {
- return elem.getAttribute( name, 4 );
- }
- };
- });
-}
-
-if ( !jQuery.support.style ) {
- jQuery.attrHooks.style = {
- get: function( elem ) {
- // Return undefined in the case of empty string
- // Note: IE uppercases css property names, but if we were to .toLowerCase()
- // .cssText, that would destroy case senstitivity in URL's, like in "background"
- return elem.style.cssText || undefined;
- },
- set: function( elem, value ) {
- return ( elem.style.cssText = value + "" );
- }
- };
-}
-
-// Safari mis-reports the default selected property of an option
-// Accessing the parent's selectedIndex property fixes it
-if ( !jQuery.support.optSelected ) {
- jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
- get: function( elem ) {
- var parent = elem.parentNode;
-
- if ( parent ) {
- parent.selectedIndex;
-
- // Make sure that it also works with optgroups, see #5701
- if ( parent.parentNode ) {
- parent.parentNode.selectedIndex;
- }
- }
- return null;
- }
- });
-}
-
-// IE6/7 call enctype encoding
-if ( !jQuery.support.enctype ) {
- jQuery.propFix.enctype = "encoding";
-}
-
-// Radios and checkboxes getter/setter
-if ( !jQuery.support.checkOn ) {
- jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = {
- get: function( elem ) {
- // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
- return elem.getAttribute("value") === null ? "on" : elem.value;
- }
- };
- });
-}
-jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
- set: function( elem, value ) {
- if ( jQuery.isArray( value ) ) {
- return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
- }
- }
- });
-});
-var rformElems = /^(?:input|select|textarea)$/i,
- rkeyEvent = /^key/,
- rmouseEvent = /^(?:mouse|contextmenu)|click/,
- rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
- rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
-
-function returnTrue() {
- return true;
-}
-
-function returnFalse() {
- return false;
-}
-
-/*
- * Helper functions for managing events -- not part of the public interface.
- * Props to Dean Edwards' addEvent library for many of the ideas.
- */
-jQuery.event = {
-
- global: {},
-
- add: function( elem, types, handler, data, selector ) {
- var tmp, events, t, handleObjIn,
- special, eventHandle, handleObj,
- handlers, type, namespaces, origType,
- elemData = jQuery._data( elem );
-
- // Don't attach events to noData or text/comment nodes (but allow plain objects)
- if ( !elemData ) {
- return;
- }
-
- // Caller can pass in an object of custom data in lieu of the handler
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- selector = handleObjIn.selector;
- }
-
- // Make sure that the handler has a unique ID, used to find/remove it later
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- // Init the element's event structure and main handler, if this is the first
- if ( !(events = elemData.events) ) {
- events = elemData.events = {};
- }
- if ( !(eventHandle = elemData.handle) ) {
- eventHandle = elemData.handle = function( e ) {
- // Discard the second event of a jQuery.event.trigger() and
- // when an event is called after a page has unloaded
- return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
- undefined;
- };
- // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
- eventHandle.elem = elem;
- }
-
- // Handle multiple events separated by a space
- // jQuery(...).bind("mouseover mouseout", fn);
- types = ( types || "" ).match( core_rnotwhite ) || [""];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // If event changes its type, use the special event handlers for the changed type
- special = jQuery.event.special[ type ] || {};
-
- // If selector defined, determine special event api type, otherwise given type
- type = ( selector ? special.delegateType : special.bindType ) || type;
-
- // Update special based on newly reset type
- special = jQuery.event.special[ type ] || {};
-
- // handleObj is passed to all event handlers
- handleObj = jQuery.extend({
- type: type,
- origType: origType,
- data: data,
- handler: handler,
- guid: handler.guid,
- selector: selector,
- needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
- namespace: namespaces.join(".")
- }, handleObjIn );
-
- // Init the event handler queue if we're the first
- if ( !(handlers = events[ type ]) ) {
- handlers = events[ type ] = [];
- handlers.delegateCount = 0;
-
- // Only use addEventListener/attachEvent if the special events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
- // Bind the global event handler to the element
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle, false );
-
- } else if ( elem.attachEvent ) {
- elem.attachEvent( "on" + type, eventHandle );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- // Add to the element's handler list, delegates in front
- if ( selector ) {
- handlers.splice( handlers.delegateCount++, 0, handleObj );
- } else {
- handlers.push( handleObj );
- }
-
- // Keep track of which events have ever been used, for event optimization
- jQuery.event.global[ type ] = true;
- }
-
- // Nullify elem to prevent memory leaks in IE
- elem = null;
- },
-
- // Detach an event or set of events from an element
- remove: function( elem, types, handler, selector, mappedTypes ) {
- var j, handleObj, tmp,
- origCount, t, events,
- special, handlers, type,
- namespaces, origType,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem );
-
- if ( !elemData || !(events = elemData.events) ) {
- return;
- }
-
- // Once for each type.namespace in types; type may be omitted
- types = ( types || "" ).match( core_rnotwhite ) || [""];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // Unbind all events (on this namespace, if provided) for the element
- if ( !type ) {
- for ( type in events ) {
- jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
- }
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
- type = ( selector ? special.delegateType : special.bindType ) || type;
- handlers = events[ type ] || [];
- tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
-
- // Remove matching events
- origCount = j = handlers.length;
- while ( j-- ) {
- handleObj = handlers[ j ];
-
- if ( ( mappedTypes || origType === handleObj.origType ) &&
- ( !handler || handler.guid === handleObj.guid ) &&
- ( !tmp || tmp.test( handleObj.namespace ) ) &&
- ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
- handlers.splice( j, 1 );
-
- if ( handleObj.selector ) {
- handlers.delegateCount--;
- }
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
- }
-
- // Remove generic event handler if we removed something and no more handlers exist
- // (avoids potential for endless recursion during removal of special event handlers)
- if ( origCount && !handlers.length ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- delete events[ type ];
- }
- }
-
- // Remove the expando if it's no longer used
- if ( jQuery.isEmptyObject( events ) ) {
- delete elemData.handle;
-
- // removeData also checks for emptiness and clears the expando if empty
- // so use it instead of delete
- jQuery._removeData( elem, "events" );
- }
- },
-
- trigger: function( event, data, elem, onlyHandlers ) {
- var handle, ontype, cur,
- bubbleType, special, tmp, i,
- eventPath = [ elem || document ],
- type = core_hasOwn.call( event, "type" ) ? event.type : event,
- namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
-
- cur = tmp = elem = elem || document;
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- // focus/blur morphs to focusin/out; ensure we're not firing them right now
- if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
- return;
- }
-
- if ( type.indexOf(".") >= 0 ) {
- // Namespaced trigger; create a regexp to match event type in handle()
- namespaces = type.split(".");
- type = namespaces.shift();
- namespaces.sort();
- }
- ontype = type.indexOf(":") < 0 && "on" + type;
-
- // Caller can pass in a jQuery.Event object, Object, or just an event type string
- event = event[ jQuery.expando ] ?
- event :
- new jQuery.Event( type, typeof event === "object" && event );
-
- event.isTrigger = true;
- event.namespace = namespaces.join(".");
- event.namespace_re = event.namespace ?
- new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
- null;
-
- // Clean up the event in case it is being reused
- event.result = undefined;
- if ( !event.target ) {
- event.target = elem;
- }
-
- // Clone any incoming data and prepend the event, creating the handler arg list
- data = data == null ?
- [ event ] :
- jQuery.makeArray( data, [ event ] );
-
- // Allow special events to draw outside the lines
- special = jQuery.event.special[ type ] || {};
- if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
- return;
- }
-
- // Determine event propagation path in advance, per W3C events spec (#9951)
- // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
- if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
- bubbleType = special.delegateType || type;
- if ( !rfocusMorph.test( bubbleType + type ) ) {
- cur = cur.parentNode;
- }
- for ( ; cur; cur = cur.parentNode ) {
- eventPath.push( cur );
- tmp = cur;
- }
-
- // Only add window if we got to document (e.g., not plain obj or detached DOM)
- if ( tmp === (elem.ownerDocument || document) ) {
- eventPath.push( tmp.defaultView || tmp.parentWindow || window );
- }
- }
-
- // Fire handlers on the event path
- i = 0;
- while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
-
- event.type = i > 1 ?
- bubbleType :
- special.bindType || type;
-
- // jQuery handler
- handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
- if ( handle ) {
- handle.apply( cur, data );
- }
-
- // Native handler
- handle = ontype && cur[ ontype ];
- if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
- event.preventDefault();
- }
- }
- event.type = type;
-
- // If nobody prevented the default action, do it now
- if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
- if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
- !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
-
- // Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction() check here because IE6/7 fails that test.
- // Don't do default actions on window, that's where global variables be (#6170)
- if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
-
- // Don't re-trigger an onFOO event when we call its FOO() method
- tmp = elem[ ontype ];
-
- if ( tmp ) {
- elem[ ontype ] = null;
- }
-
- // Prevent re-triggering of the same event, since we already bubbled it above
- jQuery.event.triggered = type;
- try {
- elem[ type ]();
- } catch ( e ) {
- // IE<9 dies on focus/blur to hidden element (#1486,#12518)
- // only reproducible on winXP IE8 native, not IE9 in IE8 mode
- }
- jQuery.event.triggered = undefined;
-
- if ( tmp ) {
- elem[ ontype ] = tmp;
- }
- }
- }
- }
-
- return event.result;
- },
-
- dispatch: function( event ) {
-
- // Make a writable jQuery.Event from the native event object
- event = jQuery.event.fix( event );
-
- var i, ret, handleObj, matched, j,
- handlerQueue = [],
- args = core_slice.call( arguments ),
- handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
- special = jQuery.event.special[ event.type ] || {};
-
- // Use the fix-ed jQuery.Event rather than the (read-only) native event
- args[0] = event;
- event.delegateTarget = this;
-
- // Call the preDispatch hook for the mapped type, and let it bail if desired
- if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
- return;
- }
-
- // Determine handlers
- handlerQueue = jQuery.event.handlers.call( this, event, handlers );
-
- // Run delegates first; they may want to stop propagation beneath us
- i = 0;
- while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
- event.currentTarget = matched.elem;
-
- j = 0;
- while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
-
- // Triggered event must either 1) have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
- if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
-
- event.handleObj = handleObj;
- event.data = handleObj.data;
-
- ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
- .apply( matched.elem, args );
-
- if ( ret !== undefined ) {
- if ( (event.result = ret) === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
- }
-
- // Call the postDispatch hook for the mapped type
- if ( special.postDispatch ) {
- special.postDispatch.call( this, event );
- }
-
- return event.result;
- },
-
- handlers: function( event, handlers ) {
- var sel, handleObj, matches, i,
- handlerQueue = [],
- delegateCount = handlers.delegateCount,
- cur = event.target;
-
- // Find delegate handlers
- // Black-hole SVG <use> instance trees (#13180)
- // Avoid non-left-click bubbling in Firefox (#3861)
- if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
-
- for ( ; cur != this; cur = cur.parentNode || this ) {
-
- // Don't check non-elements (#13208)
- // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
- if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
- matches = [];
- for ( i = 0; i < delegateCount; i++ ) {
- handleObj = handlers[ i ];
-
- // Don't conflict with Object.prototype properties (#13203)
- sel = handleObj.selector + " ";
-
- if ( matches[ sel ] === undefined ) {
- matches[ sel ] = handleObj.needsContext ?
- jQuery( sel, this ).index( cur ) >= 0 :
- jQuery.find( sel, this, null, [ cur ] ).length;
- }
- if ( matches[ sel ] ) {
- matches.push( handleObj );
- }
- }
- if ( matches.length ) {
- handlerQueue.push({ elem: cur, handlers: matches });
- }
- }
- }
- }
-
- // Add the remaining (directly-bound) handlers
- if ( delegateCount < handlers.length ) {
- handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
- }
-
- return handlerQueue;
- },
-
- fix: function( event ) {
- if ( event[ jQuery.expando ] ) {
- return event;
- }
-
- // Create a writable copy of the event object and normalize some properties
- var i, prop, copy,
- type = event.type,
- originalEvent = event,
- fixHook = this.fixHooks[ type ];
-
- if ( !fixHook ) {
- this.fixHooks[ type ] = fixHook =
- rmouseEvent.test( type ) ? this.mouseHooks :
- rkeyEvent.test( type ) ? this.keyHooks :
- {};
- }
- copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
-
- event = new jQuery.Event( originalEvent );
-
- i = copy.length;
- while ( i-- ) {
- prop = copy[ i ];
- event[ prop ] = originalEvent[ prop ];
- }
-
- // Support: IE<9
- // Fix target property (#1925)
- if ( !event.target ) {
- event.target = originalEvent.srcElement || document;
- }
-
- // Support: Chrome 23+, Safari?
- // Target should not be a text node (#504, #13143)
- if ( event.target.nodeType === 3 ) {
- event.target = event.target.parentNode;
- }
-
- // Support: IE<9
- // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
- event.metaKey = !!event.metaKey;
-
- return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
- },
-
- // Includes some event props shared by KeyEvent and MouseEvent
- props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
-
- fixHooks: {},
-
- keyHooks: {
- props: "char charCode key keyCode".split(" "),
- filter: function( event, original ) {
-
- // Add which for key events
- if ( event.which == null ) {
- event.which = original.charCode != null ? original.charCode : original.keyCode;
- }
-
- return event;
- }
- },
-
- mouseHooks: {
- props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
- filter: function( event, original ) {
- var body, eventDoc, doc,
- button = original.button,
- fromElement = original.fromElement;
-
- // Calculate pageX/Y if missing and clientX/Y available
- if ( event.pageX == null && original.clientX != null ) {
- eventDoc = event.target.ownerDocument || document;
- doc = eventDoc.documentElement;
- body = eventDoc.body;
-
- event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
- event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
- }
-
- // Add relatedTarget, if necessary
- if ( !event.relatedTarget && fromElement ) {
- event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
- }
-
- // Add which for click: 1 === left; 2 === middle; 3 === right
- // Note: button is not normalized, so don't use it
- if ( !event.which && button !== undefined ) {
- event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
- }
-
- return event;
- }
- },
-
- special: {
- load: {
- // Prevent triggered image.load events from bubbling to window.load
- noBubble: true
- },
- click: {
- // For checkbox, fire native event so checked state will be right
- trigger: function() {
- if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
- this.click();
- return false;
- }
- }
- },
- focus: {
- // Fire native event if possible so blur/focus sequence is correct
- trigger: function() {
- if ( this !== document.activeElement && this.focus ) {
- try {
- this.focus();
- return false;
- } catch ( e ) {
- // Support: IE<9
- // If we error on focus to hidden element (#1486, #12518),
- // let .trigger() run the handlers
- }
- }
- },
- delegateType: "focusin"
- },
- blur: {
- trigger: function() {
- if ( this === document.activeElement && this.blur ) {
- this.blur();
- return false;
- }
- },
- delegateType: "focusout"
- },
-
- beforeunload: {
- postDispatch: function( event ) {
-
- // Even when returnValue equals to undefined Firefox will still show alert
- if ( event.result !== undefined ) {
- event.originalEvent.returnValue = event.result;
- }
- }
- }
- },
-
- simulate: function( type, elem, event, bubble ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
- var e = jQuery.extend(
- new jQuery.Event(),
- event,
- { type: type,
- isSimulated: true,
- originalEvent: {}
- }
- );
- if ( bubble ) {
- jQuery.event.trigger( e, null, elem );
- } else {
- jQuery.event.dispatch.call( elem, e );
- }
- if ( e.isDefaultPrevented() ) {
- event.preventDefault();
- }
- }
-};
-
-jQuery.removeEvent = document.removeEventListener ?
- function( elem, type, handle ) {
- if ( elem.removeEventListener ) {
- elem.removeEventListener( type, handle, false );
- }
- } :
- function( elem, type, handle ) {
- var name = "on" + type;
-
- if ( elem.detachEvent ) {
-
- // #8545, #7054, preventing memory leaks for custom events in IE6-8
- // detachEvent needed property on element, by name of that event, to properly expose it to GC
- if ( typeof elem[ name ] === core_strundefined ) {
- elem[ name ] = null;
- }
-
- elem.detachEvent( name, handle );
- }
- };
-
-jQuery.Event = function( src, props ) {
- // Allow instantiation without the 'new' keyword
- if ( !(this instanceof jQuery.Event) ) {
- return new jQuery.Event( src, props );
- }
-
- // Event object
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
-
- // Events bubbling up the document may have been marked as prevented
- // by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
- src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
-
- // Event type
- } else {
- this.type = src;
- }
-
- // Put explicitly provided properties onto the event object
- if ( props ) {
- jQuery.extend( this, props );
- }
-
- // Create a timestamp if incoming event doesn't have one
- this.timeStamp = src && src.timeStamp || jQuery.now();
-
- // Mark it as fixed
- this[ jQuery.expando ] = true;
-};
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse,
-
- preventDefault: function() {
- var e = this.originalEvent;
-
- this.isDefaultPrevented = returnTrue;
- if ( !e ) {
- return;
- }
-
- // If preventDefault exists, run it on the original event
- if ( e.preventDefault ) {
- e.preventDefault();
-
- // Support: IE
- // Otherwise set the returnValue property of the original event to false
- } else {
- e.returnValue = false;
- }
- },
- stopPropagation: function() {
- var e = this.originalEvent;
-
- this.isPropagationStopped = returnTrue;
- if ( !e ) {
- return;
- }
- // If stopPropagation exists, run it on the original event
- if ( e.stopPropagation ) {
- e.stopPropagation();
- }
-
- // Support: IE
- // Set the cancelBubble property of the original event to true
- e.cancelBubble = true;
- },
- stopImmediatePropagation: function() {
- this.isImmediatePropagationStopped = returnTrue;
- this.stopPropagation();
- }
-};
-
-// Create mouseenter/leave events using mouseover/out and event-time checks
-jQuery.each({
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-}, function( orig, fix ) {
- jQuery.event.special[ orig ] = {
- delegateType: fix,
- bindType: fix,
-
- handle: function( event ) {
- var ret,
- target = this,
- related = event.relatedTarget,
- handleObj = event.handleObj;
-
- // For mousenter/leave call the handler if related is outside the target.
- // NB: No relatedTarget if the mouse left/entered the browser window
- if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
- event.type = handleObj.origType;
- ret = handleObj.handler.apply( this, arguments );
- event.type = fix;
- }
- return ret;
- }
- };
-});
-
-// IE submit delegation
-if ( !jQuery.support.submitBubbles ) {
-
- jQuery.event.special.submit = {
- setup: function() {
- // Only need this for delegated form submit events
- if ( jQuery.nodeName( this, "form" ) ) {
- return false;
- }
-
- // Lazy-add a submit handler when a descendant form may potentially be submitted
- jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
- // Node name check avoids a VML-related crash in IE (#9807)
- var elem = e.target,
- form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
- if ( form && !jQuery._data( form, "submitBubbles" ) ) {
- jQuery.event.add( form, "submit._submit", function( event ) {
- event._submit_bubble = true;
- });
- jQuery._data( form, "submitBubbles", true );
- }
- });
- // return undefined since we don't need an event listener
- },
-
- postDispatch: function( event ) {
- // If form was submitted by the user, bubble the event up the tree
- if ( event._submit_bubble ) {
- delete event._submit_bubble;
- if ( this.parentNode && !event.isTrigger ) {
- jQuery.event.simulate( "submit", this.parentNode, event, true );
- }
- }
- },
-
- teardown: function() {
- // Only need this for delegated form submit events
- if ( jQuery.nodeName( this, "form" ) ) {
- return false;
- }
-
- // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
- jQuery.event.remove( this, "._submit" );
- }
- };
-}
-
-// IE change delegation and checkbox/radio fix
-if ( !jQuery.support.changeBubbles ) {
-
- jQuery.event.special.change = {
-
- setup: function() {
-
- if ( rformElems.test( this.nodeName ) ) {
- // IE doesn't fire change on a check/radio until blur; trigger it on click
- // after a propertychange. Eat the blur-change in special.change.handle.
- // This still fires onchange a second time for check/radio after blur.
- if ( this.type === "checkbox" || this.type === "radio" ) {
- jQuery.event.add( this, "propertychange._change", function( event ) {
- if ( event.originalEvent.propertyName === "checked" ) {
- this._just_changed = true;
- }
- });
- jQuery.event.add( this, "click._change", function( event ) {
- if ( this._just_changed && !event.isTrigger ) {
- this._just_changed = false;
- }
- // Allow triggered, simulated change events (#11500)
- jQuery.event.simulate( "change", this, event, true );
- });
- }
- return false;
- }
- // Delegated event; lazy-add a change handler on descendant inputs
- jQuery.event.add( this, "beforeactivate._change", function( e ) {
- var elem = e.target;
-
- if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
- jQuery.event.add( elem, "change._change", function( event ) {
- if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
- jQuery.event.simulate( "change", this.parentNode, event, true );
- }
- });
- jQuery._data( elem, "changeBubbles", true );
- }
- });
- },
-
- handle: function( event ) {
- var elem = event.target;
-
- // Swallow native change events from checkbox/radio, we already triggered them above
- if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
- return event.handleObj.handler.apply( this, arguments );
- }
- },
-
- teardown: function() {
- jQuery.event.remove( this, "._change" );
-
- return !rformElems.test( this.nodeName );
- }
- };
-}
-
-// Create "bubbling" focus and blur events
-if ( !jQuery.support.focusinBubbles ) {
- jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
-
- // Attach a single capturing handler while someone wants focusin/focusout
- var attaches = 0,
- handler = function( event ) {
- jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
- };
-
- jQuery.event.special[ fix ] = {
- setup: function() {
- if ( attaches++ === 0 ) {
- document.addEventListener( orig, handler, true );
- }
- },
- teardown: function() {
- if ( --attaches === 0 ) {
- document.removeEventListener( orig, handler, true );
- }
- }
- };
- });
-}
-
-jQuery.fn.extend({
-
- on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
- var type, origFn;
-
- // Types can be a map of types/handlers
- if ( typeof types === "object" ) {
- // ( types-Object, selector, data )
- if ( typeof selector !== "string" ) {
- // ( types-Object, data )
- data = data || selector;
- selector = undefined;
- }
- for ( type in types ) {
- this.on( type, selector, data, types[ type ], one );
- }
- return this;
- }
-
- if ( data == null && fn == null ) {
- // ( types, fn )
- fn = selector;
- data = selector = undefined;
- } else if ( fn == null ) {
- if ( typeof selector === "string" ) {
- // ( types, selector, fn )
- fn = data;
- data = undefined;
- } else {
- // ( types, data, fn )
- fn = data;
- data = selector;
- selector = undefined;
- }
- }
- if ( fn === false ) {
- fn = returnFalse;
- } else if ( !fn ) {
- return this;
- }
-
- if ( one === 1 ) {
- origFn = fn;
- fn = function( event ) {
- // Can use an empty set, since event contains the info
- jQuery().off( event );
- return origFn.apply( this, arguments );
- };
- // Use same guid so caller can remove using origFn
- fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
- }
- return this.each( function() {
- jQuery.event.add( this, types, fn, data, selector );
- });
- },
- one: function( types, selector, data, fn ) {
- return this.on( types, selector, data, fn, 1 );
- },
- off: function( types, selector, fn ) {
- var handleObj, type;
- if ( types && types.preventDefault && types.handleObj ) {
- // ( event ) dispatched jQuery.Event
- handleObj = types.handleObj;
- jQuery( types.delegateTarget ).off(
- handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
- handleObj.selector,
- handleObj.handler
- );
- return this;
- }
- if ( typeof types === "object" ) {
- // ( types-object [, selector] )
- for ( type in types ) {
- this.off( type, selector, types[ type ] );
- }
- return this;
- }
- if ( selector === false || typeof selector === "function" ) {
- // ( types [, fn] )
- fn = selector;
- selector = undefined;
- }
- if ( fn === false ) {
- fn = returnFalse;
- }
- return this.each(function() {
- jQuery.event.remove( this, types, fn, selector );
- });
- },
-
- bind: function( types, data, fn ) {
- return this.on( types, null, data, fn );
- },
- unbind: function( types, fn ) {
- return this.off( types, null, fn );
- },
-
- delegate: function( selector, types, data, fn ) {
- return this.on( types, selector, data, fn );
- },
- undelegate: function( selector, types, fn ) {
- // ( namespace ) or ( selector, types [, fn] )
- return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
- },
-
- trigger: function( type, data ) {
- return this.each(function() {
- jQuery.event.trigger( type, data, this );
- });
- },
- triggerHandler: function( type, data ) {
- var elem = this[0];
- if ( elem ) {
- return jQuery.event.trigger( type, data, elem, true );
- }
- }
-});
-/*!
- * Sizzle CSS Selector Engine
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license
- * http://sizzlejs.com/
- */
-(function( window, undefined ) {
-
-var i,
- cachedruns,
- Expr,
- getText,
- isXML,
- compile,
- hasDuplicate,
- outermostContext,
-
- // Local document vars
- setDocument,
- document,
- docElem,
- documentIsXML,
- rbuggyQSA,
- rbuggyMatches,
- matches,
- contains,
- sortOrder,
-
- // Instance-specific data
- expando = "sizzle" + -(new Date()),
- preferredDoc = window.document,
- support = {},
- dirruns = 0,
- done = 0,
- classCache = createCache(),
- tokenCache = createCache(),
- compilerCache = createCache(),
-
- // General-purpose constants
- strundefined = typeof undefined,
- MAX_NEGATIVE = 1 << 31,
-
- // Array methods
- arr = [],
- pop = arr.pop,
- push = arr.push,
- slice = arr.slice,
- // Use a stripped-down indexOf if we can't use a native one
- indexOf = arr.indexOf || function( elem ) {
- var i = 0,
- len = this.length;
- for ( ; i < len; i++ ) {
- if ( this[i] === elem ) {
- return i;
- }
- }
- return -1;
- },
-
-
- // Regular expressions
-
- // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
- whitespace = "[\\x20\\t\\r\\n\\f]",
- // http://www.w3.org/TR/css3-syntax/#characters
- characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
-
- // Loosely modeled on CSS identifier characters
- // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
- // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
- identifier = characterEncoding.replace( "w", "w#" ),
-
- // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
- operators = "([*^$|!~]?=)",
- attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
- "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
-
- // Prefer arguments quoted,
- // then not containing pseudos/brackets,
- // then attribute selectors/non-parenthetical expressions,
- // then anything else
- // These preferences are here to reduce the number of selectors
- // needing tokenize in the PSEUDO preFilter
- pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
-
- // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
- rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
-
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
- rpseudo = new RegExp( pseudos ),
- ridentifier = new RegExp( "^" + identifier + "$" ),
-
- matchExpr = {
- "ID": new RegExp( "^#(" + characterEncoding + ")" ),
- "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
- "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
- "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
- "ATTR": new RegExp( "^" + attributes ),
- "PSEUDO": new RegExp( "^" + pseudos ),
- "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
- "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
- "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- // For use in libraries implementing .is()
- // We use this for POS matching in `select`
- "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
- whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
- },
-
- rsibling = /[\x20\t\r\n\f]*[+~]/,
-
- rnative = /^[^{]+\{\s*\[native code/,
-
- // Easily-parseable/retrievable ID or TAG or CLASS selectors
- rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
- rinputs = /^(?:input|select|textarea|button)$/i,
- rheader = /^h\d$/i,
-
- rescape = /'|\\/g,
- rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
-
- // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
- runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
- funescape = function( _, escaped ) {
- var high = "0x" + escaped - 0x10000;
- // NaN means non-codepoint
- return high !== high ?
- escaped :
- // BMP codepoint
- high < 0 ?
- String.fromCharCode( high + 0x10000 ) :
- // Supplemental Plane codepoint (surrogate pair)
- String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
- };
-
-// Use a stripped-down slice if we can't use a native one
-try {
- slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType;
-} catch ( e ) {
- slice = function( i ) {
- var elem,
- results = [];
- while ( (elem = this[i++]) ) {
- results.push( elem );
- }
- return results;
- };
-}
-
-/**
- * For feature detection
- * @param {Function} fn The function to test for native support
- */
-function isNative( fn ) {
- return rnative.test( fn + "" );
-}
-
-/**
- * Create key-value caches of limited size
- * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
- * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- * deleting the oldest entry
- */
-function createCache() {
- var cache,
- keys = [];
-
- return (cache = function( key, value ) {
- // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
- if ( keys.push( key += " " ) > Expr.cacheLength ) {
- // Only keep the most recent entries
- delete cache[ keys.shift() ];
- }
- return (cache[ key ] = value);
- });
-}
-
-/**
- * Mark a function for special use by Sizzle
- * @param {Function} fn The function to mark
- */
-function markFunction( fn ) {
- fn[ expando ] = true;
- return fn;
-}
-
-/**
- * Support testing using an element
- * @param {Function} fn Passed the created div and expects a boolean result
- */
-function assert( fn ) {
- var div = document.createElement("div");
-
- try {
- return fn( div );
- } catch (e) {
- return false;
- } finally {
- // release memory in IE
- div = null;
- }
-}
-
-function Sizzle( selector, context, results, seed ) {
- var match, elem, m, nodeType,
- // QSA vars
- i, groups, old, nid, newContext, newSelector;
-
- if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
- setDocument( context );
- }
-
- context = context || document;
- results = results || [];
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
- return [];
- }
-
- if ( !documentIsXML && !seed ) {
-
- // Shortcuts
- if ( (match = rquickExpr.exec( selector )) ) {
- // Speed-up: Sizzle("#ID")
- if ( (m = match[1]) ) {
- if ( nodeType === 9 ) {
- elem = context.getElementById( m );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE, Opera, and Webkit return items
- // by name instead of ID
- if ( elem.id === m ) {
- results.push( elem );
- return results;
- }
- } else {
- return results;
- }
- } else {
- // Context is not a document
- if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
- contains( context, elem ) && elem.id === m ) {
- results.push( elem );
- return results;
- }
- }
-
- // Speed-up: Sizzle("TAG")
- } else if ( match[2] ) {
- push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
- return results;
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) {
- push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
- return results;
- }
- }
-
- // QSA path
- if ( support.qsa && !rbuggyQSA.test(selector) ) {
- old = true;
- nid = expando;
- newContext = context;
- newSelector = nodeType === 9 && selector;
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- groups = tokenize( selector );
-
- if ( (old = context.getAttribute("id")) ) {
- nid = old.replace( rescape, "\\$&" );
- } else {
- context.setAttribute( "id", nid );
- }
- nid = "[id='" + nid + "'] ";
-
- i = groups.length;
- while ( i-- ) {
- groups[i] = nid + toSelector( groups[i] );
- }
- newContext = rsibling.test( selector ) && context.parentNode || context;
- newSelector = groups.join(",");
- }
-
- if ( newSelector ) {
- try {
- push.apply( results, slice.call( newContext.querySelectorAll(
- newSelector
- ), 0 ) );
- return results;
- } catch(qsaError) {
- } finally {
- if ( !old ) {
- context.removeAttribute("id");
- }
- }
- }
- }
- }
-
- // All others
- return select( selector.replace( rtrim, "$1" ), context, results, seed );
-}
-
-/**
- * Detect xml
- * @param {Element|Object} elem An element or a document
- */
-isXML = Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = elem && (elem.ownerDocument || elem).documentElement;
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-/**
- * Sets document-related variables once based on the current document
- * @param {Element|Object} [doc] An element or document object to use to set the document
- * @returns {Object} Returns the current document
- */
-setDocument = Sizzle.setDocument = function( node ) {
- var doc = node ? node.ownerDocument || node : preferredDoc;
-
- // If no document and documentElement is available, return
- if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
- return document;
- }
-
- // Set our document
- document = doc;
- docElem = doc.documentElement;
-
- // Support tests
- documentIsXML = isXML( doc );
-
- // Check if getElementsByTagName("*") returns only elements
- support.tagNameNoComments = assert(function( div ) {
- div.appendChild( doc.createComment("") );
- return !div.getElementsByTagName("*").length;
- });
-
- // Check if attributes should be retrieved by attribute nodes
- support.attributes = assert(function( div ) {
- div.innerHTML = "<select></select>";
- var type = typeof div.lastChild.getAttribute("multiple");
- // IE8 returns a string for some attributes even when not present
- return type !== "boolean" && type !== "string";
- });
-
- // Check if getElementsByClassName can be trusted
- support.getByClassName = assert(function( div ) {
- // Opera can't find a second classname (in 9.6)
- div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
- if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
- return false;
- }
-
- // Safari 3.2 caches class attributes and doesn't catch changes
- div.lastChild.className = "e";
- return div.getElementsByClassName("e").length === 2;
- });
-
- // Check if getElementById returns elements by name
- // Check if getElementsByName privileges form controls or returns elements by ID
- support.getByName = assert(function( div ) {
- // Inject content
- div.id = expando + 0;
- div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
- docElem.insertBefore( div, docElem.firstChild );
-
- // Test
- var pass = doc.getElementsByName &&
- // buggy browsers will return fewer than the correct 2
- doc.getElementsByName( expando ).length === 2 +
- // buggy browsers will return more than the correct 0
- doc.getElementsByName( expando + 0 ).length;
- support.getIdNotName = !doc.getElementById( expando );
-
- // Cleanup
- docElem.removeChild( div );
-
- return pass;
- });
-
- // IE6/7 return modified attributes
- Expr.attrHandle = assert(function( div ) {
- div.innerHTML = "<a href='#'></a>";
- return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
- div.firstChild.getAttribute("href") === "#";
- }) ?
- {} :
- {
- "href": function( elem ) {
- return elem.getAttribute( "href", 2 );
- },
- "type": function( elem ) {
- return elem.getAttribute("type");
- }
- };
-
- // ID find and filter
- if ( support.getIdNotName ) {
- Expr.find["ID"] = function( id, context ) {
- if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
- var m = context.getElementById( id );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- };
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- return elem.getAttribute("id") === attrId;
- };
- };
- } else {
- Expr.find["ID"] = function( id, context ) {
- if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
- var m = context.getElementById( id );
-
- return m ?
- m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
- [m] :
- undefined :
- [];
- }
- };
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
- return node && node.value === attrId;
- };
- };
- }
-
- // Tag
- Expr.find["TAG"] = support.tagNameNoComments ?
- function( tag, context ) {
- if ( typeof context.getElementsByTagName !== strundefined ) {
- return context.getElementsByTagName( tag );
- }
- } :
- function( tag, context ) {
- var elem,
- tmp = [],
- i = 0,
- results = context.getElementsByTagName( tag );
-
- // Filter out possible comments
- if ( tag === "*" ) {
- while ( (elem = results[i++]) ) {
- if ( elem.nodeType === 1 ) {
- tmp.push( elem );
- }
- }
-
- return tmp;
- }
- return results;
- };
-
- // Name
- Expr.find["NAME"] = support.getByName && function( tag, context ) {
- if ( typeof context.getElementsByName !== strundefined ) {
- return context.getElementsByName( name );
- }
- };
-
- // Class
- Expr.find["CLASS"] = support.getByClassName && function( className, context ) {
- if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) {
- return context.getElementsByClassName( className );
- }
- };
-
- // QSA and matchesSelector support
-
- // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
- rbuggyMatches = [];
-
- // qSa(:focus) reports false when true (Chrome 21),
- // no need to also add to buggyMatches since matches checks buggyQSA
- // A support test would require too much code (would include document ready)
- rbuggyQSA = [ ":focus" ];
-
- if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
- // Build QSA regex
- // Regex strategy adopted from Diego Perini
- assert(function( div ) {
- // Select is set to empty string on purpose
- // This is to test IE's treatment of not explictly
- // setting a boolean content attribute,
- // since its presence should be enough
- // http://bugs.jquery.com/ticket/12359
- div.innerHTML = "<select><option selected=''></option></select>";
-
- // IE8 - Some boolean attributes are not treated correctly
- if ( !div.querySelectorAll("[selected]").length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
- }
-
- // Webkit/Opera - :checked should return selected option elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":checked").length ) {
- rbuggyQSA.push(":checked");
- }
- });
-
- assert(function( div ) {
-
- // Opera 10-12/IE8 - ^= $= *= and empty values
- // Should not select anything
- div.innerHTML = "<input type='hidden' i=''/>";
- if ( div.querySelectorAll("[i^='']").length ) {
- rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
- }
-
- // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":enabled").length ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
-
- // Opera 10-11 does not throw on post-comma invalid pseudos
- div.querySelectorAll("*,:x");
- rbuggyQSA.push(",.*:");
- });
- }
-
- if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
- docElem.mozMatchesSelector ||
- docElem.webkitMatchesSelector ||
- docElem.oMatchesSelector ||
- docElem.msMatchesSelector) )) ) {
-
- assert(function( div ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9)
- support.disconnectedMatch = matches.call( div, "div" );
-
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( div, "[s!='']:x" );
- rbuggyMatches.push( "!=", pseudos );
- });
- }
-
- rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
- rbuggyMatches = new RegExp( rbuggyMatches.join("|") );
-
- // Element contains another
- // Purposefully does not implement inclusive descendent
- // As in, an element does not contain itself
- contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
- function( a, b ) {
- var adown = a.nodeType === 9 ? a.documentElement : a,
- bup = b && b.parentNode;
- return a === bup || !!( bup && bup.nodeType === 1 && (
- adown.contains ?
- adown.contains( bup ) :
- a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
- ));
- } :
- function( a, b ) {
- if ( b ) {
- while ( (b = b.parentNode) ) {
- if ( b === a ) {
- return true;
- }
- }
- }
- return false;
- };
-
- // Document order sorting
- sortOrder = docElem.compareDocumentPosition ?
- function( a, b ) {
- var compare;
-
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
- if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
- if ( a === doc || contains( preferredDoc, a ) ) {
- return -1;
- }
- if ( b === doc || contains( preferredDoc, b ) ) {
- return 1;
- }
- return 0;
- }
- return compare & 4 ? -1 : 1;
- }
-
- return a.compareDocumentPosition ? -1 : 1;
- } :
- function( a, b ) {
- var cur,
- i = 0,
- aup = a.parentNode,
- bup = b.parentNode,
- ap = [ a ],
- bp = [ b ];
-
- // Exit early if the nodes are identical
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
-
- // Parentless nodes are either documents or disconnected
- } else if ( !aup || !bup ) {
- return a === doc ? -1 :
- b === doc ? 1 :
- aup ? -1 :
- bup ? 1 :
- 0;
-
- // If the nodes are siblings, we can do a quick check
- } else if ( aup === bup ) {
- return siblingCheck( a, b );
- }
-
- // Otherwise we need full lists of their ancestors for comparison
- cur = a;
- while ( (cur = cur.parentNode) ) {
- ap.unshift( cur );
- }
- cur = b;
- while ( (cur = cur.parentNode) ) {
- bp.unshift( cur );
- }
-
- // Walk down the tree looking for a discrepancy
- while ( ap[i] === bp[i] ) {
- i++;
- }
-
- return i ?
- // Do a sibling check if the nodes have a common ancestor
- siblingCheck( ap[i], bp[i] ) :
-
- // Otherwise nodes in our document sort first
- ap[i] === preferredDoc ? -1 :
- bp[i] === preferredDoc ? 1 :
- 0;
- };
-
- // Always assume the presence of duplicates if sort doesn't
- // pass them to our comparison function (as in Google Chrome).
- hasDuplicate = false;
- [0, 0].sort( sortOrder );
- support.detectDuplicates = hasDuplicate;
-
- return document;
-};
-
-Sizzle.matches = function( expr, elements ) {
- return Sizzle( expr, null, null, elements );
-};
-
-Sizzle.matchesSelector = function( elem, expr ) {
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
-
- // Make sure that attribute selectors are quoted
- expr = expr.replace( rattributeQuotes, "='$1']" );
-
- // rbuggyQSA always contains :focus, so no need for an existence check
- if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
- try {
- var ret = matches.call( elem, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || support.disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9
- elem.document && elem.document.nodeType !== 11 ) {
- return ret;
- }
- } catch(e) {}
- }
-
- return Sizzle( expr, document, null, [elem] ).length > 0;
-};
-
-Sizzle.contains = function( context, elem ) {
- // Set document vars if needed
- if ( ( context.ownerDocument || context ) !== document ) {
- setDocument( context );
- }
- return contains( context, elem );
-};
-
-Sizzle.attr = function( elem, name ) {
- var val;
-
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
-
- if ( !documentIsXML ) {
- name = name.toLowerCase();
- }
- if ( (val = Expr.attrHandle[ name ]) ) {
- return val( elem );
- }
- if ( documentIsXML || support.attributes ) {
- return elem.getAttribute( name );
- }
- return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
- name :
- val && val.specified ? val.value : null;
-};
-
-Sizzle.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-// Document sorting and removing duplicates
-Sizzle.uniqueSort = function( results ) {
- var elem,
- duplicates = [],
- i = 1,
- j = 0;
-
- // Unless we *know* we can detect duplicates, assume their presence
- hasDuplicate = !support.detectDuplicates;
- results.sort( sortOrder );
-
- if ( hasDuplicate ) {
- for ( ; (elem = results[i]); i++ ) {
- if ( elem === results[ i - 1 ] ) {
- j = duplicates.push( i );
- }
- }
- while ( j-- ) {
- results.splice( duplicates[ j ], 1 );
- }
- }
-
- return results;
-};
-
-function siblingCheck( a, b ) {
- var cur = b && a,
- diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
-
- // Use IE sourceIndex if available on both nodes
- if ( diff ) {
- return diff;
- }
-
- // Check if b follows a
- if ( cur ) {
- while ( (cur = cur.nextSibling) ) {
- if ( cur === b ) {
- return -1;
- }
- }
- }
-
- return a ? 1 : -1;
-}
-
-// Returns a function to use in pseudos for input types
-function createInputPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === type;
- };
-}
-
-// Returns a function to use in pseudos for buttons
-function createButtonPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && elem.type === type;
- };
-}
-
-// Returns a function to use in pseudos for positionals
-function createPositionalPseudo( fn ) {
- return markFunction(function( argument ) {
- argument = +argument;
- return markFunction(function( seed, matches ) {
- var j,
- matchIndexes = fn( [], seed.length, argument ),
- i = matchIndexes.length;
-
- // Match elements found at the specified indexes
- while ( i-- ) {
- if ( seed[ (j = matchIndexes[i]) ] ) {
- seed[j] = !(matches[j] = seed[j]);
- }
- }
- });
- });
-}
-
-/**
- * Utility function for retrieving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-getText = Sizzle.getText = function( elem ) {
- var node,
- ret = "",
- i = 0,
- nodeType = elem.nodeType;
-
- if ( !nodeType ) {
- // If no nodeType, this is expected to be an array
- for ( ; (node = elem[i]); i++ ) {
- // Do not traverse comment nodes
- ret += getText( node );
- }
- } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
- // Use textContent for elements
- // innerText usage removed for consistency of new lines (see #11153)
- if ( typeof elem.textContent === "string" ) {
- return elem.textContent;
- } else {
- // Traverse its children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- ret += getText( elem );
- }
- }
- } else if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
- // Do not include comment or processing instruction nodes
-
- return ret;
-};
-
-Expr = Sizzle.selectors = {
-
- // Can be adjusted by the user
- cacheLength: 50,
-
- createPseudo: markFunction,
-
- match: matchExpr,
-
- find: {},
-
- relative: {
- ">": { dir: "parentNode", first: true },
- " ": { dir: "parentNode" },
- "+": { dir: "previousSibling", first: true },
- "~": { dir: "previousSibling" }
- },
-
- preFilter: {
- "ATTR": function( match ) {
- match[1] = match[1].replace( runescape, funescape );
-
- // Move the given value to match[3] whether quoted or unquoted
- match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
-
- if ( match[2] === "~=" ) {
- match[3] = " " + match[3] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- "CHILD": function( match ) {
- /* matches from matchExpr["CHILD"]
- 1 type (only|nth|...)
- 2 what (child|of-type)
- 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
- 4 xn-component of xn+y argument ([+-]?\d*n|)
- 5 sign of xn-component
- 6 x of xn-component
- 7 sign of y-component
- 8 y of y-component
- */
- match[1] = match[1].toLowerCase();
-
- if ( match[1].slice( 0, 3 ) === "nth" ) {
- // nth-* requires argument
- if ( !match[3] ) {
- Sizzle.error( match[0] );
- }
-
- // numeric x and y parameters for Expr.filter.CHILD
- // remember that false/true cast respectively to 0/1
- match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
- match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
-
- // other types prohibit arguments
- } else if ( match[3] ) {
- Sizzle.error( match[0] );
- }
-
- return match;
- },
-
- "PSEUDO": function( match ) {
- var excess,
- unquoted = !match[5] && match[2];
-
- if ( matchExpr["CHILD"].test( match[0] ) ) {
- return null;
- }
-
- // Accept quoted arguments as-is
- if ( match[4] ) {
- match[2] = match[4];
-
- // Strip excess characters from unquoted arguments
- } else if ( unquoted && rpseudo.test( unquoted ) &&
- // Get excess from tokenize (recursively)
- (excess = tokenize( unquoted, true )) &&
- // advance to the next closing parenthesis
- (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
- // excess is a negative index
- match[0] = match[0].slice( 0, excess );
- match[2] = unquoted.slice( 0, excess );
- }
-
- // Return only captures needed by the pseudo filter method (type and argument)
- return match.slice( 0, 3 );
- }
- },
-
- filter: {
-
- "TAG": function( nodeName ) {
- if ( nodeName === "*" ) {
- return function() { return true; };
- }
-
- nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
- return function( elem ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
- };
- },
-
- "CLASS": function( className ) {
- var pattern = classCache[ className + " " ];
-
- return pattern ||
- (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
- classCache( className, function( elem ) {
- return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
- });
- },
-
- "ATTR": function( name, operator, check ) {
- return function( elem ) {
- var result = Sizzle.attr( elem, name );
-
- if ( result == null ) {
- return operator === "!=";
- }
- if ( !operator ) {
- return true;
- }
-
- result += "";
-
- return operator === "=" ? result === check :
- operator === "!=" ? result !== check :
- operator === "^=" ? check && result.indexOf( check ) === 0 :
- operator === "*=" ? check && result.indexOf( check ) > -1 :
- operator === "$=" ? check && result.slice( -check.length ) === check :
- operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
- operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
- false;
- };
- },
-
- "CHILD": function( type, what, argument, first, last ) {
- var simple = type.slice( 0, 3 ) !== "nth",
- forward = type.slice( -4 ) !== "last",
- ofType = what === "of-type";
-
- return first === 1 && last === 0 ?
-
- // Shortcut for :nth-*(n)
- function( elem ) {
- return !!elem.parentNode;
- } :
-
- function( elem, context, xml ) {
- var cache, outerCache, node, diff, nodeIndex, start,
- dir = simple !== forward ? "nextSibling" : "previousSibling",
- parent = elem.parentNode,
- name = ofType && elem.nodeName.toLowerCase(),
- useCache = !xml && !ofType;
-
- if ( parent ) {
-
- // :(first|last|only)-(child|of-type)
- if ( simple ) {
- while ( dir ) {
- node = elem;
- while ( (node = node[ dir ]) ) {
- if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
- return false;
- }
- }
- // Reverse direction for :only-* (if we haven't yet done so)
- start = dir = type === "only" && !start && "nextSibling";
- }
- return true;
- }
-
- start = [ forward ? parent.firstChild : parent.lastChild ];
-
- // non-xml :nth-child(...) stores cache data on `parent`
- if ( forward && useCache ) {
- // Seek `elem` from a previously-cached index
- outerCache = parent[ expando ] || (parent[ expando ] = {});
- cache = outerCache[ type ] || [];
- nodeIndex = cache[0] === dirruns && cache[1];
- diff = cache[0] === dirruns && cache[2];
- node = nodeIndex && parent.childNodes[ nodeIndex ];
-
- while ( (node = ++nodeIndex && node && node[ dir ] ||
-
- // Fallback to seeking `elem` from the start
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- // When found, cache indexes on `parent` and break
- if ( node.nodeType === 1 && ++diff && node === elem ) {
- outerCache[ type ] = [ dirruns, nodeIndex, diff ];
- break;
- }
- }
-
- // Use previously-cached element index if available
- } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
- diff = cache[1];
-
- // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
- } else {
- // Use the same loop as above to seek `elem` from the start
- while ( (node = ++nodeIndex && node && node[ dir ] ||
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
- // Cache the index of each encountered element
- if ( useCache ) {
- (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
- }
-
- if ( node === elem ) {
- break;
- }
- }
- }
- }
-
- // Incorporate the offset, then check against cycle size
- diff -= last;
- return diff === first || ( diff % first === 0 && diff / first >= 0 );
- }
- };
- },
-
- "PSEUDO": function( pseudo, argument ) {
- // pseudo-class names are case-insensitive
- // http://www.w3.org/TR/selectors/#pseudo-classes
- // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
- // Remember that setFilters inherits from pseudos
- var args,
- fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
- Sizzle.error( "unsupported pseudo: " + pseudo );
-
- // The user may use createPseudo to indicate that
- // arguments are needed to create the filter function
- // just as Sizzle does
- if ( fn[ expando ] ) {
- return fn( argument );
- }
-
- // But maintain support for old signatures
- if ( fn.length > 1 ) {
- args = [ pseudo, pseudo, "", argument ];
- return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
- markFunction(function( seed, matches ) {
- var idx,
- matched = fn( seed, argument ),
- i = matched.length;
- while ( i-- ) {
- idx = indexOf.call( seed, matched[i] );
- seed[ idx ] = !( matches[ idx ] = matched[i] );
- }
- }) :
- function( elem ) {
- return fn( elem, 0, args );
- };
- }
-
- return fn;
- }
- },
-
- pseudos: {
- // Potentially complex pseudos
- "not": markFunction(function( selector ) {
- // Trim the selector passed to compile
- // to avoid treating leading and trailing
- // spaces as combinators
- var input = [],
- results = [],
- matcher = compile( selector.replace( rtrim, "$1" ) );
-
- return matcher[ expando ] ?
- markFunction(function( seed, matches, context, xml ) {
- var elem,
- unmatched = matcher( seed, null, xml, [] ),
- i = seed.length;
-
- // Match elements unmatched by `matcher`
- while ( i-- ) {
- if ( (elem = unmatched[i]) ) {
- seed[i] = !(matches[i] = elem);
- }
- }
- }) :
- function( elem, context, xml ) {
- input[0] = elem;
- matcher( input, null, xml, results );
- return !results.pop();
- };
- }),
-
- "has": markFunction(function( selector ) {
- return function( elem ) {
- return Sizzle( selector, elem ).length > 0;
- };
- }),
-
- "contains": markFunction(function( text ) {
- return function( elem ) {
- return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
- };
- }),
-
- // "Whether an element is represented by a :lang() selector
- // is based solely on the element's language value
- // being equal to the identifier C,
- // or beginning with the identifier C immediately followed by "-".
- // The matching of C against the element's language value is performed case-insensitively.
- // The identifier C does not have to be a valid language name."
- // http://www.w3.org/TR/selectors/#lang-pseudo
- "lang": markFunction( function( lang ) {
- // lang value must be a valid identifider
- if ( !ridentifier.test(lang || "") ) {
- Sizzle.error( "unsupported lang: " + lang );
- }
- lang = lang.replace( runescape, funescape ).toLowerCase();
- return function( elem ) {
- var elemLang;
- do {
- if ( (elemLang = documentIsXML ?
- elem.getAttribute("xml:lang") || elem.getAttribute("lang") :
- elem.lang) ) {
-
- elemLang = elemLang.toLowerCase();
- return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
- }
- } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
- return false;
- };
- }),
-
- // Miscellaneous
- "target": function( elem ) {
- var hash = window.location && window.location.hash;
- return hash && hash.slice( 1 ) === elem.id;
- },
-
- "root": function( elem ) {
- return elem === docElem;
- },
-
- "focus": function( elem ) {
- return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
- },
-
- // Boolean properties
- "enabled": function( elem ) {
- return elem.disabled === false;
- },
-
- "disabled": function( elem ) {
- return elem.disabled === true;
- },
-
- "checked": function( elem ) {
- // In CSS3, :checked should return both checked and selected elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- var nodeName = elem.nodeName.toLowerCase();
- return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
- },
-
- "selected": function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- // Contents
- "empty": function( elem ) {
- // http://www.w3.org/TR/selectors/#empty-pseudo
- // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
- // not comment, processing instructions, or others
- // Thanks to Diego Perini for the nodeName shortcut
- // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
- return false;
- }
- }
- return true;
- },
-
- "parent": function( elem ) {
- return !Expr.pseudos["empty"]( elem );
- },
-
- // Element/input types
- "header": function( elem ) {
- return rheader.test( elem.nodeName );
- },
-
- "input": function( elem ) {
- return rinputs.test( elem.nodeName );
- },
-
- "button": function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === "button" || name === "button";
- },
-
- "text": function( elem ) {
- var attr;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" &&
- elem.type === "text" &&
- ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
- },
-
- // Position-in-collection
- "first": createPositionalPseudo(function() {
- return [ 0 ];
- }),
-
- "last": createPositionalPseudo(function( matchIndexes, length ) {
- return [ length - 1 ];
- }),
-
- "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
- return [ argument < 0 ? argument + length : argument ];
- }),
-
- "even": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 0;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "odd": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 1;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; --i >= 0; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; ++i < length; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- })
- }
-};
-
-// Add button/input type pseudos
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
- Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
- Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-function tokenize( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ selector + " " ];
-
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
-
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
-
- while ( soFar ) {
-
- // Comma and first run
- if ( !matched || (match = rcomma.exec( soFar )) ) {
- if ( match ) {
- // Don't consume trailing commas as valid
- soFar = soFar.slice( match[0].length ) || soFar;
- }
- groups.push( tokens = [] );
- }
-
- matched = false;
-
- // Combinators
- if ( (match = rcombinators.exec( soFar )) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
- // Cast descendant combinators to space
- type: match[0].replace( rtrim, " " )
- } );
- soFar = soFar.slice( matched.length );
- }
-
- // Filters
- for ( type in Expr.filter ) {
- if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
- (match = preFilters[ type ]( match ))) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
- type: type,
- matches: match
- } );
- soFar = soFar.slice( matched.length );
- }
- }
-
- if ( !matched ) {
- break;
- }
- }
-
- // Return the length of the invalid excess
- // if we're just parsing
- // Otherwise, throw an error or return tokens
- return parseOnly ?
- soFar.length :
- soFar ?
- Sizzle.error( selector ) :
- // Cache the tokens
- tokenCache( selector, groups ).slice( 0 );
-}
-
-function toSelector( tokens ) {
- var i = 0,
- len = tokens.length,
- selector = "";
- for ( ; i < len; i++ ) {
- selector += tokens[i].value;
- }
- return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
- var dir = combinator.dir,
- checkNonElements = base && dir === "parentNode",
- doneName = done++;
-
- return combinator.first ?
- // Check against closest ancestor/preceding element
- function( elem, context, xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- return matcher( elem, context, xml );
- }
- }
- } :
-
- // Check against all ancestor/preceding elements
- function( elem, context, xml ) {
- var data, cache, outerCache,
- dirkey = dirruns + " " + doneName;
-
- // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
- if ( xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- if ( matcher( elem, context, xml ) ) {
- return true;
- }
- }
- }
- } else {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- outerCache = elem[ expando ] || (elem[ expando ] = {});
- if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
- if ( (data = cache[1]) === true || data === cachedruns ) {
- return data === true;
- }
- } else {
- cache = outerCache[ dir ] = [ dirkey ];
- cache[1] = matcher( elem, context, xml ) || cachedruns;
- if ( cache[1] === true ) {
- return true;
- }
- }
- }
- }
- }
- };
-}
-
-function elementMatcher( matchers ) {
- return matchers.length > 1 ?
- function( elem, context, xml ) {
- var i = matchers.length;
- while ( i-- ) {
- if ( !matchers[i]( elem, context, xml ) ) {
- return false;
- }
- }
- return true;
- } :
- matchers[0];
-}
-
-function condense( unmatched, map, filter, context, xml ) {
- var elem,
- newUnmatched = [],
- i = 0,
- len = unmatched.length,
- mapped = map != null;
-
- for ( ; i < len; i++ ) {
- if ( (elem = unmatched[i]) ) {
- if ( !filter || filter( elem, context, xml ) ) {
- newUnmatched.push( elem );
- if ( mapped ) {
- map.push( i );
- }
- }
- }
- }
-
- return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
- postFilter = setMatcher( postFilter );
- }
- if ( postFinder && !postFinder[ expando ] ) {
- postFinder = setMatcher( postFinder, postSelector );
- }
- return markFunction(function( seed, results, context, xml ) {
- var temp, i, elem,
- preMap = [],
- postMap = [],
- preexisting = results.length,
-
- // Get initial elements from seed or context
- elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
-
- // Prefilter to get matcher input, preserving a map for seed-results synchronization
- matcherIn = preFilter && ( seed || !selector ) ?
- condense( elems, preMap, preFilter, context, xml ) :
- elems,
-
- matcherOut = matcher ?
- // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
- postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
- // ...intermediate processing is necessary
- [] :
-
- // ...otherwise use results directly
- results :
- matcherIn;
-
- // Find primary matches
- if ( matcher ) {
- matcher( matcherIn, matcherOut, context, xml );
- }
-
- // Apply postFilter
- if ( postFilter ) {
- temp = condense( matcherOut, postMap );
- postFilter( temp, [], context, xml );
-
- // Un-match failing elements by moving them back to matcherIn
- i = temp.length;
- while ( i-- ) {
- if ( (elem = temp[i]) ) {
- matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
- }
- }
- }
-
- if ( seed ) {
- if ( postFinder || preFilter ) {
- if ( postFinder ) {
- // Get the final matcherOut by condensing this intermediate into postFinder contexts
- temp = [];
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) ) {
- // Restore matcherIn since elem is not yet a final match
- temp.push( (matcherIn[i] = elem) );
- }
- }
- postFinder( null, (matcherOut = []), temp, xml );
- }
-
- // Move matched elements from seed to results to keep them synchronized
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) &&
- (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
-
- seed[temp] = !(results[temp] = elem);
- }
- }
- }
-
- // Add elements to results, through postFinder if defined
- } else {
- matcherOut = condense(
- matcherOut === results ?
- matcherOut.splice( preexisting, matcherOut.length ) :
- matcherOut
- );
- if ( postFinder ) {
- postFinder( null, results, matcherOut, xml );
- } else {
- push.apply( results, matcherOut );
- }
- }
- });
-}
-
-function matcherFromTokens( tokens ) {
- var checkContext, matcher, j,
- len = tokens.length,
- leadingRelative = Expr.relative[ tokens[0].type ],
- implicitRelative = leadingRelative || Expr.relative[" "],
- i = leadingRelative ? 1 : 0,
-
- // The foundational matcher ensures that elements are reachable from top-level context(s)
- matchContext = addCombinator( function( elem ) {
- return elem === checkContext;
- }, implicitRelative, true ),
- matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
- }, implicitRelative, true ),
- matchers = [ function( elem, context, xml ) {
- return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
- (checkContext = context).nodeType ?
- matchContext( elem, context, xml ) :
- matchAnyContext( elem, context, xml ) );
- } ];
-
- for ( ; i < len; i++ ) {
- if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
- matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
- } else {
- matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
-
- // Return special upon seeing a positional matcher
- if ( matcher[ expando ] ) {
- // Find the next relative operator (if any) for proper handling
- j = ++i;
- for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[j].type ] ) {
- break;
- }
- }
- return setMatcher(
- i > 1 && elementMatcher( matchers ),
- i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
- matcher,
- i < j && matcherFromTokens( tokens.slice( i, j ) ),
- j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
- j < len && toSelector( tokens )
- );
- }
- matchers.push( matcher );
- }
- }
-
- return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
- // A counter to specify which element is currently being matched
- var matcherCachedRuns = 0,
- bySet = setMatchers.length > 0,
- byElement = elementMatchers.length > 0,
- superMatcher = function( seed, context, xml, results, expandContext ) {
- var elem, j, matcher,
- setMatched = [],
- matchedCount = 0,
- i = "0",
- unmatched = seed && [],
- outermost = expandContext != null,
- contextBackup = outermostContext,
- // We must always have either seed elements or context
- elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
- // Use integer dirruns iff this is the outermost matcher
- dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
-
- if ( outermost ) {
- outermostContext = context !== document && context;
- cachedruns = matcherCachedRuns;
- }
-
- // Add elements passing elementMatchers directly to results
- // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
- for ( ; (elem = elems[i]) != null; i++ ) {
- if ( byElement && elem ) {
- j = 0;
- while ( (matcher = elementMatchers[j++]) ) {
- if ( matcher( elem, context, xml ) ) {
- results.push( elem );
- break;
- }
- }
- if ( outermost ) {
- dirruns = dirrunsUnique;
- cachedruns = ++matcherCachedRuns;
- }
- }
-
- // Track unmatched elements for set filters
- if ( bySet ) {
- // They will have gone through all possible matchers
- if ( (elem = !matcher && elem) ) {
- matchedCount--;
- }
-
- // Lengthen the array for every element, matched or not
- if ( seed ) {
- unmatched.push( elem );
- }
- }
- }
-
- // Apply set filters to unmatched elements
- matchedCount += i;
- if ( bySet && i !== matchedCount ) {
- j = 0;
- while ( (matcher = setMatchers[j++]) ) {
- matcher( unmatched, setMatched, context, xml );
- }
-
- if ( seed ) {
- // Reintegrate element matches to eliminate the need for sorting
- if ( matchedCount > 0 ) {
- while ( i-- ) {
- if ( !(unmatched[i] || setMatched[i]) ) {
- setMatched[i] = pop.call( results );
- }
- }
- }
-
- // Discard index placeholder values to get only actual matches
- setMatched = condense( setMatched );
- }
-
- // Add matches to results
- push.apply( results, setMatched );
-
- // Seedless set matches succeeding multiple successful matchers stipulate sorting
- if ( outermost && !seed && setMatched.length > 0 &&
- ( matchedCount + setMatchers.length ) > 1 ) {
-
- Sizzle.uniqueSort( results );
- }
- }
-
- // Override manipulation of globals by nested matchers
- if ( outermost ) {
- dirruns = dirrunsUnique;
- outermostContext = contextBackup;
- }
-
- return unmatched;
- };
-
- return bySet ?
- markFunction( superMatcher ) :
- superMatcher;
-}
-
-compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
- var i,
- setMatchers = [],
- elementMatchers = [],
- cached = compilerCache[ selector + " " ];
-
- if ( !cached ) {
- // Generate a function of recursive functions that can be used to check each element
- if ( !group ) {
- group = tokenize( selector );
- }
- i = group.length;
- while ( i-- ) {
- cached = matcherFromTokens( group[i] );
- if ( cached[ expando ] ) {
- setMatchers.push( cached );
- } else {
- elementMatchers.push( cached );
- }
- }
-
- // Cache the compiled function
- cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
- }
- return cached;
-};
-
-function multipleContexts( selector, contexts, results ) {
- var i = 0,
- len = contexts.length;
- for ( ; i < len; i++ ) {
- Sizzle( selector, contexts[i], results );
- }
- return results;
-}
-
-function select( selector, context, results, seed ) {
- var i, tokens, token, type, find,
- match = tokenize( selector );
-
- if ( !seed ) {
- // Try to minimize operations if there is only one group
- if ( match.length === 1 ) {
-
- // Take a shortcut and set the context if the root selector is an ID
- tokens = match[0] = match[0].slice( 0 );
- if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
- context.nodeType === 9 && !documentIsXML &&
- Expr.relative[ tokens[1].type ] ) {
-
- context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0];
- if ( !context ) {
- return results;
- }
-
- selector = selector.slice( tokens.shift().value.length );
- }
-
- // Fetch a seed set for right-to-left matching
- i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
- while ( i-- ) {
- token = tokens[i];
-
- // Abort if we hit a combinator
- if ( Expr.relative[ (type = token.type) ] ) {
- break;
- }
- if ( (find = Expr.find[ type ]) ) {
- // Search, expanding context for leading sibling combinators
- if ( (seed = find(
- token.matches[0].replace( runescape, funescape ),
- rsibling.test( tokens[0].type ) && context.parentNode || context
- )) ) {
-
- // If seed is empty or no tokens remain, we can return early
- tokens.splice( i, 1 );
- selector = seed.length && toSelector( tokens );
- if ( !selector ) {
- push.apply( results, slice.call( seed, 0 ) );
- return results;
- }
-
- break;
- }
- }
- }
- }
- }
-
- // Compile and execute a filtering function
- // Provide `match` to avoid retokenization if we modified the selector above
- compile( selector, match )(
- seed,
- context,
- documentIsXML,
- results,
- rsibling.test( selector )
- );
- return results;
-}
-
-// Deprecated
-Expr.pseudos["nth"] = Expr.pseudos["eq"];
-
-// Easy API for creating new setFilters
-function setFilters() {}
-Expr.filters = setFilters.prototype = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-// Initialize with the default document
-setDocument();
-
-// Override sizzle attribute retrieval
-Sizzle.attr = jQuery.attr;
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.pseudos;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-})( window );
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prev(?:Until|All))/,
- isSimple = /^.[^:#\[\.,]*$/,
- rneedsContext = jQuery.expr.match.needsContext,
- // methods guaranteed to produce a unique set when starting from a unique set
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.fn.extend({
- find: function( selector ) {
- var i, ret, self,
- len = this.length;
-
- if ( typeof selector !== "string" ) {
- self = this;
- return this.pushStack( jQuery( selector ).filter(function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- }) );
- }
-
- ret = [];
- for ( i = 0; i < len; i++ ) {
- jQuery.find( selector, this[ i ], ret );
- }
-
- // Needed because $( selector, context ) becomes $( context ).find( selector )
- ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
- ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
- return ret;
- },
-
- has: function( target ) {
- var i,
- targets = jQuery( target, this ),
- len = targets.length;
-
- return this.filter(function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( this, targets[i] ) ) {
- return true;
- }
- }
- });
- },
-
- not: function( selector ) {
- return this.pushStack( winnow(this, selector, false) );
- },
-
- filter: function( selector ) {
- return this.pushStack( winnow(this, selector, true) );
- },
-
- is: function( selector ) {
- return !!selector && (
- typeof selector === "string" ?
- // If this is a positional/relative selector, check membership in the returned set
- // so $("p:first").is("p:last") won't return true for a doc with two "p".
- rneedsContext.test( selector ) ?
- jQuery( selector, this.context ).index( this[0] ) >= 0 :
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
- },
-
- closest: function( selectors, context ) {
- var cur,
- i = 0,
- l = this.length,
- ret = [],
- pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
-
- for ( ; i < l; i++ ) {
- cur = this[i];
-
- while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- ret.push( cur );
- break;
- }
- cur = cur.parentNode;
- }
- }
-
- return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
-
- // No argument, return index in parent
- if ( !elem ) {
- return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
- }
-
- // index in selector
- if ( typeof elem === "string" ) {
- return jQuery.inArray( this[0], jQuery( elem ) );
- }
-
- // Locate the position of the desired element
- return jQuery.inArray(
- // If it receives a jQuery object, the first element is used
- elem.jquery ? elem[0] : elem, this );
- },
-
- add: function( selector, context ) {
- var set = typeof selector === "string" ?
- jQuery( selector, context ) :
- jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
- all = jQuery.merge( this.get(), set );
-
- return this.pushStack( jQuery.unique(all) );
- },
-
- addBack: function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter(selector)
- );
- }
-});
-
-jQuery.fn.andSelf = jQuery.fn.addBack;
-
-function sibling( cur, dir ) {
- do {
- cur = cur[ dir ];
- } while ( cur && cur.nodeType !== 1 );
-
- return cur;
-}
-
-jQuery.each({
- parent: function( elem ) {
- var parent = elem.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
- },
- parents: function( elem ) {
- return jQuery.dir( elem, "parentNode" );
- },
- parentsUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "parentNode", until );
- },
- next: function( elem ) {
- return sibling( elem, "nextSibling" );
- },
- prev: function( elem ) {
- return sibling( elem, "previousSibling" );
- },
- nextAll: function( elem ) {
- return jQuery.dir( elem, "nextSibling" );
- },
- prevAll: function( elem ) {
- return jQuery.dir( elem, "previousSibling" );
- },
- nextUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "nextSibling", until );
- },
- prevUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "previousSibling", until );
- },
- siblings: function( elem ) {
- return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
- },
- children: function( elem ) {
- return jQuery.sibling( elem.firstChild );
- },
- contents: function( elem ) {
- return jQuery.nodeName( elem, "iframe" ) ?
- elem.contentDocument || elem.contentWindow.document :
- jQuery.merge( [], elem.childNodes );
- }
-}, function( name, fn ) {
- jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until );
-
- if ( !runtil.test( name ) ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- ret = jQuery.filter( selector, ret );
- }
-
- ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
-
- if ( this.length > 1 && rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
-
- return this.pushStack( ret );
- };
-});
-
-jQuery.extend({
- filter: function( expr, elems, not ) {
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- return elems.length === 1 ?
- jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
- jQuery.find.matches(expr, elems);
- },
-
- dir: function( elem, dir, until ) {
- var matched = [],
- cur = elem[ dir ];
-
- while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
- if ( cur.nodeType === 1 ) {
- matched.push( cur );
- }
- cur = cur[dir];
- }
- return matched;
- },
-
- sibling: function( n, elem ) {
- var r = [];
-
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType === 1 && n !== elem ) {
- r.push( n );
- }
- }
-
- return r;
- }
-});
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, keep ) {
-
- // Can't pass null or undefined to indexOf in Firefox 4
- // Set to 0 to skip string check
- qualifier = qualifier || 0;
-
- if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep(elements, function( elem, i ) {
- var retVal = !!qualifier.call( elem, i, elem );
- return retVal === keep;
- });
-
- } else if ( qualifier.nodeType ) {
- return jQuery.grep(elements, function( elem ) {
- return ( elem === qualifier ) === keep;
- });
-
- } else if ( typeof qualifier === "string" ) {
- var filtered = jQuery.grep(elements, function( elem ) {
- return elem.nodeType === 1;
- });
-
- if ( isSimple.test( qualifier ) ) {
- return jQuery.filter(qualifier, filtered, !keep);
- } else {
- qualifier = jQuery.filter( qualifier, filtered );
- }
- }
-
- return jQuery.grep(elements, function( elem ) {
- return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
- });
-}
-function createSafeFragment( document ) {
- var list = nodeNames.split( "|" ),
- safeFrag = document.createDocumentFragment();
-
- if ( safeFrag.createElement ) {
- while ( list.length ) {
- safeFrag.createElement(
- list.pop()
- );
- }
- }
- return safeFrag;
-}
-
-var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
- "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
- rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
- rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
- rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
- rtagName = /<([\w:]+)/,
- rtbody = /<tbody/i,
- rhtml = /<|&#?\w+;/,
- rnoInnerhtml = /<(?:script|style|link)/i,
- manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
- // checked="checked" or checked
- rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
- rscriptType = /^$|\/(?:java|ecma)script/i,
- rscriptTypeMasked = /^true\/(.*)/,
- rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
-
- // We have to close these tags to support XHTML (#13200)
- wrapMap = {
- option: [ 1, "<select multiple='multiple'>", "</select>" ],
- legend: [ 1, "<fieldset>", "</fieldset>" ],
- area: [ 1, "<map>", "</map>" ],
- param: [ 1, "<object>", "</object>" ],
- thead: [ 1, "<table>", "</table>" ],
- tr: [ 2, "<table><tbody>", "</tbody></table>" ],
- col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
- td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
-
- // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
- // unless wrapped in a div with non-breaking characters in front of it.
- _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
- },
- safeFragment = createSafeFragment( document ),
- fragmentDiv = safeFragment.appendChild( document.createElement("div") );
-
-wrapMap.optgroup = wrapMap.option;
-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
-wrapMap.th = wrapMap.td;
-
-jQuery.fn.extend({
- text: function( value ) {
- return jQuery.access( this, function( value ) {
- return value === undefined ?
- jQuery.text( this ) :
- this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
- }, null, value, arguments.length );
- },
-
- wrapAll: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapAll( html.call(this, i) );
- });
- }
-
- if ( this[0] ) {
- // The elements to wrap the target around
- var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
-
- if ( this[0].parentNode ) {
- wrap.insertBefore( this[0] );
- }
-
- wrap.map(function() {
- var elem = this;
-
- while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
- elem = elem.firstChild;
- }
-
- return elem;
- }).append( this );
- }
-
- return this;
- },
-
- wrapInner: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapInner( html.call(this, i) );
- });
- }
-
- return this.each(function() {
- var self = jQuery( this ),
- contents = self.contents();
-
- if ( contents.length ) {
- contents.wrapAll( html );
-
- } else {
- self.append( html );
- }
- });
- },
-
- wrap: function( html ) {
- var isFunction = jQuery.isFunction( html );
-
- return this.each(function(i) {
- jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
- });
- },
-
- unwrap: function() {
- return this.parent().each(function() {
- if ( !jQuery.nodeName( this, "body" ) ) {
- jQuery( this ).replaceWith( this.childNodes );
- }
- }).end();
- },
-
- append: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
- this.appendChild( elem );
- }
- });
- },
-
- prepend: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
- this.insertBefore( elem, this.firstChild );
- }
- });
- },
-
- before: function() {
- return this.domManip( arguments, false, function( elem ) {
- if ( this.parentNode ) {
- this.parentNode.insertBefore( elem, this );
- }
- });
- },
-
- after: function() {
- return this.domManip( arguments, false, function( elem ) {
- if ( this.parentNode ) {
- this.parentNode.insertBefore( elem, this.nextSibling );
- }
- });
- },
-
- // keepData is for internal use only--do not document
- remove: function( selector, keepData ) {
- var elem,
- i = 0;
-
- for ( ; (elem = this[i]) != null; i++ ) {
- if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {
- if ( !keepData && elem.nodeType === 1 ) {
- jQuery.cleanData( getAll( elem ) );
- }
-
- if ( elem.parentNode ) {
- if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
- setGlobalEval( getAll( elem, "script" ) );
- }
- elem.parentNode.removeChild( elem );
- }
- }
- }
-
- return this;
- },
-
- empty: function() {
- var elem,
- i = 0;
-
- for ( ; (elem = this[i]) != null; i++ ) {
- // Remove element nodes and prevent memory leaks
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( getAll( elem, false ) );
- }
-
- // Remove any remaining nodes
- while ( elem.firstChild ) {
- elem.removeChild( elem.firstChild );
- }
-
- // If this is a select, ensure that it displays empty (#12336)
- // Support: IE<9
- if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
- elem.options.length = 0;
- }
- }
-
- return this;
- },
-
- clone: function( dataAndEvents, deepDataAndEvents ) {
- dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
- deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
-
- return this.map( function () {
- return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
- });
- },
-
- html: function( value ) {
- return jQuery.access( this, function( value ) {
- var elem = this[0] || {},
- i = 0,
- l = this.length;
-
- if ( value === undefined ) {
- return elem.nodeType === 1 ?
- elem.innerHTML.replace( rinlinejQuery, "" ) :
- undefined;
- }
-
- // See if we can take a shortcut and just use innerHTML
- if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
- ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
- ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
- !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
-
- value = value.replace( rxhtmlTag, "<$1></$2>" );
-
- try {
- for (; i < l; i++ ) {
- // Remove element nodes and prevent memory leaks
- elem = this[i] || {};
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( getAll( elem, false ) );
- elem.innerHTML = value;
- }
- }
-
- elem = 0;
-
- // If using innerHTML throws an exception, use the fallback method
- } catch(e) {}
- }
-
- if ( elem ) {
- this.empty().append( value );
- }
- }, null, value, arguments.length );
- },
-
- replaceWith: function( value ) {
- var isFunc = jQuery.isFunction( value );
-
- // Make sure that the elements are removed from the DOM before they are inserted
- // this can help fix replacing a parent with child elements
- if ( !isFunc && typeof value !== "string" ) {
- value = jQuery( value ).not( this ).detach();
- }
-
- return this.domManip( [ value ], true, function( elem ) {
- var next = this.nextSibling,
- parent = this.parentNode;
-
- if ( parent ) {
- jQuery( this ).remove();
- parent.insertBefore( elem, next );
- }
- });
- },
-
- detach: function( selector ) {
- return this.remove( selector, true );
- },
-
- domManip: function( args, table, callback ) {
-
- // Flatten any nested arrays
- args = core_concat.apply( [], args );
-
- var first, node, hasScripts,
- scripts, doc, fragment,
- i = 0,
- l = this.length,
- set = this,
- iNoClone = l - 1,
- value = args[0],
- isFunction = jQuery.isFunction( value );
-
- // We can't cloneNode fragments that contain checked, in WebKit
- if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
- return this.each(function( index ) {
- var self = set.eq( index );
- if ( isFunction ) {
- args[0] = value.call( this, index, table ? self.html() : undefined );
- }
- self.domManip( args, table, callback );
- });
- }
-
- if ( l ) {
- fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
- first = fragment.firstChild;
-
- if ( fragment.childNodes.length === 1 ) {
- fragment = first;
- }
-
- if ( first ) {
- table = table && jQuery.nodeName( first, "tr" );
- scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
- hasScripts = scripts.length;
-
- // Use the original fragment for the last item instead of the first because it can end up
- // being emptied incorrectly in certain situations (#8070).
- for ( ; i < l; i++ ) {
- node = fragment;
-
- if ( i !== iNoClone ) {
- node = jQuery.clone( node, true, true );
-
- // Keep references to cloned scripts for later restoration
- if ( hasScripts ) {
- jQuery.merge( scripts, getAll( node, "script" ) );
- }
- }
-
- callback.call(
- table && jQuery.nodeName( this[i], "table" ) ?
- findOrAppend( this[i], "tbody" ) :
- this[i],
- node,
- i
- );
- }
-
- if ( hasScripts ) {
- doc = scripts[ scripts.length - 1 ].ownerDocument;
-
- // Reenable scripts
- jQuery.map( scripts, restoreScript );
-
- // Evaluate executable scripts on first document insertion
- for ( i = 0; i < hasScripts; i++ ) {
- node = scripts[ i ];
- if ( rscriptType.test( node.type || "" ) &&
- !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
-
- if ( node.src ) {
- // Hope ajax is available...
- jQuery.ajax({
- url: node.src,
- type: "GET",
- dataType: "script",
- async: false,
- global: false,
- "throws": true
- });
- } else {
- jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
- }
- }
- }
- }
-
- // Fix #11809: Avoid leaking memory
- fragment = first = null;
- }
- }
-
- return this;
- }
-});
-
-function findOrAppend( elem, tag ) {
- return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
-}
-
-// Replace/restore the type attribute of script elements for safe DOM manipulation
-function disableScript( elem ) {
- var attr = elem.getAttributeNode("type");
- elem.type = ( attr && attr.specified ) + "/" + elem.type;
- return elem;
-}
-function restoreScript( elem ) {
- var match = rscriptTypeMasked.exec( elem.type );
- if ( match ) {
- elem.type = match[1];
- } else {
- elem.removeAttribute("type");
- }
- return elem;
-}
-
-// Mark scripts as having already been evaluated
-function setGlobalEval( elems, refElements ) {
- var elem,
- i = 0;
- for ( ; (elem = elems[i]) != null; i++ ) {
- jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
- }
-}
-
-function cloneCopyEvent( src, dest ) {
-
- if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
- return;
- }
-
- var type, i, l,
- oldData = jQuery._data( src ),
- curData = jQuery._data( dest, oldData ),
- events = oldData.events;
-
- if ( events ) {
- delete curData.handle;
- curData.events = {};
-
- for ( type in events ) {
- for ( i = 0, l = events[ type ].length; i < l; i++ ) {
- jQuery.event.add( dest, type, events[ type ][ i ] );
- }
- }
- }
-
- // make the cloned public data object a copy from the original
- if ( curData.data ) {
- curData.data = jQuery.extend( {}, curData.data );
- }
-}
-
-function fixCloneNodeIssues( src, dest ) {
- var nodeName, e, data;
-
- // We do not need to do anything for non-Elements
- if ( dest.nodeType !== 1 ) {
- return;
- }
-
- nodeName = dest.nodeName.toLowerCase();
-
- // IE6-8 copies events bound via attachEvent when using cloneNode.
- if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
- data = jQuery._data( dest );
-
- for ( e in data.events ) {
- jQuery.removeEvent( dest, e, data.handle );
- }
-
- // Event data gets referenced instead of copied if the expando gets copied too
- dest.removeAttribute( jQuery.expando );
- }
-
- // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
- if ( nodeName === "script" && dest.text !== src.text ) {
- disableScript( dest ).text = src.text;
- restoreScript( dest );
-
- // IE6-10 improperly clones children of object elements using classid.
- // IE10 throws NoModificationAllowedError if parent is null, #12132.
- } else if ( nodeName === "object" ) {
- if ( dest.parentNode ) {
- dest.outerHTML = src.outerHTML;
- }
-
- // This path appears unavoidable for IE9. When cloning an object
- // element in IE9, the outerHTML strategy above is not sufficient.
- // If the src has innerHTML and the destination does not,
- // copy the src.innerHTML into the dest.innerHTML. #10324
- if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
- dest.innerHTML = src.innerHTML;
- }
-
- } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
- // IE6-8 fails to persist the checked state of a cloned checkbox
- // or radio button. Worse, IE6-7 fail to give the cloned element
- // a checked appearance if the defaultChecked value isn't also set
-
- dest.defaultChecked = dest.checked = src.checked;
-
- // IE6-7 get confused and end up setting the value of a cloned
- // checkbox/radio button to an empty string instead of "on"
- if ( dest.value !== src.value ) {
- dest.value = src.value;
- }
-
- // IE6-8 fails to return the selected option to the default selected
- // state when cloning options
- } else if ( nodeName === "option" ) {
- dest.defaultSelected = dest.selected = src.defaultSelected;
-
- // IE6-8 fails to set the defaultValue to the correct value when
- // cloning other types of input fields
- } else if ( nodeName === "input" || nodeName === "textarea" ) {
- dest.defaultValue = src.defaultValue;
- }
-}
-
-jQuery.each({
- appendTo: "append",
- prependTo: "prepend",
- insertBefore: "before",
- insertAfter: "after",
- replaceAll: "replaceWith"
-}, function( name, original ) {
- jQuery.fn[ name ] = function( selector ) {
- var elems,
- i = 0,
- ret = [],
- insert = jQuery( selector ),
- last = insert.length - 1;
-
- for ( ; i <= last; i++ ) {
- elems = i === last ? this : this.clone(true);
- jQuery( insert[i] )[ original ]( elems );
-
- // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
- core_push.apply( ret, elems.get() );
- }
-
- return this.pushStack( ret );
- };
-});
-
-function getAll( context, tag ) {
- var elems, elem,
- i = 0,
- found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
- typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
- undefined;
-
- if ( !found ) {
- for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
- if ( !tag || jQuery.nodeName( elem, tag ) ) {
- found.push( elem );
- } else {
- jQuery.merge( found, getAll( elem, tag ) );
- }
- }
- }
-
- return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
- jQuery.merge( [ context ], found ) :
- found;
-}
-
-// Used in buildFragment, fixes the defaultChecked property
-function fixDefaultChecked( elem ) {
- if ( manipulation_rcheckableType.test( elem.type ) ) {
- elem.defaultChecked = elem.checked;
- }
-}
-
-jQuery.extend({
- clone: function( elem, dataAndEvents, deepDataAndEvents ) {
- var destElements, node, clone, i, srcElements,
- inPage = jQuery.contains( elem.ownerDocument, elem );
-
- if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
- clone = elem.cloneNode( true );
-
- // IE<=8 does not properly clone detached, unknown element nodes
- } else {
- fragmentDiv.innerHTML = elem.outerHTML;
- fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
- }
-
- if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
- (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
-
- // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
- destElements = getAll( clone );
- srcElements = getAll( elem );
-
- // Fix all IE cloning issues
- for ( i = 0; (node = srcElements[i]) != null; ++i ) {
- // Ensure that the destination node is not null; Fixes #9587
- if ( destElements[i] ) {
- fixCloneNodeIssues( node, destElements[i] );
- }
- }
- }
-
- // Copy the events from the original to the clone
- if ( dataAndEvents ) {
- if ( deepDataAndEvents ) {
- srcElements = srcElements || getAll( elem );
- destElements = destElements || getAll( clone );
-
- for ( i = 0; (node = srcElements[i]) != null; i++ ) {
- cloneCopyEvent( node, destElements[i] );
- }
- } else {
- cloneCopyEvent( elem, clone );
- }
- }
-
- // Preserve script evaluation history
- destElements = getAll( clone, "script" );
- if ( destElements.length > 0 ) {
- setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
- }
-
- destElements = srcElements = node = null;
-
- // Return the cloned set
- return clone;
- },
-
- buildFragment: function( elems, context, scripts, selection ) {
- var j, elem, contains,
- tmp, tag, tbody, wrap,
- l = elems.length,
-
- // Ensure a safe fragment
- safe = createSafeFragment( context ),
-
- nodes = [],
- i = 0;
-
- for ( ; i < l; i++ ) {
- elem = elems[ i ];
-
- if ( elem || elem === 0 ) {
-
- // Add nodes directly
- if ( jQuery.type( elem ) === "object" ) {
- jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
-
- // Convert non-html into a text node
- } else if ( !rhtml.test( elem ) ) {
- nodes.push( context.createTextNode( elem ) );
-
- // Convert html into DOM nodes
- } else {
- tmp = tmp || safe.appendChild( context.createElement("div") );
-
- // Deserialize a standard representation
- tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
- wrap = wrapMap[ tag ] || wrapMap._default;
-
- tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
-
- // Descend through wrappers to the right content
- j = wrap[0];
- while ( j-- ) {
- tmp = tmp.lastChild;
- }
-
- // Manually add leading whitespace removed by IE
- if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
- nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
- }
-
- // Remove IE's autoinserted <tbody> from table fragments
- if ( !jQuery.support.tbody ) {
-
- // String was a <table>, *may* have spurious <tbody>
- elem = tag === "table" && !rtbody.test( elem ) ?
- tmp.firstChild :
-
- // String was a bare <thead> or <tfoot>
- wrap[1] === "<table>" && !rtbody.test( elem ) ?
- tmp :
- 0;
-
- j = elem && elem.childNodes.length;
- while ( j-- ) {
- if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
- elem.removeChild( tbody );
- }
- }
- }
-
- jQuery.merge( nodes, tmp.childNodes );
-
- // Fix #12392 for WebKit and IE > 9
- tmp.textContent = "";
-
- // Fix #12392 for oldIE
- while ( tmp.firstChild ) {
- tmp.removeChild( tmp.firstChild );
- }
-
- // Remember the top-level container for proper cleanup
- tmp = safe.lastChild;
- }
- }
- }
-
- // Fix #11356: Clear elements from fragment
- if ( tmp ) {
- safe.removeChild( tmp );
- }
-
- // Reset defaultChecked for any radios and checkboxes
- // about to be appended to the DOM in IE 6/7 (#8060)
- if ( !jQuery.support.appendChecked ) {
- jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
- }
-
- i = 0;
- while ( (elem = nodes[ i++ ]) ) {
-
- // #4087 - If origin and destination elements are the same, and this is
- // that element, do not do anything
- if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
- continue;
- }
-
- contains = jQuery.contains( elem.ownerDocument, elem );
-
- // Append to fragment
- tmp = getAll( safe.appendChild( elem ), "script" );
-
- // Preserve script evaluation history
- if ( contains ) {
- setGlobalEval( tmp );
- }
-
- // Capture executables
- if ( scripts ) {
- j = 0;
- while ( (elem = tmp[ j++ ]) ) {
- if ( rscriptType.test( elem.type || "" ) ) {
- scripts.push( elem );
- }
- }
- }
- }
-
- tmp = null;
-
- return safe;
- },
-
- cleanData: function( elems, /* internal */ acceptData ) {
- var elem, type, id, data,
- i = 0,
- internalKey = jQuery.expando,
- cache = jQuery.cache,
- deleteExpando = jQuery.support.deleteExpando,
- special = jQuery.event.special;
-
- for ( ; (elem = elems[i]) != null; i++ ) {
-
- if ( acceptData || jQuery.acceptData( elem ) ) {
-
- id = elem[ internalKey ];
- data = id && cache[ id ];
-
- if ( data ) {
- if ( data.events ) {
- for ( type in data.events ) {
- if ( special[ type ] ) {
- jQuery.event.remove( elem, type );
-
- // This is a shortcut to avoid jQuery.event.remove's overhead
- } else {
- jQuery.removeEvent( elem, type, data.handle );
- }
- }
- }
-
- // Remove cache only if it was not already removed by jQuery.event.remove
- if ( cache[ id ] ) {
-
- delete cache[ id ];
-
- // IE does not allow us to delete expando properties from nodes,
- // nor does it have a removeAttribute function on Document nodes;
- // we must handle all of these cases
- if ( deleteExpando ) {
- delete elem[ internalKey ];
-
- } else if ( typeof elem.removeAttribute !== core_strundefined ) {
- elem.removeAttribute( internalKey );
-
- } else {
- elem[ internalKey ] = null;
- }
-
- core_deletedIds.push( id );
- }
- }
- }
- }
- }
-});
-var iframe, getStyles, curCSS,
- ralpha = /alpha\([^)]*\)/i,
- ropacity = /opacity\s*=\s*([^)]*)/,
- rposition = /^(top|right|bottom|left)$/,
- // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
- // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
- rdisplayswap = /^(none|table(?!-c[ea]).+)/,
- rmargin = /^margin/,
- rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
- rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
- rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
- elemdisplay = { BODY: "block" },
-
- cssShow = { position: "absolute", visibility: "hidden", display: "block" },
- cssNormalTransform = {
- letterSpacing: 0,
- fontWeight: 400
- },
-
- cssExpand = [ "Top", "Right", "Bottom", "Left" ],
- cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
-
-// return a css property mapped to a potentially vendor prefixed property
-function vendorPropName( style, name ) {
-
- // shortcut for names that are not vendor prefixed
- if ( name in style ) {
- return name;
- }
-
- // check for vendor prefixed names
- var capName = name.charAt(0).toUpperCase() + name.slice(1),
- origName = name,
- i = cssPrefixes.length;
-
- while ( i-- ) {
- name = cssPrefixes[ i ] + capName;
- if ( name in style ) {
- return name;
- }
- }
-
- return origName;
-}
-
-function isHidden( elem, el ) {
- // isHidden might be called from jQuery#filter function;
- // in that case, element will be second argument
- elem = el || elem;
- return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
-}
-
-function showHide( elements, show ) {
- var display, elem, hidden,
- values = [],
- index = 0,
- length = elements.length;
-
- for ( ; index < length; index++ ) {
- elem = elements[ index ];
- if ( !elem.style ) {
- continue;
- }
-
- values[ index ] = jQuery._data( elem, "olddisplay" );
- display = elem.style.display;
- if ( show ) {
- // Reset the inline display of this element to learn if it is
- // being hidden by cascaded rules or not
- if ( !values[ index ] && display === "none" ) {
- elem.style.display = "";
- }
-
- // Set elements which have been overridden with display: none
- // in a stylesheet to whatever the default browser style is
- // for such an element
- if ( elem.style.display === "" && isHidden( elem ) ) {
- values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
- }
- } else {
-
- if ( !values[ index ] ) {
- hidden = isHidden( elem );
-
- if ( display && display !== "none" || !hidden ) {
- jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
- }
- }
- }
- }
-
- // Set the display of most of the elements in a second loop
- // to avoid the constant reflow
- for ( index = 0; index < length; index++ ) {
- elem = elements[ index ];
- if ( !elem.style ) {
- continue;
- }
- if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
- elem.style.display = show ? values[ index ] || "" : "none";
- }
- }
-
- return elements;
-}
-
-jQuery.fn.extend({
- css: function( name, value ) {
- return jQuery.access( this, function( elem, name, value ) {
- var len, styles,
- map = {},
- i = 0;
-
- if ( jQuery.isArray( name ) ) {
- styles = getStyles( elem );
- len = name.length;
-
- for ( ; i < len; i++ ) {
- map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
- }
-
- return map;
- }
-
- return value !== undefined ?
- jQuery.style( elem, name, value ) :
- jQuery.css( elem, name );
- }, name, value, arguments.length > 1 );
- },
- show: function() {
- return showHide( this, true );
- },
- hide: function() {
- return showHide( this );
- },
- toggle: function( state ) {
- var bool = typeof state === "boolean";
-
- return this.each(function() {
- if ( bool ? state : isHidden( this ) ) {
- jQuery( this ).show();
- } else {
- jQuery( this ).hide();
- }
- });
- }
-});
-
-jQuery.extend({
- // Add in style property hooks for overriding the default
- // behavior of getting and setting a style property
- cssHooks: {
- opacity: {
- get: function( elem, computed ) {
- if ( computed ) {
- // We should always get a number back from opacity
- var ret = curCSS( elem, "opacity" );
- return ret === "" ? "1" : ret;
- }
- }
- }
- },
-
- // Exclude the following css properties to add px
- cssNumber: {
- "columnCount": true,
- "fillOpacity": true,
- "fontWeight": true,
- "lineHeight": true,
- "opacity": true,
- "orphans": true,
- "widows": true,
- "zIndex": true,
- "zoom": true
- },
-
- // Add in properties whose names you wish to fix before
- // setting or getting the value
- cssProps: {
- // normalize float css property
- "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
- },
-
- // Get and set the style property on a DOM Node
- style: function( elem, name, value, extra ) {
- // Don't set styles on text and comment nodes
- if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
- return;
- }
-
- // Make sure that we're working with the right name
- var ret, type, hooks,
- origName = jQuery.camelCase( name ),
- style = elem.style;
-
- name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
-
- // gets hook for the prefixed version
- // followed by the unprefixed version
- hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
-
- // Check if we're setting a value
- if ( value !== undefined ) {
- type = typeof value;
-
- // convert relative number strings (+= or -=) to relative numbers. #7345
- if ( type === "string" && (ret = rrelNum.exec( value )) ) {
- value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
- // Fixes bug #9237
- type = "number";
- }
-
- // Make sure that NaN and null values aren't set. See: #7116
- if ( value == null || type === "number" && isNaN( value ) ) {
- return;
- }
-
- // If a number was passed in, add 'px' to the (except for certain CSS properties)
- if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
- value += "px";
- }
-
- // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
- // but it would mean to define eight (for every problematic property) identical functions
- if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
- style[ name ] = "inherit";
- }
-
- // If a hook was provided, use that value, otherwise just set the specified value
- if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
-
- // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
- // Fixes bug #5509
- try {
- style[ name ] = value;
- } catch(e) {}
- }
-
- } else {
- // If a hook was provided get the non-computed value from there
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
- return ret;
- }
-
- // Otherwise just get the value from the style object
- return style[ name ];
- }
- },
-
- css: function( elem, name, extra, styles ) {
- var num, val, hooks,
- origName = jQuery.camelCase( name );
-
- // Make sure that we're working with the right name
- name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
-
- // gets hook for the prefixed version
- // followed by the unprefixed version
- hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
-
- // If a hook was provided get the computed value from there
- if ( hooks && "get" in hooks ) {
- val = hooks.get( elem, true, extra );
- }
-
- // Otherwise, if a way to get the computed value exists, use that
- if ( val === undefined ) {
- val = curCSS( elem, name, styles );
- }
-
- //convert "normal" to computed value
- if ( val === "normal" && name in cssNormalTransform ) {
- val = cssNormalTransform[ name ];
- }
-
- // Return, converting to number if forced or a qualifier was provided and val looks numeric
- if ( extra === "" || extra ) {
- num = parseFloat( val );
- return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
- }
- return val;
- },
-
- // A method for quickly swapping in/out CSS properties to get correct calculations
- swap: function( elem, options, callback, args ) {
- var ret, name,
- old = {};
-
- // Remember the old values, and insert the new ones
- for ( name in options ) {
- old[ name ] = elem.style[ name ];
- elem.style[ name ] = options[ name ];
- }
-
- ret = callback.apply( elem, args || [] );
-
- // Revert the old values
- for ( name in options ) {
- elem.style[ name ] = old[ name ];
- }
-
- return ret;
- }
-});
-
-// NOTE: we've included the "window" in window.getComputedStyle
-// because jsdom on node.js will break without it.
-if ( window.getComputedStyle ) {
- getStyles = function( elem ) {
- return window.getComputedStyle( elem, null );
- };
-
- curCSS = function( elem, name, _computed ) {
- var width, minWidth, maxWidth,
- computed = _computed || getStyles( elem ),
-
- // getPropertyValue is only needed for .css('filter') in IE9, see #12537
- ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
- style = elem.style;
-
- if ( computed ) {
-
- if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
- ret = jQuery.style( elem, name );
- }
-
- // A tribute to the "awesome hack by Dean Edwards"
- // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
- // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
- // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
- if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
-
- // Remember the original values
- width = style.width;
- minWidth = style.minWidth;
- maxWidth = style.maxWidth;
-
- // Put in the new values to get a computed value out
- style.minWidth = style.maxWidth = style.width = ret;
- ret = computed.width;
-
- // Revert the changed values
- style.width = width;
- style.minWidth = minWidth;
- style.maxWidth = maxWidth;
- }
- }
-
- return ret;
- };
-} else if ( document.documentElement.currentStyle ) {
- getStyles = function( elem ) {
- return elem.currentStyle;
- };
-
- curCSS = function( elem, name, _computed ) {
- var left, rs, rsLeft,
- computed = _computed || getStyles( elem ),
- ret = computed ? computed[ name ] : undefined,
- style = elem.style;
-
- // Avoid setting ret to empty string here
- // so we don't default to auto
- if ( ret == null && style && style[ name ] ) {
- ret = style[ name ];
- }
-
- // From the awesome hack by Dean Edwards
- // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
-
- // If we're not dealing with a regular pixel number
- // but a number that has a weird ending, we need to convert it to pixels
- // but not position css attributes, as those are proportional to the parent element instead
- // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
- if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
-
- // Remember the original values
- left = style.left;
- rs = elem.runtimeStyle;
- rsLeft = rs && rs.left;
-
- // Put in the new values to get a computed value out
- if ( rsLeft ) {
- rs.left = elem.currentStyle.left;
- }
- style.left = name === "fontSize" ? "1em" : ret;
- ret = style.pixelLeft + "px";
-
- // Revert the changed values
- style.left = left;
- if ( rsLeft ) {
- rs.left = rsLeft;
- }
- }
-
- return ret === "" ? "auto" : ret;
- };
-}
-
-function setPositiveNumber( elem, value, subtract ) {
- var matches = rnumsplit.exec( value );
- return matches ?
- // Guard against undefined "subtract", e.g., when used as in cssHooks
- Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
- value;
-}
-
-function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
- var i = extra === ( isBorderBox ? "border" : "content" ) ?
- // If we already have the right measurement, avoid augmentation
- 4 :
- // Otherwise initialize for horizontal or vertical properties
- name === "width" ? 1 : 0,
-
- val = 0;
-
- for ( ; i < 4; i += 2 ) {
- // both box models exclude margin, so add it if we want it
- if ( extra === "margin" ) {
- val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
- }
-
- if ( isBorderBox ) {
- // border-box includes padding, so remove it if we want content
- if ( extra === "content" ) {
- val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
- }
-
- // at this point, extra isn't border nor margin, so remove border
- if ( extra !== "margin" ) {
- val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
- }
- } else {
- // at this point, extra isn't content, so add padding
- val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
-
- // at this point, extra isn't content nor padding, so add border
- if ( extra !== "padding" ) {
- val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
- }
- }
- }
-
- return val;
-}
-
-function getWidthOrHeight( elem, name, extra ) {
-
- // Start with offset property, which is equivalent to the border-box value
- var valueIsBorderBox = true,
- val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
- styles = getStyles( elem ),
- isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
-
- // some non-html elements return undefined for offsetWidth, so check for null/undefined
- // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
- // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
- if ( val <= 0 || val == null ) {
- // Fall back to computed then uncomputed css if necessary
- val = curCSS( elem, name, styles );
- if ( val < 0 || val == null ) {
- val = elem.style[ name ];
- }
-
- // Computed unit is not pixels. Stop here and return.
- if ( rnumnonpx.test(val) ) {
- return val;
- }
-
- // we need the check for style in case a browser which returns unreliable values
- // for getComputedStyle silently falls back to the reliable elem.style
- valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
-
- // Normalize "", auto, and prepare for extra
- val = parseFloat( val ) || 0;
- }
-
- // use the active box-sizing model to add/subtract irrelevant styles
- return ( val +
- augmentWidthOrHeight(
- elem,
- name,
- extra || ( isBorderBox ? "border" : "content" ),
- valueIsBorderBox,
- styles
- )
- ) + "px";
-}
-
-// Try to determine the default display value of an element
-function css_defaultDisplay( nodeName ) {
- var doc = document,
- display = elemdisplay[ nodeName ];
-
- if ( !display ) {
- display = actualDisplay( nodeName, doc );
-
- // If the simple way fails, read from inside an iframe
- if ( display === "none" || !display ) {
- // Use the already-created iframe if possible
- iframe = ( iframe ||
- jQuery("<iframe frameborder='0' width='0' height='0'/>")
- .css( "cssText", "display:block !important" )
- ).appendTo( doc.documentElement );
-
- // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
- doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
- doc.write("<!doctype html><html><body>");
- doc.close();
-
- display = actualDisplay( nodeName, doc );
- iframe.detach();
- }
-
- // Store the correct default display
- elemdisplay[ nodeName ] = display;
- }
-
- return display;
-}
-
-// Called ONLY from within css_defaultDisplay
-function actualDisplay( name, doc ) {
- var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
- display = jQuery.css( elem[0], "display" );
- elem.remove();
- return display;
-}
-
-jQuery.each([ "height", "width" ], function( i, name ) {
- jQuery.cssHooks[ name ] = {
- get: function( elem, computed, extra ) {
- if ( computed ) {
- // certain elements can have dimension info if we invisibly show them
- // however, it must have a current display style that would benefit from this
- return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
- jQuery.swap( elem, cssShow, function() {
- return getWidthOrHeight( elem, name, extra );
- }) :
- getWidthOrHeight( elem, name, extra );
- }
- },
-
- set: function( elem, value, extra ) {
- var styles = extra && getStyles( elem );
- return setPositiveNumber( elem, value, extra ?
- augmentWidthOrHeight(
- elem,
- name,
- extra,
- jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
- styles
- ) : 0
- );
- }
- };
-});
-
-if ( !jQuery.support.opacity ) {
- jQuery.cssHooks.opacity = {
- get: function( elem, computed ) {
- // IE uses filters for opacity
- return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
- ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
- computed ? "1" : "";
- },
-
- set: function( elem, value ) {
- var style = elem.style,
- currentStyle = elem.currentStyle,
- opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
- filter = currentStyle && currentStyle.filter || style.filter || "";
-
- // IE has trouble with opacity if it does not have layout
- // Force it by setting the zoom level
- style.zoom = 1;
-
- // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
- // if value === "", then remove inline opacity #12685
- if ( ( value >= 1 || value === "" ) &&
- jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
- style.removeAttribute ) {
-
- // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
- // if "filter:" is present at all, clearType is disabled, we want to avoid this
- // style.removeAttribute is IE Only, but so apparently is this code path...
- style.removeAttribute( "filter" );
-
- // if there is no filter style applied in a css rule or unset inline opacity, we are done
- if ( value === "" || currentStyle && !currentStyle.filter ) {
- return;
- }
- }
-
- // otherwise, set new filter values
- style.filter = ralpha.test( filter ) ?
- filter.replace( ralpha, opacity ) :
- filter + " " + opacity;
- }
- };
-}
-
-// These hooks cannot be added until DOM ready because the support test
-// for it is not run until after DOM ready
-jQuery(function() {
- if ( !jQuery.support.reliableMarginRight ) {
- jQuery.cssHooks.marginRight = {
- get: function( elem, computed ) {
- if ( computed ) {
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- // Work around by temporarily setting element display to inline-block
- return jQuery.swap( elem, { "display": "inline-block" },
- curCSS, [ elem, "marginRight" ] );
- }
- }
- };
- }
-
- // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
- // getComputedStyle returns percent when specified for top/left/bottom/right
- // rather than make the css module depend on the offset module, we just check for it here
- if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
- jQuery.each( [ "top", "left" ], function( i, prop ) {
- jQuery.cssHooks[ prop ] = {
- get: function( elem, computed ) {
- if ( computed ) {
- computed = curCSS( elem, prop );
- // if curCSS returns percentage, fallback to offset
- return rnumnonpx.test( computed ) ?
- jQuery( elem ).position()[ prop ] + "px" :
- computed;
- }
- }
- };
- });
- }
-
-});
-
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.hidden = function( elem ) {
- // Support: Opera <= 12.12
- // Opera reports offsetWidths and offsetHeights less than zero on some elements
- return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
- (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
- };
-
- jQuery.expr.filters.visible = function( elem ) {
- return !jQuery.expr.filters.hidden( elem );
- };
-}
-
-// These hooks are used by animate to expand properties
-jQuery.each({
- margin: "",
- padding: "",
- border: "Width"
-}, function( prefix, suffix ) {
- jQuery.cssHooks[ prefix + suffix ] = {
- expand: function( value ) {
- var i = 0,
- expanded = {},
-
- // assumes a single number if not a string
- parts = typeof value === "string" ? value.split(" ") : [ value ];
-
- for ( ; i < 4; i++ ) {
- expanded[ prefix + cssExpand[ i ] + suffix ] =
- parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
- }
-
- return expanded;
- }
- };
-
- if ( !rmargin.test( prefix ) ) {
- jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
- }
-});
-var r20 = /%20/g,
- rbracket = /\[\]$/,
- rCRLF = /\r?\n/g,
- rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
- rsubmittable = /^(?:input|select|textarea|keygen)/i;
-
-jQuery.fn.extend({
- serialize: function() {
- return jQuery.param( this.serializeArray() );
- },
- serializeArray: function() {
- return this.map(function(){
- // Can add propHook for "elements" to filter or add form elements
- var elements = jQuery.prop( this, "elements" );
- return elements ? jQuery.makeArray( elements ) : this;
- })
- .filter(function(){
- var type = this.type;
- // Use .is(":disabled") so that fieldset[disabled] works
- return this.name && !jQuery( this ).is( ":disabled" ) &&
- rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
- ( this.checked || !manipulation_rcheckableType.test( type ) );
- })
- .map(function( i, elem ){
- var val = jQuery( this ).val();
-
- return val == null ?
- null :
- jQuery.isArray( val ) ?
- jQuery.map( val, function( val ){
- return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }) :
- { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
- }).get();
- }
-});
-
-//Serialize an array of form elements or a set of
-//key/values into a query string
-jQuery.param = function( a, traditional ) {
- var prefix,
- s = [],
- add = function( key, value ) {
- // If value is a function, invoke it and return its value
- value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
- s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
- };
-
- // Set traditional to true for jQuery <= 1.3.2 behavior.
- if ( traditional === undefined ) {
- traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
- }
-
- // If an array was passed in, assume that it is an array of form elements.
- if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
- // Serialize the form elements
- jQuery.each( a, function() {
- add( this.name, this.value );
- });
-
- } else {
- // If traditional, encode the "old" way (the way 1.3.2 or older
- // did it), otherwise encode params recursively.
- for ( prefix in a ) {
- buildParams( prefix, a[ prefix ], traditional, add );
- }
- }
-
- // Return the resulting serialization
- return s.join( "&" ).replace( r20, "+" );
-};
-
-function buildParams( prefix, obj, traditional, add ) {
- var name;
-
- if ( jQuery.isArray( obj ) ) {
- // Serialize array item.
- jQuery.each( obj, function( i, v ) {
- if ( traditional || rbracket.test( prefix ) ) {
- // Treat each array item as a scalar.
- add( prefix, v );
-
- } else {
- // Item is non-scalar (array or object), encode its numeric index.
- buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
- }
- });
-
- } else if ( !traditional && jQuery.type( obj ) === "object" ) {
- // Serialize object item.
- for ( name in obj ) {
- buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
- }
-
- } else {
- // Serialize scalar item.
- add( prefix, obj );
- }
-}
-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
- "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
- "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
-
- // Handle event binding
- jQuery.fn[ name ] = function( data, fn ) {
- return arguments.length > 0 ?
- this.on( name, null, data, fn ) :
- this.trigger( name );
- };
-});
-
-jQuery.fn.hover = function( fnOver, fnOut ) {
- return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
-};
-var
- // Document location
- ajaxLocParts,
- ajaxLocation,
- ajax_nonce = jQuery.now(),
-
- ajax_rquery = /\?/,
- rhash = /#.*$/,
- rts = /([?&])_=[^&]*/,
- rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
- // #7653, #8125, #8152: local protocol detection
- rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
- rnoContent = /^(?:GET|HEAD)$/,
- rprotocol = /^\/\//,
- rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
-
- // Keep a copy of the old load method
- _load = jQuery.fn.load,
-
- /* Prefilters
- * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
- * 2) These are called:
- * - BEFORE asking for a transport
- * - AFTER param serialization (s.data is a string if s.processData is true)
- * 3) key is the dataType
- * 4) the catchall symbol "*" can be used
- * 5) execution will start with transport dataType and THEN continue down to "*" if needed
- */
- prefilters = {},
-
- /* Transports bindings
- * 1) key is the dataType
- * 2) the catchall symbol "*" can be used
- * 3) selection will start with transport dataType and THEN go to "*" if needed
- */
- transports = {},
-
- // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
- allTypes = "*/".concat("*");
-
-// #8138, IE may throw an exception when accessing
-// a field from window.location if document.domain has been set
-try {
- ajaxLocation = location.href;
-} catch( e ) {
- // Use the href attribute of an A element
- // since IE will modify it given document.location
- ajaxLocation = document.createElement( "a" );
- ajaxLocation.href = "";
- ajaxLocation = ajaxLocation.href;
-}
-
-// Segment location into parts
-ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
-
-// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
-function addToPrefiltersOrTransports( structure ) {
-
- // dataTypeExpression is optional and defaults to "*"
- return function( dataTypeExpression, func ) {
-
- if ( typeof dataTypeExpression !== "string" ) {
- func = dataTypeExpression;
- dataTypeExpression = "*";
- }
-
- var dataType,
- i = 0,
- dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
-
- if ( jQuery.isFunction( func ) ) {
- // For each dataType in the dataTypeExpression
- while ( (dataType = dataTypes[i++]) ) {
- // Prepend if requested
- if ( dataType[0] === "+" ) {
- dataType = dataType.slice( 1 ) || "*";
- (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
-
- // Otherwise append
- } else {
- (structure[ dataType ] = structure[ dataType ] || []).push( func );
- }
- }
- }
- };
-}
-
-// Base inspection function for prefilters and transports
-function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
-
- var inspected = {},
- seekingTransport = ( structure === transports );
-
- function inspect( dataType ) {
- var selected;
- inspected[ dataType ] = true;
- jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
- var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
- if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
- options.dataTypes.unshift( dataTypeOrTransport );
- inspect( dataTypeOrTransport );
- return false;
- } else if ( seekingTransport ) {
- return !( selected = dataTypeOrTransport );
- }
- });
- return selected;
- }
-
- return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
-}
-
-// A special extend for ajax options
-// that takes "flat" options (not to be deep extended)
-// Fixes #9887
-function ajaxExtend( target, src ) {
- var deep, key,
- flatOptions = jQuery.ajaxSettings.flatOptions || {};
-
- for ( key in src ) {
- if ( src[ key ] !== undefined ) {
- ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
- }
- }
- if ( deep ) {
- jQuery.extend( true, target, deep );
- }
-
- return target;
-}
-
-jQuery.fn.load = function( url, params, callback ) {
- if ( typeof url !== "string" && _load ) {
- return _load.apply( this, arguments );
- }
-
- var selector, response, type,
- self = this,
- off = url.indexOf(" ");
-
- if ( off >= 0 ) {
- selector = url.slice( off, url.length );
- url = url.slice( 0, off );
- }
-
- // If it's a function
- if ( jQuery.isFunction( params ) ) {
-
- // We assume that it's the callback
- callback = params;
- params = undefined;
-
- // Otherwise, build a param string
- } else if ( params && typeof params === "object" ) {
- type = "POST";
- }
-
- // If we have elements to modify, make the request
- if ( self.length > 0 ) {
- jQuery.ajax({
- url: url,
-
- // if "type" variable is undefined, then "GET" method will be used
- type: type,
- dataType: "html",
- data: params
- }).done(function( responseText ) {
-
- // Save response for use in complete callback
- response = arguments;
-
- self.html( selector ?
-
- // If a selector was specified, locate the right elements in a dummy div
- // Exclude scripts to avoid IE 'Permission Denied' errors
- jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
-
- // Otherwise use the full result
- responseText );
-
- }).complete( callback && function( jqXHR, status ) {
- self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
- });
- }
-
- return this;
-};
-
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
- jQuery.fn[ type ] = function( fn ){
- return this.on( type, fn );
- };
-});
-
-jQuery.each( [ "get", "post" ], function( i, method ) {
- jQuery[ method ] = function( url, data, callback, type ) {
- // shift arguments if data argument was omitted
- if ( jQuery.isFunction( data ) ) {
- type = type || callback;
- callback = data;
- data = undefined;
- }
-
- return jQuery.ajax({
- url: url,
- type: method,
- dataType: type,
- data: data,
- success: callback
- });
- };
-});
-
-jQuery.extend({
-
- // Counter for holding the number of active queries
- active: 0,
-
- // Last-Modified header cache for next request
- lastModified: {},
- etag: {},
-
- ajaxSettings: {
- url: ajaxLocation,
- type: "GET",
- isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
- global: true,
- processData: true,
- async: true,
- contentType: "application/x-www-form-urlencoded; charset=UTF-8",
- /*
- timeout: 0,
- data: null,
- dataType: null,
- username: null,
- password: null,
- cache: null,
- throws: false,
- traditional: false,
- headers: {},
- */
-
- accepts: {
- "*": allTypes,
- text: "text/plain",
- html: "text/html",
- xml: "application/xml, text/xml",
- json: "application/json, text/javascript"
- },
-
- contents: {
- xml: /xml/,
- html: /html/,
- json: /json/
- },
-
- responseFields: {
- xml: "responseXML",
- text: "responseText"
- },
-
- // Data converters
- // Keys separate source (or catchall "*") and destination types with a single space
- converters: {
-
- // Convert anything to text
- "* text": window.String,
-
- // Text to html (true = no transformation)
- "text html": true,
-
- // Evaluate text as a json expression
- "text json": jQuery.parseJSON,
-
- // Parse text as xml
- "text xml": jQuery.parseXML
- },
-
- // For options that shouldn't be deep extended:
- // you can add your own custom options here if
- // and when you create one that shouldn't be
- // deep extended (see ajaxExtend)
- flatOptions: {
- url: true,
- context: true
- }
- },
-
- // Creates a full fledged settings object into target
- // with both ajaxSettings and settings fields.
- // If target is omitted, writes into ajaxSettings.
- ajaxSetup: function( target, settings ) {
- return settings ?
-
- // Building a settings object
- ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
-
- // Extending ajaxSettings
- ajaxExtend( jQuery.ajaxSettings, target );
- },
-
- ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
- ajaxTransport: addToPrefiltersOrTransports( transports ),
-
- // Main method
- ajax: function( url, options ) {
-
- // If url is an object, simulate pre-1.5 signature
- if ( typeof url === "object" ) {
- options = url;
- url = undefined;
- }
-
- // Force options to be an object
- options = options || {};
-
- var // Cross-domain detection vars
- parts,
- // Loop variable
- i,
- // URL without anti-cache param
- cacheURL,
- // Response headers as string
- responseHeadersString,
- // timeout handle
- timeoutTimer,
-
- // To know if global events are to be dispatched
- fireGlobals,
-
- transport,
- // Response headers
- responseHeaders,
- // Create the final options object
- s = jQuery.ajaxSetup( {}, options ),
- // Callbacks context
- callbackContext = s.context || s,
- // Context for global events is callbackContext if it is a DOM node or jQuery collection
- globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
- jQuery( callbackContext ) :
- jQuery.event,
- // Deferreds
- deferred = jQuery.Deferred(),
- completeDeferred = jQuery.Callbacks("once memory"),
- // Status-dependent callbacks
- statusCode = s.statusCode || {},
- // Headers (they are sent all at once)
- requestHeaders = {},
- requestHeadersNames = {},
- // The jqXHR state
- state = 0,
- // Default abort message
- strAbort = "canceled",
- // Fake xhr
- jqXHR = {
- readyState: 0,
-
- // Builds headers hashtable if needed
- getResponseHeader: function( key ) {
- var match;
- if ( state === 2 ) {
- if ( !responseHeaders ) {
- responseHeaders = {};
- while ( (match = rheaders.exec( responseHeadersString )) ) {
- responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
- }
- }
- match = responseHeaders[ key.toLowerCase() ];
- }
- return match == null ? null : match;
- },
-
- // Raw string
- getAllResponseHeaders: function() {
- return state === 2 ? responseHeadersString : null;
- },
-
- // Caches the header
- setRequestHeader: function( name, value ) {
- var lname = name.toLowerCase();
- if ( !state ) {
- name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
- requestHeaders[ name ] = value;
- }
- return this;
- },
-
- // Overrides response content-type header
- overrideMimeType: function( type ) {
- if ( !state ) {
- s.mimeType = type;
- }
- return this;
- },
-
- // Status-dependent callbacks
- statusCode: function( map ) {
- var code;
- if ( map ) {
- if ( state < 2 ) {
- for ( code in map ) {
- // Lazy-add the new callback in a way that preserves old ones
- statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
- }
- } else {
- // Execute the appropriate callbacks
- jqXHR.always( map[ jqXHR.status ] );
- }
- }
- return this;
- },
-
- // Cancel the request
- abort: function( statusText ) {
- var finalText = statusText || strAbort;
- if ( transport ) {
- transport.abort( finalText );
- }
- done( 0, finalText );
- return this;
- }
- };
-
- // Attach deferreds
- deferred.promise( jqXHR ).complete = completeDeferred.add;
- jqXHR.success = jqXHR.done;
- jqXHR.error = jqXHR.fail;
-
- // Remove hash character (#7531: and string promotion)
- // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
- // Handle falsy url in the settings object (#10093: consistency with old signature)
- // We also use the url parameter if available
- s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
-
- // Alias method option to type as per ticket #12004
- s.type = options.method || options.type || s.method || s.type;
-
- // Extract dataTypes list
- s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
-
- // A cross-domain request is in order when we have a protocol:host:port mismatch
- if ( s.crossDomain == null ) {
- parts = rurl.exec( s.url.toLowerCase() );
- s.crossDomain = !!( parts &&
- ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
- ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
- ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
- );
- }
-
- // Convert data if not already a string
- if ( s.data && s.processData && typeof s.data !== "string" ) {
- s.data = jQuery.param( s.data, s.traditional );
- }
-
- // Apply prefilters
- inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
-
- // If request was aborted inside a prefilter, stop there
- if ( state === 2 ) {
- return jqXHR;
- }
-
- // We can fire global events as of now if asked to
- fireGlobals = s.global;
-
- // Watch for a new set of requests
- if ( fireGlobals && jQuery.active++ === 0 ) {
- jQuery.event.trigger("ajaxStart");
- }
-
- // Uppercase the type
- s.type = s.type.toUpperCase();
-
- // Determine if request has content
- s.hasContent = !rnoContent.test( s.type );
-
- // Save the URL in case we're toying with the If-Modified-Since
- // and/or If-None-Match header later on
- cacheURL = s.url;
-
- // More options handling for requests with no content
- if ( !s.hasContent ) {
-
- // If data is available, append data to url
- if ( s.data ) {
- cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
- // #9682: remove data so that it's not used in an eventual retry
- delete s.data;
- }
-
- // Add anti-cache in url if needed
- if ( s.cache === false ) {
- s.url = rts.test( cacheURL ) ?
-
- // If there is already a '_' parameter, set its value
- cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
-
- // Otherwise add one to the end
- cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
- }
- }
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if ( s.ifModified ) {
- if ( jQuery.lastModified[ cacheURL ] ) {
- jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
- }
- if ( jQuery.etag[ cacheURL ] ) {
- jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
- }
- }
-
- // Set the correct header, if data is being sent
- if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
- jqXHR.setRequestHeader( "Content-Type", s.contentType );
- }
-
- // Set the Accepts header for the server, depending on the dataType
- jqXHR.setRequestHeader(
- "Accept",
- s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
- s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
- s.accepts[ "*" ]
- );
-
- // Check for headers option
- for ( i in s.headers ) {
- jqXHR.setRequestHeader( i, s.headers[ i ] );
- }
-
- // Allow custom headers/mimetypes and early abort
- if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
- // Abort if not done already and return
- return jqXHR.abort();
- }
-
- // aborting is no longer a cancellation
- strAbort = "abort";
-
- // Install callbacks on deferreds
- for ( i in { success: 1, error: 1, complete: 1 } ) {
- jqXHR[ i ]( s[ i ] );
- }
-
- // Get transport
- transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
-
- // If no transport, we auto-abort
- if ( !transport ) {
- done( -1, "No Transport" );
- } else {
- jqXHR.readyState = 1;
-
- // Send global event
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
- }
- // Timeout
- if ( s.async && s.timeout > 0 ) {
- timeoutTimer = setTimeout(function() {
- jqXHR.abort("timeout");
- }, s.timeout );
- }
-
- try {
- state = 1;
- transport.send( requestHeaders, done );
- } catch ( e ) {
- // Propagate exception as error if not done
- if ( state < 2 ) {
- done( -1, e );
- // Simply rethrow otherwise
- } else {
- throw e;
- }
- }
- }
-
- // Callback for when everything is done
- function done( status, nativeStatusText, responses, headers ) {
- var isSuccess, success, error, response, modified,
- statusText = nativeStatusText;
-
- // Called once
- if ( state === 2 ) {
- return;
- }
-
- // State is "done" now
- state = 2;
-
- // Clear timeout if it exists
- if ( timeoutTimer ) {
- clearTimeout( timeoutTimer );
- }
-
- // Dereference transport for early garbage collection
- // (no matter how long the jqXHR object will be used)
- transport = undefined;
-
- // Cache response headers
- responseHeadersString = headers || "";
-
- // Set readyState
- jqXHR.readyState = status > 0 ? 4 : 0;
-
- // Get response data
- if ( responses ) {
- response = ajaxHandleResponses( s, jqXHR, responses );
- }
-
- // If successful, handle type chaining
- if ( status >= 200 && status < 300 || status === 304 ) {
-
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if ( s.ifModified ) {
- modified = jqXHR.getResponseHeader("Last-Modified");
- if ( modified ) {
- jQuery.lastModified[ cacheURL ] = modified;
- }
- modified = jqXHR.getResponseHeader("etag");
- if ( modified ) {
- jQuery.etag[ cacheURL ] = modified;
- }
- }
-
- // if no content
- if ( status === 204 ) {
- isSuccess = true;
- statusText = "nocontent";
-
- // if not modified
- } else if ( status === 304 ) {
- isSuccess = true;
- statusText = "notmodified";
-
- // If we have data, let's convert it
- } else {
- isSuccess = ajaxConvert( s, response );
- statusText = isSuccess.state;
- success = isSuccess.data;
- error = isSuccess.error;
- isSuccess = !error;
- }
- } else {
- // We extract error from statusText
- // then normalize statusText and status for non-aborts
- error = statusText;
- if ( status || !statusText ) {
- statusText = "error";
- if ( status < 0 ) {
- status = 0;
- }
- }
- }
-
- // Set data for the fake xhr object
- jqXHR.status = status;
- jqXHR.statusText = ( nativeStatusText || statusText ) + "";
-
- // Success/Error
- if ( isSuccess ) {
- deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
- } else {
- deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
- }
-
- // Status-dependent callbacks
- jqXHR.statusCode( statusCode );
- statusCode = undefined;
-
- if ( fireGlobals ) {
- globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
- [ jqXHR, s, isSuccess ? success : error ] );
- }
-
- // Complete
- completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
-
- if ( fireGlobals ) {
- globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
- // Handle the global AJAX counter
- if ( !( --jQuery.active ) ) {
- jQuery.event.trigger("ajaxStop");
- }
- }
- }
-
- return jqXHR;
- },
-
- getScript: function( url, callback ) {
- return jQuery.get( url, undefined, callback, "script" );
- },
-
- getJSON: function( url, data, callback ) {
- return jQuery.get( url, data, callback, "json" );
- }
-});
-
-/* Handles responses to an ajax request:
- * - sets all responseXXX fields accordingly
- * - finds the right dataType (mediates between content-type and expected dataType)
- * - returns the corresponding response
- */
-function ajaxHandleResponses( s, jqXHR, responses ) {
- var firstDataType, ct, finalDataType, type,
- contents = s.contents,
- dataTypes = s.dataTypes,
- responseFields = s.responseFields;
-
- // Fill responseXXX fields
- for ( type in responseFields ) {
- if ( type in responses ) {
- jqXHR[ responseFields[type] ] = responses[ type ];
- }
- }
-
- // Remove auto dataType and get content-type in the process
- while( dataTypes[ 0 ] === "*" ) {
- dataTypes.shift();
- if ( ct === undefined ) {
- ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
- }
- }
-
- // Check if we're dealing with a known content-type
- if ( ct ) {
- for ( type in contents ) {
- if ( contents[ type ] && contents[ type ].test( ct ) ) {
- dataTypes.unshift( type );
- break;
- }
- }
- }
-
- // Check to see if we have a response for the expected dataType
- if ( dataTypes[ 0 ] in responses ) {
- finalDataType = dataTypes[ 0 ];
- } else {
- // Try convertible dataTypes
- for ( type in responses ) {
- if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
- finalDataType = type;
- break;
- }
- if ( !firstDataType ) {
- firstDataType = type;
- }
- }
- // Or just use first one
- finalDataType = finalDataType || firstDataType;
- }
-
- // If we found a dataType
- // We add the dataType to the list if needed
- // and return the corresponding response
- if ( finalDataType ) {
- if ( finalDataType !== dataTypes[ 0 ] ) {
- dataTypes.unshift( finalDataType );
- }
- return responses[ finalDataType ];
- }
-}
-
-// Chain conversions given the request and the original response
-function ajaxConvert( s, response ) {
- var conv2, current, conv, tmp,
- converters = {},
- i = 0,
- // Work with a copy of dataTypes in case we need to modify it for conversion
- dataTypes = s.dataTypes.slice(),
- prev = dataTypes[ 0 ];
-
- // Apply the dataFilter if provided
- if ( s.dataFilter ) {
- response = s.dataFilter( response, s.dataType );
- }
-
- // Create converters map with lowercased keys
- if ( dataTypes[ 1 ] ) {
- for ( conv in s.converters ) {
- converters[ conv.toLowerCase() ] = s.converters[ conv ];
- }
- }
-
- // Convert to each sequential dataType, tolerating list modification
- for ( ; (current = dataTypes[++i]); ) {
-
- // There's only work to do if current dataType is non-auto
- if ( current !== "*" ) {
-
- // Convert response if prev dataType is non-auto and differs from current
- if ( prev !== "*" && prev !== current ) {
-
- // Seek a direct converter
- conv = converters[ prev + " " + current ] || converters[ "* " + current ];
-
- // If none found, seek a pair
- if ( !conv ) {
- for ( conv2 in converters ) {
-
- // If conv2 outputs current
- tmp = conv2.split(" ");
- if ( tmp[ 1 ] === current ) {
-
- // If prev can be converted to accepted input
- conv = converters[ prev + " " + tmp[ 0 ] ] ||
- converters[ "* " + tmp[ 0 ] ];
- if ( conv ) {
- // Condense equivalence converters
- if ( conv === true ) {
- conv = converters[ conv2 ];
-
- // Otherwise, insert the intermediate dataType
- } else if ( converters[ conv2 ] !== true ) {
- current = tmp[ 0 ];
- dataTypes.splice( i--, 0, current );
- }
-
- break;
- }
- }
- }
- }
-
- // Apply converter (if not an equivalence)
- if ( conv !== true ) {
-
- // Unless errors are allowed to bubble, catch and return them
- if ( conv && s["throws"] ) {
- response = conv( response );
- } else {
- try {
- response = conv( response );
- } catch ( e ) {
- return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
- }
- }
- }
- }
-
- // Update prev for next iteration
- prev = current;
- }
- }
-
- return { state: "success", data: response };
-}
-// Install script dataType
-jQuery.ajaxSetup({
- accepts: {
- script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
- },
- contents: {
- script: /(?:java|ecma)script/
- },
- converters: {
- "text script": function( text ) {
- jQuery.globalEval( text );
- return text;
- }
- }
-});
-
-// Handle cache's special case and global
-jQuery.ajaxPrefilter( "script", function( s ) {
- if ( s.cache === undefined ) {
- s.cache = false;
- }
- if ( s.crossDomain ) {
- s.type = "GET";
- s.global = false;
- }
-});
-
-// Bind script tag hack transport
-jQuery.ajaxTransport( "script", function(s) {
-
- // This transport only deals with cross domain requests
- if ( s.crossDomain ) {
-
- var script,
- head = document.head || jQuery("head")[0] || document.documentElement;
-
- return {
-
- send: function( _, callback ) {
-
- script = document.createElement("script");
-
- script.async = true;
-
- if ( s.scriptCharset ) {
- script.charset = s.scriptCharset;
- }
-
- script.src = s.url;
-
- // Attach handlers for all browsers
- script.onload = script.onreadystatechange = function( _, isAbort ) {
-
- if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
-
- // Handle memory leak in IE
- script.onload = script.onreadystatechange = null;
-
- // Remove the script
- if ( script.parentNode ) {
- script.parentNode.removeChild( script );
- }
-
- // Dereference the script
- script = null;
-
- // Callback if not abort
- if ( !isAbort ) {
- callback( 200, "success" );
- }
- }
- };
-
- // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
- // Use native DOM manipulation to avoid our domManip AJAX trickery
- head.insertBefore( script, head.firstChild );
- },
-
- abort: function() {
- if ( script ) {
- script.onload( undefined, true );
- }
- }
- };
- }
-});
-var oldCallbacks = [],
- rjsonp = /(=)\?(?=&|$)|\?\?/;
-
-// Default jsonp settings
-jQuery.ajaxSetup({
- jsonp: "callback",
- jsonpCallback: function() {
- var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
- this[ callback ] = true;
- return callback;
- }
-});
-
-// Detect, normalize options and install callbacks for jsonp requests
-jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
-
- var callbackName, overwritten, responseContainer,
- jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
- "url" :
- typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
- );
-
- // Handle iff the expected data type is "jsonp" or we have a parameter to set
- if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
-
- // Get callback name, remembering preexisting value associated with it
- callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
- s.jsonpCallback() :
- s.jsonpCallback;
-
- // Insert callback into url or form data
- if ( jsonProp ) {
- s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
- } else if ( s.jsonp !== false ) {
- s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
- }
-
- // Use data converter to retrieve json after script execution
- s.converters["script json"] = function() {
- if ( !responseContainer ) {
- jQuery.error( callbackName + " was not called" );
- }
- return responseContainer[ 0 ];
- };
-
- // force json dataType
- s.dataTypes[ 0 ] = "json";
-
- // Install callback
- overwritten = window[ callbackName ];
- window[ callbackName ] = function() {
- responseContainer = arguments;
- };
-
- // Clean-up function (fires after converters)
- jqXHR.always(function() {
- // Restore preexisting value
- window[ callbackName ] = overwritten;
-
- // Save back as free
- if ( s[ callbackName ] ) {
- // make sure that re-using the options doesn't screw things around
- s.jsonpCallback = originalSettings.jsonpCallback;
-
- // save the callback name for future use
- oldCallbacks.push( callbackName );
- }
-
- // Call if it was a function and we have a response
- if ( responseContainer && jQuery.isFunction( overwritten ) ) {
- overwritten( responseContainer[ 0 ] );
- }
-
- responseContainer = overwritten = undefined;
- });
-
- // Delegate to script
- return "script";
- }
-});
-var xhrCallbacks, xhrSupported,
- xhrId = 0,
- // #5280: Internet Explorer will keep connections alive if we don't abort on unload
- xhrOnUnloadAbort = window.ActiveXObject && function() {
- // Abort all pending requests
- var key;
- for ( key in xhrCallbacks ) {
- xhrCallbacks[ key ]( undefined, true );
- }
- };
-
-// Functions to create xhrs
-function createStandardXHR() {
- try {
- return new window.XMLHttpRequest();
- } catch( e ) {}
-}
-
-function createActiveXHR() {
- try {
- return new window.ActiveXObject("Microsoft.XMLHTTP");
- } catch( e ) {}
-}
-
-// Create the request object
-// (This is still attached to ajaxSettings for backward compatibility)
-jQuery.ajaxSettings.xhr = window.ActiveXObject ?
- /* Microsoft failed to properly
- * implement the XMLHttpRequest in IE7 (can't request local files),
- * so we use the ActiveXObject when it is available
- * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
- * we need a fallback.
- */
- function() {
- return !this.isLocal && createStandardXHR() || createActiveXHR();
- } :
- // For all other browsers, use the standard XMLHttpRequest object
- createStandardXHR;
-
-// Determine support properties
-xhrSupported = jQuery.ajaxSettings.xhr();
-jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
-xhrSupported = jQuery.support.ajax = !!xhrSupported;
-
-// Create transport if the browser can provide an xhr
-if ( xhrSupported ) {
-
- jQuery.ajaxTransport(function( s ) {
- // Cross domain only allowed if supported through XMLHttpRequest
- if ( !s.crossDomain || jQuery.support.cors ) {
-
- var callback;
-
- return {
- send: function( headers, complete ) {
-
- // Get a new xhr
- var handle, i,
- xhr = s.xhr();
-
- // Open the socket
- // Passing null username, generates a login popup on Opera (#2865)
- if ( s.username ) {
- xhr.open( s.type, s.url, s.async, s.username, s.password );
- } else {
- xhr.open( s.type, s.url, s.async );
- }
-
- // Apply custom fields if provided
- if ( s.xhrFields ) {
- for ( i in s.xhrFields ) {
- xhr[ i ] = s.xhrFields[ i ];
- }
- }
-
- // Override mime type if needed
- if ( s.mimeType && xhr.overrideMimeType ) {
- xhr.overrideMimeType( s.mimeType );
- }
-
- // X-Requested-With header
- // For cross-domain requests, seeing as conditions for a preflight are
- // akin to a jigsaw puzzle, we simply never set it to be sure.
- // (it can always be set on a per-request basis or even using ajaxSetup)
- // For same-domain requests, won't change header if already provided.
- if ( !s.crossDomain && !headers["X-Requested-With"] ) {
- headers["X-Requested-With"] = "XMLHttpRequest";
- }
-
- // Need an extra try/catch for cross domain requests in Firefox 3
- try {
- for ( i in headers ) {
- xhr.setRequestHeader( i, headers[ i ] );
- }
- } catch( err ) {}
-
- // Do send the request
- // This may raise an exception which is actually
- // handled in jQuery.ajax (so no try/catch here)
- xhr.send( ( s.hasContent && s.data ) || null );
-
- // Listener
- callback = function( _, isAbort ) {
- var status, responseHeaders, statusText, responses;
-
- // Firefox throws exceptions when accessing properties
- // of an xhr when a network error occurred
- // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
- try {
-
- // Was never called and is aborted or complete
- if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
-
- // Only called once
- callback = undefined;
-
- // Do not keep as active anymore
- if ( handle ) {
- xhr.onreadystatechange = jQuery.noop;
- if ( xhrOnUnloadAbort ) {
- delete xhrCallbacks[ handle ];
- }
- }
-
- // If it's an abort
- if ( isAbort ) {
- // Abort it manually if needed
- if ( xhr.readyState !== 4 ) {
- xhr.abort();
- }
- } else {
- responses = {};
- status = xhr.status;
- responseHeaders = xhr.getAllResponseHeaders();
-
- // When requesting binary data, IE6-9 will throw an exception
- // on any attempt to access responseText (#11426)
- if ( typeof xhr.responseText === "string" ) {
- responses.text = xhr.responseText;
- }
-
- // Firefox throws an exception when accessing
- // statusText for faulty cross-domain requests
- try {
- statusText = xhr.statusText;
- } catch( e ) {
- // We normalize with Webkit giving an empty statusText
- statusText = "";
- }
-
- // Filter status for non standard behaviors
-
- // If the request is local and we have data: assume a success
- // (success with no data won't get notified, that's the best we
- // can do given current implementations)
- if ( !status && s.isLocal && !s.crossDomain ) {
- status = responses.text ? 200 : 404;
- // IE - #1450: sometimes returns 1223 when it should be 204
- } else if ( status === 1223 ) {
- status = 204;
- }
- }
- }
- } catch( firefoxAccessException ) {
- if ( !isAbort ) {
- complete( -1, firefoxAccessException );
- }
- }
-
- // Call complete if needed
- if ( responses ) {
- complete( status, statusText, responses, responseHeaders );
- }
- };
-
- if ( !s.async ) {
- // if we're in sync mode we fire the callback
- callback();
- } else if ( xhr.readyState === 4 ) {
- // (IE6 & IE7) if it's in cache and has been
- // retrieved directly we need to fire the callback
- setTimeout( callback );
- } else {
- handle = ++xhrId;
- if ( xhrOnUnloadAbort ) {
- // Create the active xhrs callbacks list if needed
- // and attach the unload handler
- if ( !xhrCallbacks ) {
- xhrCallbacks = {};
- jQuery( window ).unload( xhrOnUnloadAbort );
- }
- // Add to list of active xhrs callbacks
- xhrCallbacks[ handle ] = callback;
- }
- xhr.onreadystatechange = callback;
- }
- },
-
- abort: function() {
- if ( callback ) {
- callback( undefined, true );
- }
- }
- };
- }
- });
-}
-var fxNow, timerId,
- rfxtypes = /^(?:toggle|show|hide)$/,
- rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
- rrun = /queueHooks$/,
- animationPrefilters = [ defaultPrefilter ],
- tweeners = {
- "*": [function( prop, value ) {
- var end, unit,
- tween = this.createTween( prop, value ),
- parts = rfxnum.exec( value ),
- target = tween.cur(),
- start = +target || 0,
- scale = 1,
- maxIterations = 20;
-
- if ( parts ) {
- end = +parts[2];
- unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
-
- // We need to compute starting value
- if ( unit !== "px" && start ) {
- // Iteratively approximate from a nonzero starting point
- // Prefer the current property, because this process will be trivial if it uses the same units
- // Fallback to end or a simple constant
- start = jQuery.css( tween.elem, prop, true ) || end || 1;
-
- do {
- // If previous iteration zeroed out, double until we get *something*
- // Use a string for doubling factor so we don't accidentally see scale as unchanged below
- scale = scale || ".5";
-
- // Adjust and apply
- start = start / scale;
- jQuery.style( tween.elem, prop, start + unit );
-
- // Update scale, tolerating zero or NaN from tween.cur()
- // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
- } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
- }
-
- tween.unit = unit;
- tween.start = start;
- // If a +=/-= token was provided, we're doing a relative animation
- tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
- }
- return tween;
- }]
- };
-
-// Animations created synchronously will run synchronously
-function createFxNow() {
- setTimeout(function() {
- fxNow = undefined;
- });
- return ( fxNow = jQuery.now() );
-}
-
-function createTweens( animation, props ) {
- jQuery.each( props, function( prop, value ) {
- var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
- index = 0,
- length = collection.length;
- for ( ; index < length; index++ ) {
- if ( collection[ index ].call( animation, prop, value ) ) {
-
- // we're done with this property
- return;
- }
- }
- });
-}
-
-function Animation( elem, properties, options ) {
- var result,
- stopped,
- index = 0,
- length = animationPrefilters.length,
- deferred = jQuery.Deferred().always( function() {
- // don't match elem in the :animated selector
- delete tick.elem;
- }),
- tick = function() {
- if ( stopped ) {
- return false;
- }
- var currentTime = fxNow || createFxNow(),
- remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
- // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
- temp = remaining / animation.duration || 0,
- percent = 1 - temp,
- index = 0,
- length = animation.tweens.length;
-
- for ( ; index < length ; index++ ) {
- animation.tweens[ index ].run( percent );
- }
-
- deferred.notifyWith( elem, [ animation, percent, remaining ]);
-
- if ( percent < 1 && length ) {
- return remaining;
- } else {
- deferred.resolveWith( elem, [ animation ] );
- return false;
- }
- },
- animation = deferred.promise({
- elem: elem,
- props: jQuery.extend( {}, properties ),
- opts: jQuery.extend( true, { specialEasing: {} }, options ),
- originalProperties: properties,
- originalOptions: options,
- startTime: fxNow || createFxNow(),
- duration: options.duration,
- tweens: [],
- createTween: function( prop, end ) {
- var tween = jQuery.Tween( elem, animation.opts, prop, end,
- animation.opts.specialEasing[ prop ] || animation.opts.easing );
- animation.tweens.push( tween );
- return tween;
- },
- stop: function( gotoEnd ) {
- var index = 0,
- // if we are going to the end, we want to run all the tweens
- // otherwise we skip this part
- length = gotoEnd ? animation.tweens.length : 0;
- if ( stopped ) {
- return this;
- }
- stopped = true;
- for ( ; index < length ; index++ ) {
- animation.tweens[ index ].run( 1 );
- }
-
- // resolve when we played the last frame
- // otherwise, reject
- if ( gotoEnd ) {
- deferred.resolveWith( elem, [ animation, gotoEnd ] );
- } else {
- deferred.rejectWith( elem, [ animation, gotoEnd ] );
- }
- return this;
- }
- }),
- props = animation.props;
-
- propFilter( props, animation.opts.specialEasing );
-
- for ( ; index < length ; index++ ) {
- result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
- if ( result ) {
- return result;
- }
- }
-
- createTweens( animation, props );
-
- if ( jQuery.isFunction( animation.opts.start ) ) {
- animation.opts.start.call( elem, animation );
- }
-
- jQuery.fx.timer(
- jQuery.extend( tick, {
- elem: elem,
- anim: animation,
- queue: animation.opts.queue
- })
- );
-
- // attach callbacks from options
- return animation.progress( animation.opts.progress )
- .done( animation.opts.done, animation.opts.complete )
- .fail( animation.opts.fail )
- .always( animation.opts.always );
-}
-
-function propFilter( props, specialEasing ) {
- var value, name, index, easing, hooks;
-
- // camelCase, specialEasing and expand cssHook pass
- for ( index in props ) {
- name = jQuery.camelCase( index );
- easing = specialEasing[ name ];
- value = props[ index ];
- if ( jQuery.isArray( value ) ) {
- easing = value[ 1 ];
- value = props[ index ] = value[ 0 ];
- }
-
- if ( index !== name ) {
- props[ name ] = value;
- delete props[ index ];
- }
-
- hooks = jQuery.cssHooks[ name ];
- if ( hooks && "expand" in hooks ) {
- value = hooks.expand( value );
- delete props[ name ];
-
- // not quite $.extend, this wont overwrite keys already present.
- // also - reusing 'index' from above because we have the correct "name"
- for ( index in value ) {
- if ( !( index in props ) ) {
- props[ index ] = value[ index ];
- specialEasing[ index ] = easing;
- }
- }
- } else {
- specialEasing[ name ] = easing;
- }
- }
-}
-
-jQuery.Animation = jQuery.extend( Animation, {
-
- tweener: function( props, callback ) {
- if ( jQuery.isFunction( props ) ) {
- callback = props;
- props = [ "*" ];
- } else {
- props = props.split(" ");
- }
-
- var prop,
- index = 0,
- length = props.length;
-
- for ( ; index < length ; index++ ) {
- prop = props[ index ];
- tweeners[ prop ] = tweeners[ prop ] || [];
- tweeners[ prop ].unshift( callback );
- }
- },
-
- prefilter: function( callback, prepend ) {
- if ( prepend ) {
- animationPrefilters.unshift( callback );
- } else {
- animationPrefilters.push( callback );
- }
- }
-});
-
-function defaultPrefilter( elem, props, opts ) {
- /*jshint validthis:true */
- var prop, index, length,
- value, dataShow, toggle,
- tween, hooks, oldfire,
- anim = this,
- style = elem.style,
- orig = {},
- handled = [],
- hidden = elem.nodeType && isHidden( elem );
-
- // handle queue: false promises
- if ( !opts.queue ) {
- hooks = jQuery._queueHooks( elem, "fx" );
- if ( hooks.unqueued == null ) {
- hooks.unqueued = 0;
- oldfire = hooks.empty.fire;
- hooks.empty.fire = function() {
- if ( !hooks.unqueued ) {
- oldfire();
- }
- };
- }
- hooks.unqueued++;
-
- anim.always(function() {
- // doing this makes sure that the complete handler will be called
- // before this completes
- anim.always(function() {
- hooks.unqueued--;
- if ( !jQuery.queue( elem, "fx" ).length ) {
- hooks.empty.fire();
- }
- });
- });
- }
-
- // height/width overflow pass
- if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
- // Make sure that nothing sneaks out
- // Record all 3 overflow attributes because IE does not
- // change the overflow attribute when overflowX and
- // overflowY are set to the same value
- opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
-
- // Set display property to inline-block for height/width
- // animations on inline elements that are having width/height animated
- if ( jQuery.css( elem, "display" ) === "inline" &&
- jQuery.css( elem, "float" ) === "none" ) {
-
- // inline-level elements accept inline-block;
- // block-level elements need to be inline with layout
- if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
- style.display = "inline-block";
-
- } else {
- style.zoom = 1;
- }
- }
- }
-
- if ( opts.overflow ) {
- style.overflow = "hidden";
- if ( !jQuery.support.shrinkWrapBlocks ) {
- anim.always(function() {
- style.overflow = opts.overflow[ 0 ];
- style.overflowX = opts.overflow[ 1 ];
- style.overflowY = opts.overflow[ 2 ];
- });
- }
- }
-
-
- // show/hide pass
- for ( index in props ) {
- value = props[ index ];
- if ( rfxtypes.exec( value ) ) {
- delete props[ index ];
- toggle = toggle || value === "toggle";
- if ( value === ( hidden ? "hide" : "show" ) ) {
- continue;
- }
- handled.push( index );
- }
- }
-
- length = handled.length;
- if ( length ) {
- dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
- if ( "hidden" in dataShow ) {
- hidden = dataShow.hidden;
- }
-
- // store state if its toggle - enables .stop().toggle() to "reverse"
- if ( toggle ) {
- dataShow.hidden = !hidden;
- }
- if ( hidden ) {
- jQuery( elem ).show();
- } else {
- anim.done(function() {
- jQuery( elem ).hide();
- });
- }
- anim.done(function() {
- var prop;
- jQuery._removeData( elem, "fxshow" );
- for ( prop in orig ) {
- jQuery.style( elem, prop, orig[ prop ] );
- }
- });
- for ( index = 0 ; index < length ; index++ ) {
- prop = handled[ index ];
- tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
- orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
-
- if ( !( prop in dataShow ) ) {
- dataShow[ prop ] = tween.start;
- if ( hidden ) {
- tween.end = tween.start;
- tween.start = prop === "width" || prop === "height" ? 1 : 0;
- }
- }
- }
- }
-}
-
-function Tween( elem, options, prop, end, easing ) {
- return new Tween.prototype.init( elem, options, prop, end, easing );
-}
-jQuery.Tween = Tween;
-
-Tween.prototype = {
- constructor: Tween,
- init: function( elem, options, prop, end, easing, unit ) {
- this.elem = elem;
- this.prop = prop;
- this.easing = easing || "swing";
- this.options = options;
- this.start = this.now = this.cur();
- this.end = end;
- this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
- },
- cur: function() {
- var hooks = Tween.propHooks[ this.prop ];
-
- return hooks && hooks.get ?
- hooks.get( this ) :
- Tween.propHooks._default.get( this );
- },
- run: function( percent ) {
- var eased,
- hooks = Tween.propHooks[ this.prop ];
-
- if ( this.options.duration ) {
- this.pos = eased = jQuery.easing[ this.easing ](
- percent, this.options.duration * percent, 0, 1, this.options.duration
- );
- } else {
- this.pos = eased = percent;
- }
- this.now = ( this.end - this.start ) * eased + this.start;
-
- if ( this.options.step ) {
- this.options.step.call( this.elem, this.now, this );
- }
-
- if ( hooks && hooks.set ) {
- hooks.set( this );
- } else {
- Tween.propHooks._default.set( this );
- }
- return this;
- }
-};
-
-Tween.prototype.init.prototype = Tween.prototype;
-
-Tween.propHooks = {
- _default: {
- get: function( tween ) {
- var result;
-
- if ( tween.elem[ tween.prop ] != null &&
- (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
- return tween.elem[ tween.prop ];
- }
-
- // passing an empty string as a 3rd parameter to .css will automatically
- // attempt a parseFloat and fallback to a string if the parse fails
- // so, simple values such as "10px" are parsed to Float.
- // complex values such as "rotate(1rad)" are returned as is.
- result = jQuery.css( tween.elem, tween.prop, "" );
- // Empty strings, null, undefined and "auto" are converted to 0.
- return !result || result === "auto" ? 0 : result;
- },
- set: function( tween ) {
- // use step hook for back compat - use cssHook if its there - use .style if its
- // available and use plain properties where available
- if ( jQuery.fx.step[ tween.prop ] ) {
- jQuery.fx.step[ tween.prop ]( tween );
- } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
- jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
- } else {
- tween.elem[ tween.prop ] = tween.now;
- }
- }
- }
-};
-
-// Remove in 2.0 - this supports IE8's panic based approach
-// to setting things on disconnected nodes
-
-Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
- set: function( tween ) {
- if ( tween.elem.nodeType && tween.elem.parentNode ) {
- tween.elem[ tween.prop ] = tween.now;
- }
- }
-};
-
-jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
- var cssFn = jQuery.fn[ name ];
- jQuery.fn[ name ] = function( speed, easing, callback ) {
- return speed == null || typeof speed === "boolean" ?
- cssFn.apply( this, arguments ) :
- this.animate( genFx( name, true ), speed, easing, callback );
- };
-});
-
-jQuery.fn.extend({
- fadeTo: function( speed, to, easing, callback ) {
-
- // show any hidden elements after setting opacity to 0
- return this.filter( isHidden ).css( "opacity", 0 ).show()
-
- // animate to the value specified
- .end().animate({ opacity: to }, speed, easing, callback );
- },
- animate: function( prop, speed, easing, callback ) {
- var empty = jQuery.isEmptyObject( prop ),
- optall = jQuery.speed( speed, easing, callback ),
- doAnimation = function() {
- // Operate on a copy of prop so per-property easing won't be lost
- var anim = Animation( this, jQuery.extend( {}, prop ), optall );
- doAnimation.finish = function() {
- anim.stop( true );
- };
- // Empty animations, or finishing resolves immediately
- if ( empty || jQuery._data( this, "finish" ) ) {
- anim.stop( true );
- }
- };
- doAnimation.finish = doAnimation;
-
- return empty || optall.queue === false ?
- this.each( doAnimation ) :
- this.queue( optall.queue, doAnimation );
- },
- stop: function( type, clearQueue, gotoEnd ) {
- var stopQueue = function( hooks ) {
- var stop = hooks.stop;
- delete hooks.stop;
- stop( gotoEnd );
- };
-
- if ( typeof type !== "string" ) {
- gotoEnd = clearQueue;
- clearQueue = type;
- type = undefined;
- }
- if ( clearQueue && type !== false ) {
- this.queue( type || "fx", [] );
- }
-
- return this.each(function() {
- var dequeue = true,
- index = type != null && type + "queueHooks",
- timers = jQuery.timers,
- data = jQuery._data( this );
-
- if ( index ) {
- if ( data[ index ] && data[ index ].stop ) {
- stopQueue( data[ index ] );
- }
- } else {
- for ( index in data ) {
- if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
- stopQueue( data[ index ] );
- }
- }
- }
-
- for ( index = timers.length; index--; ) {
- if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
- timers[ index ].anim.stop( gotoEnd );
- dequeue = false;
- timers.splice( index, 1 );
- }
- }
-
- // start the next in the queue if the last step wasn't forced
- // timers currently will call their complete callbacks, which will dequeue
- // but only if they were gotoEnd
- if ( dequeue || !gotoEnd ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- finish: function( type ) {
- if ( type !== false ) {
- type = type || "fx";
- }
- return this.each(function() {
- var index,
- data = jQuery._data( this ),
- queue = data[ type + "queue" ],
- hooks = data[ type + "queueHooks" ],
- timers = jQuery.timers,
- length = queue ? queue.length : 0;
-
- // enable finishing flag on private data
- data.finish = true;
-
- // empty the queue first
- jQuery.queue( this, type, [] );
-
- if ( hooks && hooks.cur && hooks.cur.finish ) {
- hooks.cur.finish.call( this );
- }
-
- // look for any active animations, and finish them
- for ( index = timers.length; index--; ) {
- if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
- timers[ index ].anim.stop( true );
- timers.splice( index, 1 );
- }
- }
-
- // look for any animations in the old queue and finish them
- for ( index = 0; index < length; index++ ) {
- if ( queue[ index ] && queue[ index ].finish ) {
- queue[ index ].finish.call( this );
- }
- }
-
- // turn off finishing flag
- delete data.finish;
- });
- }
-});
-
-// Generate parameters to create a standard animation
-function genFx( type, includeWidth ) {
- var which,
- attrs = { height: type },
- i = 0;
-
- // if we include width, step value is 1 to do all cssExpand values,
- // if we don't include width, step value is 2 to skip over Left and Right
- includeWidth = includeWidth? 1 : 0;
- for( ; i < 4 ; i += 2 - includeWidth ) {
- which = cssExpand[ i ];
- attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
- }
-
- if ( includeWidth ) {
- attrs.opacity = attrs.width = type;
- }
-
- return attrs;
-}
-
-// Generate shortcuts for custom animations
-jQuery.each({
- slideDown: genFx("show"),
- slideUp: genFx("hide"),
- slideToggle: genFx("toggle"),
- fadeIn: { opacity: "show" },
- fadeOut: { opacity: "hide" },
- fadeToggle: { opacity: "toggle" }
-}, function( name, props ) {
- jQuery.fn[ name ] = function( speed, easing, callback ) {
- return this.animate( props, speed, easing, callback );
- };
-});
-
-jQuery.speed = function( speed, easing, fn ) {
- var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
- complete: fn || !fn && easing ||
- jQuery.isFunction( speed ) && speed,
- duration: speed,
- easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
- };
-
- opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
- opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
-
- // normalize opt.queue - true/undefined/null -> "fx"
- if ( opt.queue == null || opt.queue === true ) {
- opt.queue = "fx";
- }
-
- // Queueing
- opt.old = opt.complete;
-
- opt.complete = function() {
- if ( jQuery.isFunction( opt.old ) ) {
- opt.old.call( this );
- }
-
- if ( opt.queue ) {
- jQuery.dequeue( this, opt.queue );
- }
- };
-
- return opt;
-};
-
-jQuery.easing = {
- linear: function( p ) {
- return p;
- },
- swing: function( p ) {
- return 0.5 - Math.cos( p*Math.PI ) / 2;
- }
-};
-
-jQuery.timers = [];
-jQuery.fx = Tween.prototype.init;
-jQuery.fx.tick = function() {
- var timer,
- timers = jQuery.timers,
- i = 0;
-
- fxNow = jQuery.now();
-
- for ( ; i < timers.length; i++ ) {
- timer = timers[ i ];
- // Checks the timer has not already been removed
- if ( !timer() && timers[ i ] === timer ) {
- timers.splice( i--, 1 );
- }
- }
-
- if ( !timers.length ) {
- jQuery.fx.stop();
- }
- fxNow = undefined;
-};
-
-jQuery.fx.timer = function( timer ) {
- if ( timer() && jQuery.timers.push( timer ) ) {
- jQuery.fx.start();
- }
-};
-
-jQuery.fx.interval = 13;
-
-jQuery.fx.start = function() {
- if ( !timerId ) {
- timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
- }
-};
-
-jQuery.fx.stop = function() {
- clearInterval( timerId );
- timerId = null;
-};
-
-jQuery.fx.speeds = {
- slow: 600,
- fast: 200,
- // Default speed
- _default: 400
-};
-
-// Back Compat <1.8 extension point
-jQuery.fx.step = {};
-
-if ( jQuery.expr && jQuery.expr.filters ) {
- jQuery.expr.filters.animated = function( elem ) {
- return jQuery.grep(jQuery.timers, function( fn ) {
- return elem === fn.elem;
- }).length;
- };
-}
-jQuery.fn.offset = function( options ) {
- if ( arguments.length ) {
- return options === undefined ?
- this :
- this.each(function( i ) {
- jQuery.offset.setOffset( this, options, i );
- });
- }
-
- var docElem, win,
- box = { top: 0, left: 0 },
- elem = this[ 0 ],
- doc = elem && elem.ownerDocument;
-
- if ( !doc ) {
- return;
- }
-
- docElem = doc.documentElement;
-
- // Make sure it's not a disconnected DOM node
- if ( !jQuery.contains( docElem, elem ) ) {
- return box;
- }
-
- // If we don't have gBCR, just use 0,0 rather than error
- // BlackBerry 5, iOS 3 (original iPhone)
- if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
- box = elem.getBoundingClientRect();
- }
- win = getWindow( doc );
- return {
- top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
- left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
- };
-};
-
-jQuery.offset = {
-
- setOffset: function( elem, options, i ) {
- var position = jQuery.css( elem, "position" );
-
- // set position first, in-case top/left are set even on static elem
- if ( position === "static" ) {
- elem.style.position = "relative";
- }
-
- var curElem = jQuery( elem ),
- curOffset = curElem.offset(),
- curCSSTop = jQuery.css( elem, "top" ),
- curCSSLeft = jQuery.css( elem, "left" ),
- calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
- props = {}, curPosition = {}, curTop, curLeft;
-
- // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
- if ( calculatePosition ) {
- curPosition = curElem.position();
- curTop = curPosition.top;
- curLeft = curPosition.left;
- } else {
- curTop = parseFloat( curCSSTop ) || 0;
- curLeft = parseFloat( curCSSLeft ) || 0;
- }
-
- if ( jQuery.isFunction( options ) ) {
- options = options.call( elem, i, curOffset );
- }
-
- if ( options.top != null ) {
- props.top = ( options.top - curOffset.top ) + curTop;
- }
- if ( options.left != null ) {
- props.left = ( options.left - curOffset.left ) + curLeft;
- }
-
- if ( "using" in options ) {
- options.using.call( elem, props );
- } else {
- curElem.css( props );
- }
- }
-};
-
-
-jQuery.fn.extend({
-
- position: function() {
- if ( !this[ 0 ] ) {
- return;
- }
-
- var offsetParent, offset,
- parentOffset = { top: 0, left: 0 },
- elem = this[ 0 ];
-
- // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
- if ( jQuery.css( elem, "position" ) === "fixed" ) {
- // we assume that getBoundingClientRect is available when computed position is fixed
- offset = elem.getBoundingClientRect();
- } else {
- // Get *real* offsetParent
- offsetParent = this.offsetParent();
-
- // Get correct offsets
- offset = this.offset();
- if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
- parentOffset = offsetParent.offset();
- }
-
- // Add offsetParent borders
- parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
- parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
- }
-
- // Subtract parent offsets and element margins
- // note: when an element has margin: auto the offsetLeft and marginLeft
- // are the same in Safari causing offset.left to incorrectly be 0
- return {
- top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
- left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
- };
- },
-
- offsetParent: function() {
- return this.map(function() {
- var offsetParent = this.offsetParent || document.documentElement;
- while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
- offsetParent = offsetParent.offsetParent;
- }
- return offsetParent || document.documentElement;
- });
- }
-});
-
-
-// Create scrollLeft and scrollTop methods
-jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
- var top = /Y/.test( prop );
-
- jQuery.fn[ method ] = function( val ) {
- return jQuery.access( this, function( elem, method, val ) {
- var win = getWindow( elem );
-
- if ( val === undefined ) {
- return win ? (prop in win) ? win[ prop ] :
- win.document.documentElement[ method ] :
- elem[ method ];
- }
-
- if ( win ) {
- win.scrollTo(
- !top ? val : jQuery( win ).scrollLeft(),
- top ? val : jQuery( win ).scrollTop()
- );
-
- } else {
- elem[ method ] = val;
- }
- }, method, val, arguments.length, null );
- };
-});
-
-function getWindow( elem ) {
- return jQuery.isWindow( elem ) ?
- elem :
- elem.nodeType === 9 ?
- elem.defaultView || elem.parentWindow :
- false;
-}
-// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
-jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
- jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
- // margin is only for outerHeight, outerWidth
- jQuery.fn[ funcName ] = function( margin, value ) {
- var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
- extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
-
- return jQuery.access( this, function( elem, type, value ) {
- var doc;
-
- if ( jQuery.isWindow( elem ) ) {
- // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
- // isn't a whole lot we can do. See pull request at this URL for discussion:
- // https://github.com/jquery/jquery/pull/764
- return elem.document.documentElement[ "client" + name ];
- }
-
- // Get document width or height
- if ( elem.nodeType === 9 ) {
- doc = elem.documentElement;
-
- // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
- // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
- return Math.max(
- elem.body[ "scroll" + name ], doc[ "scroll" + name ],
- elem.body[ "offset" + name ], doc[ "offset" + name ],
- doc[ "client" + name ]
- );
- }
-
- return value === undefined ?
- // Get width or height on the element, requesting but not forcing parseFloat
- jQuery.css( elem, type, extra ) :
-
- // Set width or height on the element
- jQuery.style( elem, type, value, extra );
- }, type, chainable ? margin : undefined, chainable, null );
- };
- });
-});
-// Limit scope pollution from any deprecated API
-// (function() {
-
-// })();
-// Expose jQuery to the global object
-window.jQuery = window.$ = jQuery;
-
-// Expose jQuery as an AMD module, but only for AMD loaders that
-// understand the issues with loading multiple versions of jQuery
-// in a page that all might call define(). The loader will indicate
-// they have special allowances for multiple jQuery versions by
-// specifying define.amd.jQuery = true. Register as a named module,
-// since jQuery can be concatenated with other files that may use define,
-// but not use a proper concatenation script that understands anonymous
-// AMD modules. A named AMD is safest and most robust way to register.
-// Lowercase jquery is used because AMD module names are derived from
-// file names, and jQuery is normally delivered in a lowercase file name.
-// Do this after creating the global so that if an AMD module wants to call
-// noConflict to hide this version of jQuery, it will work.
-if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
- define( "jquery", [], function () { return jQuery; } );
-}
-
-})( window );
diff --git a/go/base/static/js/vendor/jquery.cookie-1.3.1.js b/go/base/static/js/vendor/jquery.cookie-1.3.1.js
deleted file mode 100644
index f8f852c..0000000
--- a/go/base/static/js/vendor/jquery.cookie-1.3.1.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/*!
- * jQuery Cookie Plugin v1.3.1
- * https://github.com/carhartl/jquery-cookie
- *
- * Copyright 2013 Klaus Hartl
- * Released under the MIT license
- */
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals.
- factory(jQuery);
- }
-}(function ($) {
-
- var pluses = /\+/g;
-
- function decode(s) {
- if (config.raw) {
- return s;
- }
- return decodeURIComponent(s.replace(pluses, ' '));
- }
-
- function decodeAndParse(s) {
- if (s.indexOf('"') === 0) {
- // This is a quoted cookie as according to RFC2068, unescape...
- s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
- }
-
- s = decode(s);
-
- try {
- return config.json ? JSON.parse(s) : s;
- } catch(e) {}
- }
-
- var config = $.cookie = function (key, value, options) {
-
- // Write
- if (value !== undefined) {
- options = $.extend({}, config.defaults, options);
-
- if (typeof options.expires === 'number') {
- var days = options.expires, t = options.expires = new Date();
- t.setDate(t.getDate() + days);
- }
-
- value = config.json ? JSON.stringify(value) : String(value);
-
- return (document.cookie = [
- config.raw ? key : encodeURIComponent(key),
- '=',
- config.raw ? value : encodeURIComponent(value),
- options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
- options.path ? '; path=' + options.path : '',
- options.domain ? '; domain=' + options.domain : '',
- options.secure ? '; secure' : ''
- ].join(''));
- }
-
- // Read
- var cookies = document.cookie.split('; ');
- var result = key ? undefined : {};
- for (var i = 0, l = cookies.length; i < l; i++) {
- var parts = cookies[i].split('=');
- var name = decode(parts.shift());
- var cookie = parts.join('=');
-
- if (key && key === name) {
- result = decodeAndParse(cookie);
- break;
- }
-
- if (!key) {
- result[name] = decodeAndParse(cookie);
- }
- }
-
- return result;
- };
-
- config.defaults = {};
-
- $.removeCookie = function (key, options) {
- if ($.cookie(key) !== undefined) {
- // Must not alter options, thus extending a fresh object...
- $.cookie(key, '', $.extend({}, options, { expires: -1 }));
- return true;
- }
- return false;
- };
-
-}));
diff --git a/go/base/static/js/vendor/jquery.jsPlumb-1.5.2.praekelt.js b/go/base/static/js/vendor/jquery.jsPlumb-1.5.2.praekelt.js
deleted file mode 100644
index dff6e30..0000000
--- a/go/base/static/js/vendor/jquery.jsPlumb-1.5.2.praekelt.js
+++ /dev/null
@@ -1,10986 +0,0 @@
-/**
-* jsBezier-0.6
-*
-* Copyright (c) 2010 - 2013 Simon Porritt ([email protected])
-*
-* licensed under the MIT license.
-*
-* a set of Bezier curve functions that deal with Beziers, used by jsPlumb, and perhaps useful for other people. These functions work with Bezier
-* curves of arbitrary degree.
-*
-* - functions are all in the 'jsBezier' namespace.
-*
-* - all input points should be in the format {x:.., y:..}. all output points are in this format too.
-*
-* - all input curves should be in the format [ {x:.., y:..}, {x:.., y:..}, {x:.., y:..}, {x:.., y:..} ]
-*
-* - 'location' as used as an input here refers to a decimal in the range 0-1 inclusive, which indicates a point some proportion along the length
-* of the curve. location as output has the same format and meaning.
-*
-*
-* Function List:
-* --------------
-*
-* distanceFromCurve(point, curve)
-*
-* Calculates the distance that the given point lies from the given Bezier. Note that it is computed relative to the center of the Bezier,
-* so if you have stroked the curve with a wide pen you may wish to take that into account! The distance returned is relative to the values
-* of the curve and the point - it will most likely be pixels.
-*
-* gradientAtPoint(curve, location)
-*
-* Calculates the gradient to the curve at the given location, as a decimal between 0 and 1 inclusive.
-*
-* gradientAtPointAlongCurveFrom (curve, location)
-*
-* Calculates the gradient at the point on the given curve that is 'distance' units from location.
-*
-* nearestPointOnCurve(point, curve)
-*
-* Calculates the nearest point to the given point on the given curve. The return value of this is a JS object literal, containing both the
-*point's coordinates and also the 'location' of the point (see above), for example: { point:{x:551,y:150}, location:0.263365 }.
-*
-* pointOnCurve(curve, location)
-*
-* Calculates the coordinates of the point on the given Bezier curve at the given location.
-*
-* pointAlongCurveFrom(curve, location, distance)
-*
-* Calculates the coordinates of the point on the given curve that is 'distance' units from location. 'distance' should be in the same coordinate
-* space as that used to construct the Bezier curve. For an HTML Canvas usage, for example, distance would be a measure of pixels.
-*
-* locationAlongCurveFrom(curve, location, distance)
-*
-* Calculates the location on the given curve that is 'distance' units from location. 'distance' should be in the same coordinate
-* space as that used to construct the Bezier curve. For an HTML Canvas usage, for example, distance would be a measure of pixels.
-*
-* perpendicularToCurveAt(curve, location, length, distance)
-*
-* Calculates the perpendicular to the given curve at the given location. length is the length of the line you wish for (it will be centered
-* on the point at 'location'). distance is optional, and allows you to specify a point along the path from the given location as the center of
-* the perpendicular returned. The return value of this is an array of two points: [ {x:...,y:...}, {x:...,y:...} ].
-*
-*
-*/
-
-(function() {
-
- if(typeof Math.sgn == "undefined") {
- Math.sgn = function(x) { return x == 0 ? 0 : x > 0 ? 1 :-1; };
- }
-
- var Vectors = {
- subtract : function(v1, v2) { return {x:v1.x - v2.x, y:v1.y - v2.y }; },
- dotProduct : function(v1, v2) { return (v1.x * v2.x) + (v1.y * v2.y); },
- square : function(v) { return Math.sqrt((v.x * v.x) + (v.y * v.y)); },
- scale : function(v, s) { return {x:v.x * s, y:v.y * s }; }
- },
-
- maxRecursion = 64,
- flatnessTolerance = Math.pow(2.0,-maxRecursion-1);
-
- /**
- * Calculates the distance that the point lies from the curve.
- *
- * @param point a point in the form {x:567, y:3342}
- * @param curve a Bezier curve in the form [{x:..., y:...}, {x:..., y:...}, {x:..., y:...}, {x:..., y:...}]. note that this is currently
- * hardcoded to assume cubiz beziers, but would be better off supporting any degree.
- * @return a JS object literal containing location and distance, for example: {location:0.35, distance:10}. Location is analogous to the location
- * argument you pass to the pointOnPath function: it is a ratio of distance travelled along the curve. Distance is the distance in pixels from
- * the point to the curve.
- */
- var _distanceFromCurve = function(point, curve) {
- var candidates = [],
- w = _convertToBezier(point, curve),
- degree = curve.length - 1, higherDegree = (2 * degree) - 1,
- numSolutions = _findRoots(w, higherDegree, candidates, 0),
- v = Vectors.subtract(point, curve[0]), dist = Vectors.square(v), t = 0.0;
-
- for (var i = 0; i < numSolutions; i++) {
- v = Vectors.subtract(point, _bezier(curve, degree, candidates[i], null, null));
- var newDist = Vectors.square(v);
- if (newDist < dist) {
- dist = newDist;
- t = candidates[i];
- }
- }
- v = Vectors.subtract(point, curve[degree]);
- newDist = Vectors.square(v);
- if (newDist < dist) {
- dist = newDist;
- t = 1.0;
- }
- return {location:t, distance:dist};
- };
- /**
- * finds the nearest point on the curve to the given point.
- */
- var _nearestPointOnCurve = function(point, curve) {
- var td = _distanceFromCurve(point, curve);
- return {point:_bezier(curve, curve.length - 1, td.location, null, null), location:td.location};
- };
- var _convertToBezier = function(point, curve) {
- var degree = curve.length - 1, higherDegree = (2 * degree) - 1,
- c = [], d = [], cdTable = [], w = [],
- z = [ [1.0, 0.6, 0.3, 0.1], [0.4, 0.6, 0.6, 0.4], [0.1, 0.3, 0.6, 1.0] ];
-
- for (var i = 0; i <= degree; i++) c[i] = Vectors.subtract(curve[i], point);
- for (var i = 0; i <= degree - 1; i++) {
- d[i] = Vectors.subtract(curve[i+1], curve[i]);
- d[i] = Vectors.scale(d[i], 3.0);
- }
- for (var row = 0; row <= degree - 1; row++) {
- for (var column = 0; column <= degree; column++) {
- if (!cdTable[row]) cdTable[row] = [];
- cdTable[row][column] = Vectors.dotProduct(d[row], c[column]);
- }
- }
- for (i = 0; i <= higherDegree; i++) {
- if (!w[i]) w[i] = [];
- w[i].y = 0.0;
- w[i].x = parseFloat(i) / higherDegree;
- }
- var n = degree, m = degree-1;
- for (var k = 0; k <= n + m; k++) {
- var lb = Math.max(0, k - m),
- ub = Math.min(k, n);
- for (i = lb; i <= ub; i++) {
- j = k - i;
- w[i+j].y += cdTable[j][i] * z[j][i];
- }
- }
- return w;
- };
- /**
- * counts how many roots there are.
- */
- var _findRoots = function(w, degree, t, depth) {
- var left = [], right = [],
- left_count, right_count,
- left_t = [], right_t = [];
-
- switch (_getCrossingCount(w, degree)) {
- case 0 : {
- return 0;
- }
- case 1 : {
- if (depth >= maxRecursion) {
- t[0] = (w[0].x + w[degree].x) / 2.0;
- return 1;
- }
- if (_isFlatEnough(w, degree)) {
- t[0] = _computeXIntercept(w, degree);
- return 1;
- }
- break;
- }
- }
- _bezier(w, degree, 0.5, left, right);
- left_count = _findRoots(left, degree, left_t, depth+1);
- right_count = _findRoots(right, degree, right_t, depth+1);
- for (var i = 0; i < left_count; i++) t[i] = left_t[i];
- for (var i = 0; i < right_count; i++) t[i+left_count] = right_t[i];
- return (left_count+right_count);
- };
- var _getCrossingCount = function(curve, degree) {
- var n_crossings = 0, sign, old_sign;
- sign = old_sign = Math.sgn(curve[0].y);
- for (var i = 1; i <= degree; i++) {
- sign = Math.sgn(curve[i].y);
- if (sign != old_sign) n_crossings++;
- old_sign = sign;
- }
- return n_crossings;
- };
- var _isFlatEnough = function(curve, degree) {
- var error,
- intercept_1, intercept_2, left_intercept, right_intercept,
- a, b, c, det, dInv, a1, b1, c1, a2, b2, c2;
- a = curve[0].y - curve[degree].y;
- b = curve[degree].x - curve[0].x;
- c = curve[0].x * curve[degree].y - curve[degree].x * curve[0].y;
-
- var max_distance_above = max_distance_below = 0.0;
-
- for (var i = 1; i < degree; i++) {
- var value = a * curve[i].x + b * curve[i].y + c;
- if (value > max_distance_above)
- max_distance_above = value;
- else if (value < max_distance_below)
- max_distance_below = value;
- }
-
- a1 = 0.0; b1 = 1.0; c1 = 0.0; a2 = a; b2 = b;
- c2 = c - max_distance_above;
- det = a1 * b2 - a2 * b1;
- dInv = 1.0/det;
- intercept_1 = (b1 * c2 - b2 * c1) * dInv;
- a2 = a; b2 = b; c2 = c - max_distance_below;
- det = a1 * b2 - a2 * b1;
- dInv = 1.0/det;
- intercept_2 = (b1 * c2 - b2 * c1) * dInv;
- left_intercept = Math.min(intercept_1, intercept_2);
- right_intercept = Math.max(intercept_1, intercept_2);
- error = right_intercept - left_intercept;
- return (error < flatnessTolerance)? 1 : 0;
- };
- var _computeXIntercept = function(curve, degree) {
- var XLK = 1.0, YLK = 0.0,
- XNM = curve[degree].x - curve[0].x, YNM = curve[degree].y - curve[0].y,
- XMK = curve[0].x - 0.0, YMK = curve[0].y - 0.0,
- det = XNM*YLK - YNM*XLK, detInv = 1.0/det,
- S = (XNM*YMK - YNM*XMK) * detInv;
- return 0.0 + XLK * S;
- };
- var _bezier = function(curve, degree, t, left, right) {
- var temp = [[]];
- for (var j =0; j <= degree; j++) temp[0][j] = curve[j];
- for (var i = 1; i <= degree; i++) {
- for (var j =0 ; j <= degree - i; j++) {
- if (!temp[i]) temp[i] = [];
- if (!temp[i][j]) temp[i][j] = {};
- temp[i][j].x = (1.0 - t) * temp[i-1][j].x + t * temp[i-1][j+1].x;
- temp[i][j].y = (1.0 - t) * temp[i-1][j].y + t * temp[i-1][j+1].y;
- }
- }
- if (left != null)
- for (j = 0; j <= degree; j++) left[j] = temp[j][0];
- if (right != null)
- for (j = 0; j <= degree; j++) right[j] = temp[degree-j][j];
-
- return (temp[degree][0]);
- };
-
- var _curveFunctionCache = {};
- var _getCurveFunctions = function(order) {
- var fns = _curveFunctionCache[order];
- if (!fns) {
- fns = [];
- var f_term = function() { return function(t) { return Math.pow(t, order); }; },
- l_term = function() { return function(t) { return Math.pow((1-t), order); }; },
- c_term = function(c) { return function(t) { return c; }; },
- t_term = function() { return function(t) { return t; }; },
- one_minus_t_term = function() { return function(t) { return 1-t; }; },
- _termFunc = function(terms) {
- return function(t) {
- var p = 1;
- for (var i = 0; i < terms.length; i++) p = p * terms[i](t);
- return p;
- };
- };
-
- fns.push(new f_term()); // first is t to the power of the curve order
- for (var i = 1; i < order; i++) {
- var terms = [new c_term(order)];
- for (var j = 0 ; j < (order - i); j++) terms.push(new t_term());
- for (var j = 0 ; j < i; j++) terms.push(new one_minus_t_term());
- fns.push(new _termFunc(terms));
- }
- fns.push(new l_term()); // last is (1-t) to the power of the curve order
-
- _curveFunctionCache[order] = fns;
- }
-
- return fns;
- };
-
-
- /**
- * calculates a point on the curve, for a Bezier of arbitrary order.
- * @param curve an array of control points, eg [{x:10,y:20}, {x:50,y:50}, {x:100,y:100}, {x:120,y:100}]. For a cubic bezier this should have four points.
- * @param location a decimal indicating the distance along the curve the point should be located at. this is the distance along the curve as it travels, taking the way it bends into account. should be a number from 0 to 1, inclusive.
- */
- var _pointOnPath = function(curve, location) {
- var cc = _getCurveFunctions(curve.length - 1),
- _x = 0, _y = 0;
- for (var i = 0; i < curve.length ; i++) {
- _x = _x + (curve[i].x * cc[i](location));
- _y = _y + (curve[i].y * cc[i](location));
- }
-
- return {x:_x, y:_y};
- };
-
- var _dist = function(p1,p2) {
- return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
- };
-
- var _isPoint = function(curve) {
- return curve[0].x == curve[1].x && curve[0].y == curve[1].y;
- };
-
- /**
- * finds the point that is 'distance' along the path from 'location'. this method returns both the x,y location of the point and also
- * its 'location' (proportion of travel along the path); the method below - _pointAlongPathFrom - calls this method and just returns the
- * point.
- */
- var _pointAlongPath = function(curve, location, distance) {
-
- if (_isPoint(curve)) {
- return {
- point:curve[0],
- location:location
- };
- }
-
- var prev = _pointOnPath(curve, location),
- tally = 0,
- curLoc = location,
- direction = distance > 0 ? 1 : -1,
- cur = null;
-
- while (tally < Math.abs(distance)) {
- curLoc += (0.005 * direction);
- cur = _pointOnPath(curve, curLoc);
- tally += _dist(cur, prev);
- prev = cur;
- }
- return {point:cur, location:curLoc};
- };
-
- var _length = function(curve) {
- if (_isPoint(curve)) return 0;
-
- var prev = _pointOnPath(curve, 0),
- tally = 0,
- curLoc = 0,
- direction = 1,
- cur = null;
-
- while (curLoc < 1) {
- curLoc += (0.005 * direction);
- cur = _pointOnPath(curve, curLoc);
- tally += _dist(cur, prev);
- prev = cur;
- }
- return tally;
- };
-
- /**
- * finds the point that is 'distance' along the path from 'location'.
- */
- var _pointAlongPathFrom = function(curve, location, distance) {
- return _pointAlongPath(curve, location, distance).point;
- };
-
- /**
- * finds the location that is 'distance' along the path from 'location'.
- */
- var _locationAlongPathFrom = function(curve, location, distance) {
- return _pointAlongPath(curve, location, distance).location;
- };
-
- /**
- * returns the gradient of the curve at the given location, which is a decimal between 0 and 1 inclusive.
- *
- * thanks // http://bimixual.org/AnimationLibrary/beziertangents.html
- */
- var _gradientAtPoint = function(curve, location) {
- var p1 = _pointOnPath(curve, location),
- p2 = _pointOnPath(curve.slice(0, curve.length - 1), location),
- dy = p2.y - p1.y, dx = p2.x - p1.x;
- return dy == 0 ? Infinity : Math.atan(dy / dx);
- };
-
- /**
- returns the gradient of the curve at the point which is 'distance' from the given location.
- if this point is greater than location 1, the gradient at location 1 is returned.
- if this point is less than location 0, the gradient at location 0 is returned.
- */
- var _gradientAtPointAlongPathFrom = function(curve, location, distance) {
- var p = _pointAlongPath(curve, location, distance);
- if (p.location > 1) p.location = 1;
- if (p.location < 0) p.location = 0;
- return _gradientAtPoint(curve, p.location);
- };
-
- /**
- * calculates a line that is 'length' pixels long, perpendicular to, and centered on, the path at 'distance' pixels from the given location.
- * if distance is not supplied, the perpendicular for the given location is computed (ie. we set distance to zero).
- */
- var _perpendicularToPathAt = function(curve, location, length, distance) {
- distance = distance == null ? 0 : distance;
- var p = _pointAlongPath(curve, location, distance),
- m = _gradientAtPoint(curve, p.location),
- _theta2 = Math.atan(-1 / m),
- y = length / 2 * Math.sin(_theta2),
- x = length / 2 * Math.cos(_theta2);
- return [{x:p.point.x + x, y:p.point.y + y}, {x:p.point.x - x, y:p.point.y - y}];
- };
-
- var jsBezier = window.jsBezier = {
- distanceFromCurve : _distanceFromCurve,
- gradientAtPoint : _gradientAtPoint,
- gradientAtPointAlongCurveFrom : _gradientAtPointAlongPathFrom,
- nearestPointOnCurve : _nearestPointOnCurve,
- pointOnCurve : _pointOnPath,
- pointAlongCurveFrom : _pointAlongPathFrom,
- perpendicularToCurveAt : _perpendicularToPathAt,
- locationAlongCurveFrom:_locationAlongPathFrom,
- getLength:_length
- };
-})();
-
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG or VML.
- *
- * This file contains the util functions
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-;(function() {
-
- var _isa = function(a) { return Object.prototype.toString.call(a) === "[object Array]"; },
- _isnum = function(n) { return Object.prototype.toString.call(n) === "[object Number]"; },
- _iss = function(s) { return typeof s === "string"; },
- _isb = function(s) { return typeof s === "boolean"; },
- _isnull = function(s) { return s == null; },
- _iso = function(o) { return o == null ? false : Object.prototype.toString.call(o) === "[object Object]"; },
- _isd = function(o) { return Object.prototype.toString.call(o) === "[object Date]"; },
- _isf = function(o) { return Object.prototype.toString.call(o) === "[object Function]"; },
- _ise = function(o) {
- for (var i in o) { if (o.hasOwnProperty(i)) return false; }
- return true;
- },
- pointHelper = function(p1, p2, fn) {
- p1 = _isa(p1) ? p1 : [p1.x, p1.y];
- p2 = _isa(p2) ? p2 : [p2.x, p2.y];
- return fn(p1, p2);
- };
-
- jsPlumbUtil = {
- isArray : _isa,
- isString : _iss,
- isBoolean: _isb,
- isNull : _isnull,
- isObject : _iso,
- isDate : _isd,
- isFunction: _isf,
- isEmpty:_ise,
- isNumber:_isnum,
- clone : function(a) {
- if (_iss(a)) return "" + a;
- else if (_isb(a)) return !!a;
- else if (_isd(a)) return new Date(a.getTime());
- else if (_isf(a)) return a;
- else if (_isa(a)) {
- var b = [];
- for (var i = 0; i < a.length; i++)
- b.push(this.clone(a[i]));
- return b;
- }
- else if (_iso(a)) {
- var c = {};
- for (var j in a)
- c[j] = this.clone(a[j]);
- return c;
- }
- else return a;
- },
- merge : function(a, b) {
- var c = this.clone(a);
- for (var i in b) {
- if (c[i] == null || _iss(b[i]) || _isb(b[i]))
- c[i] = b[i];
- else {
- if (_isa(b[i])/* && this.isArray(c[i])*/) {
- var ar = [];
- // if c's object is also an array we can keep its values.
- if (_isa(c[i])) ar.push.apply(ar, c[i]);
- ar.push.apply(ar, b[i]);
- c[i] = ar;
- }
- else if(_iso(b[i])) {
- // overwite c's value with an object if it is not already one.
- if (!_iso(c[i]))
- c[i] = {};
- for (var j in b[i])
- c[i][j] = b[i][j];
- }
- }
- }
- return c;
- },
- copyValues:function(names, from, to) {
- for (var i = 0; i < names.length; i++)
- to[names[i]] = from[names[i]];
- },
- //
- // chain a list of functions, supplied by [ object, method name, args ], and return on the first
- // one that returns the failValue. if none return the failValue, return the successValue.
- //
- functionChain : function(successValue, failValue, fns) {
- for (var i = 0; i < fns.length; i++) {
- var o = fns[i][0][fns[i][1]].apply(fns[i][0], fns[i][2]);
- if (o === failValue) {
- return o;
- }
- }
- return successValue;
- },
- // take the given model and expand out any parameters.
- populate : function(model, values) {
- // for a string, see if it has parameter matches, and if so, try to make the substitutions.
- var getValue = function(fromString) {
- var matches = fromString.match(/(\${.*?})/g);
- if (matches != null) {
- for (var i = 0; i < matches.length; i++) {
- var val = values[matches[i].substring(2, matches[i].length - 1)];
- if (val != null) {
- fromString = fromString.replace(matches[i], val);
- }
- }
- }
- return fromString;
- },
- // process one entry.
- _one = function(d) {
- if (d != null) {
- if (_iss(d)) {
- return getValue(d);
- }
- else if (_isa(d)) {
- var r = [];
- for (var i = 0; i < d.length; i++)
- r.push(_one(d[i]));
- return r;
- }
- else if (_iso(d)) {
- var s = {};
- for (var j in d) {
- s[j] = _one(d[j]);
- }
- return s;
- }
- else {
- return d;
- }
- }
- };
-
- return _one(model);
- },
- convertStyle : function(s, ignoreAlpha) {
- // TODO: jsPlumb should support a separate 'opacity' style member.
- if ("transparent" === s) return s;
- var o = s,
- pad = function(n) { return n.length == 1 ? "0" + n : n; },
- hex = function(k) { return pad(Number(k).toString(16)); },
- pattern = /(rgb[a]?\()(.*)(\))/;
- if (s.match(pattern)) {
- var parts = s.match(pattern)[2].split(",");
- o = "#" + hex(parts[0]) + hex(parts[1]) + hex(parts[2]);
- if (!ignoreAlpha && parts.length == 4)
- o = o + hex(parts[3]);
- }
- return o;
- },
- gradient : function(p1, p2) {
- return pointHelper(p1, p2, function(_p1, _p2) {
- if (_p2[0] == _p1[0])
- return _p2[1] > _p1[1] ? Infinity : -Infinity;
- else if (_p2[1] == _p1[1])
- return _p2[0] > _p1[0] ? 0 : -0;
- else
- return (_p2[1] - _p1[1]) / (_p2[0] - _p1[0]);
- });
- },
- normal : function(p1, p2) {
- return -1 / this.gradient(p1, p2);
- },
- lineLength : function(p1, p2) {
- return pointHelper(p1, p2, function(_p1, _p2) {
- return Math.sqrt(Math.pow(_p2[1] - _p1[1], 2) + Math.pow(_p2[0] - _p1[0], 2));
- });
- },
- segment : function(p1, p2) {
- return pointHelper(p1, p2, function(_p1, _p2) {
- if (_p2[0] > _p1[0]) {
- return (_p2[1] > _p1[1]) ? 2 : 1;
- }
- else if (_p2[0] == _p1[0]) {
- return _p2[1] > _p1[1] ? 2 : 1;
- }
- else {
- return (_p2[1] > _p1[1]) ? 3 : 4;
- }
- });
- },
- theta : function(p1, p2) {
- return pointHelper(p1, p2, function(_p1, _p2) {
- var m = jsPlumbUtil.gradient(_p1, _p2),
- t = Math.atan(m),
- s = jsPlumbUtil.segment(_p1, _p2);
- if ((s == 4 || s== 3)) t += Math.PI;
- if (t < 0) t += (2 * Math.PI);
-
- return t;
- });
- },
- intersects : function(r1, r2) {
- var x1 = r1.x, x2 = r1.x + r1.w, y1 = r1.y, y2 = r1.y + r1.h,
- a1 = r2.x, a2 = r2.x + r2.w, b1 = r2.y, b2 = r2.y + r2.h;
-
- return ( (x1 <= a1 && a1 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
- ( (x1 <= a2 && a2 <= x2) && (y1 <= b1 && b1 <= y2) ) ||
- ( (x1 <= a1 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
- ( (x1 <= a2 && a1 <= x2) && (y1 <= b2 && b2 <= y2) ) ||
- ( (a1 <= x1 && x1 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
- ( (a1 <= x2 && x2 <= a2) && (b1 <= y1 && y1 <= b2) ) ||
- ( (a1 <= x1 && x1 <= a2) && (b1 <= y2 && y2 <= b2) ) ||
- ( (a1 <= x2 && x1 <= a2) && (b1 <= y2 && y2 <= b2) );
- },
- segmentMultipliers : [null, [1, -1], [1, 1], [-1, 1], [-1, -1] ],
- inverseSegmentMultipliers : [null, [-1, -1], [-1, 1], [1, 1], [1, -1] ],
- pointOnLine : function(fromPoint, toPoint, distance) {
- var m = jsPlumbUtil.gradient(fromPoint, toPoint),
- s = jsPlumbUtil.segment(fromPoint, toPoint),
- segmentMultiplier = distance > 0 ? jsPlumbUtil.segmentMultipliers[s] : jsPlumbUtil.inverseSegmentMultipliers[s],
- theta = Math.atan(m),
- y = Math.abs(distance * Math.sin(theta)) * segmentMultiplier[1],
- x = Math.abs(distance * Math.cos(theta)) * segmentMultiplier[0];
- return { x:fromPoint.x + x, y:fromPoint.y + y };
- },
- perpendicularLineTo : function(fromPoint, toPoint, length) {
- var m = jsPlumbUtil.gradient(fromPoint, toPoint),
- theta2 = Math.atan(-1 / m),
- y = length / 2 * Math.sin(theta2),
- x = length / 2 * Math.cos(theta2);
- return [{x:toPoint.x + x, y:toPoint.y + y}, {x:toPoint.x - x, y:toPoint.y - y}];
- },
- findWithFunction : function(a, f) {
- if (a)
- for (var i = 0; i < a.length; i++) if (f(a[i])) return i;
- return -1;
- },
- clampToGrid : function(x, y, grid, dontClampX, dontClampY) {
- var _gridClamp = function(n, g) {
- var e = n % g,
- f = Math.floor(n / g),
- inc = e >= (g / 2) ? 1 : 0;
- return (f + inc) * g;
- };
- return [
- dontClampX || grid == null ? x : _gridClamp(x, grid[0]),
- dontClampY || grid == null ? y : _gridClamp(y, grid[1])
- ];
- },
- indexOf : function(l, v) {
- return jsPlumbUtil.findWithFunction(l, function(_v) { return _v == v; });
- },
- removeWithFunction : function(a, f) {
- var idx = jsPlumbUtil.findWithFunction(a, f);
- if (idx > -1) a.splice(idx, 1);
- return idx != -1;
- },
- remove : function(l, v) {
- var idx = jsPlumbUtil.indexOf(l, v);
- if (idx > -1) l.splice(idx, 1);
- return idx != -1;
- },
- // TODO support insert index
- addWithFunction : function(list, item, hashFunction) {
- if (jsPlumbUtil.findWithFunction(list, hashFunction) == -1) list.push(item);
- },
- addToList : function(map, key, value, insertAtStart) {
- var l = map[key];
- if (l == null) {
- l = [];
- map[key] = l;
- }
- l[insertAtStart ? "unshift" : "push"](value);
- return l;
- },
- //
- // extends the given obj (which can be an array) with the given constructor function, prototype functions, and
- // class members, any of which may be null.
- //
- extend : function(child, parent, _protoFn, _protoAtts) {
- _protoFn = _protoFn || {};
- _protoAtts = _protoAtts || {};
- parent = _isa(parent) ? parent : [ parent ];
-
- for (var i = 0; i < parent.length; i++) {
- for (var j in parent[i].prototype) {
- if(parent[i].prototype.hasOwnProperty(j)) {
- child.prototype[j] = parent[i].prototype[j];
- }
- }
- }
-
- var _makeFn = function(name) {
- return function() {
- for (var i = 0; i < parent.length; i++) {
- if (parent[i].prototype[name])
- parent[i].prototype[name].apply(this, arguments);
- }
- return _protoFn[name].apply(this, arguments);
- };
- };
-
- for (var k in _protoFn) {
- child.prototype[k] = _makeFn(k);
- }
-
- return child;
- },
- uuid : function() {
- return ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
- return v.toString(16);
- }));
- },
- logEnabled : true,
- log : function() {
- if (jsPlumbUtil.logEnabled && typeof console != "undefined") {
- try {
- var msg = arguments[arguments.length - 1];
- console.log(msg);
- }
- catch (e) {}
- }
- },
- group : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.group(g); },
- groupEnd : function(g) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.groupEnd(g); },
- time : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.time(t); },
- timeEnd : function(t) { if (jsPlumbUtil.logEnabled && typeof console != "undefined") console.timeEnd(t); },
-
- /**
- * helper to remove an element from the DOM.
- */
- removeElement : function(element) {
- if (element != null && element.parentNode != null) {
- element.parentNode.removeChild(element);
- }
- },
- /**
- * helper to remove a list of elements from the DOM.
- */
- removeElements : function(elements) {
- for ( var i = 0; i < elements.length; i++)
- jsPlumbUtil.removeElement(elements[i]);
- },
- /*
- * Function: sizeElement
- * Helper to size and position an element. You would typically use
- * this when writing your own Connector or Endpoint implementation.
- *
- * Parameters:
- * x - [int] x position for the element origin
- * y - [int] y position for the element origin
- * w - [int] width of the element
- * h - [int] height of the element
- *
- */
- sizeElement : function(el, x, y, w, h) {
- if (el) {
- el.style.height = h + "px";
- el.height = h;
- el.style.width = w + "px";
- el.width = w;
- el.style.left = x + "px";
- el.style.top = y + "px";
- }
- },
- /**
- * @name jsPlumbUtil.wrap
- * @desc Wraps one function with another, creating a placeholder for the
- * wrapped function if it was null. this is used to wrap the various
- * drag/drop event functions - to allow jsPlumb to be notified of
- * important lifecycle events without imposing itself on the user's
- * drag/drop functionality.
- * @param {Function} wrappedFunction original function to wrap; may be null.
- * @param {Function} newFunction function to wrap the original with.
- * @param {Object} [returnOnThisValue] Optional. Indicates that the wrappedFunction should
- * not be executed if the newFunction returns a value matching 'returnOnThisValue'.
- * note that this is a simple comparison and only works for primitives right now.
- */
- wrap : function(wrappedFunction, newFunction, returnOnThisValue) {
- wrappedFunction = wrappedFunction || function() { };
- newFunction = newFunction || function() { };
- return function() {
- var r = null;
- try {
- r = newFunction.apply(this, arguments);
- } catch (e) {
- jsPlumbUtil.log("jsPlumb function failed : " + e);
- }
- if (returnOnThisValue == null || (r !== returnOnThisValue)) {
- try {
- r = wrappedFunction.apply(this, arguments);
- } catch (e) {
- jsPlumbUtil.log("wrapped function failed : " + e);
- }
- }
- return r;
- };
- }
- };
-
-
- jsPlumbUtil.EventGenerator = function() {
- var _listeners = {}, eventsSuspended = false;
-
- // this is a list of events that should re-throw any errors that occur during their dispatch. as of 1.3.0 this is private to
- // jsPlumb, but it seems feasible that people might want to manipulate this list. the thinking is that we don't want event
- // listeners to bring down jsPlumb - or do we. i can't make up my mind about this, but i know i want to hear about it if the "ready"
- // event fails, because then my page has most likely not initialised. so i have this halfway-house solution. it will be interesting
- // to hear what other people think.
- var eventsToDieOn = [ "ready" ];
-
- this.bind = function(event, listener, insertAtStart) {
- jsPlumbUtil.addToList(_listeners, event, listener, true);
- return this;
- };
-
- this.fire = function(event, value, originalEvent) {
- if (!eventsSuspended && _listeners[event]) {
- // instead of looping through the array we get a counter and a length, because it is possible
- // that an event fired from here could cause the object to get cleaned up, which would throw
- // away the listeners. so after each cycle through the loop we check to ensure we haven't
- // been nuked.
- var l = _listeners[event].length, i = 0, _gone = false, ret = null;
- if (!this.shouldFireEvent || this.shouldFireEvent(event, value, originalEvent)) {
- while (!_gone && i < l && ret !== false) {
-
- // doing it this way rather than catching and then possibly re-throwing means that an error propagated by this
- // method will have the whole call stack available in the debugger.
- if (jsPlumbUtil.findWithFunction(eventsToDieOn, function(e) { return e === event; }) != -1)
- _listeners[event][i](value, originalEvent);
- else {
- // for events we don't want to die on, catch and log.
- try {
- ret = _listeners[event][i](value, originalEvent);
- } catch (e) {
- jsPlumbUtil.log("jsPlumb: fire failed for event " + event + " : " + e);
- }
- }
- i++;
- if (_listeners == null || _listeners[event] == null) _gone = true;
- }
- }
- }
- return this;
- };
-
- this.unbind = function(event) {
- if (event)
- delete _listeners[event];
- else {
- _listeners = {};
- }
- return this;
- };
-
- this.getListener = function(forEvent) {
- return _listeners[forEvent];
- };
- this.setSuspendEvents = function(val) {
- eventsSuspended = val;
- };
- this.isSuspendEvents = function() {
- return eventsSuspended;
- };
- this.cleanupListeners = function() {
- for (var i in _listeners) {
- _listeners[i].splice(0);
- delete _listeners[i];
- }
- };
- };
-
-
- jsPlumbUtil.EventGenerator.prototype = {
- cleanup:function() {
- this.cleanupListeners();
- }
- };
-
-
- // thanks MDC
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fbind
- if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
-
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis ? this : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
-
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
-
- return fBound;
- };
- }
-
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the base functionality for DOM type adapters.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-;(function() {
-
- var canvasAvailable = !!document.createElement('canvas').getContext,
- svgAvailable = !!window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"),
- // http://stackoverflow.com/questions/654112/how-do-you-detect-support-for-vml-or-svg-in-a-browser
- vmlAvailable = function() {
- if (vmlAvailable.vml === undefined) {
- var a = document.body.appendChild(document.createElement('div'));
- a.innerHTML = '<v:shape id="vml_flag1" adj="1" />';
- var b = a.firstChild;
- if (b != null && b.style != null) {
- b.style.behavior = "url(#default#VML)";
- vmlAvailable.vml = b ? typeof b.adj == "object": true;
- }
- else
- vmlAvailable.vml = false;
- a.parentNode.removeChild(a);
- }
- return vmlAvailable.vml;
- };
-
- /**
- Manages dragging for some instance of jsPlumb.
- */
- var DragManager = function(_currentInstance) {
- var _draggables = {}, _dlist = [], _delements = {}, _elementsWithEndpoints = {},
- // elementids mapped to the draggable to which they belong.
- _draggablesForElements = {};
-
- /**
- register some element as draggable. right now the drag init stuff is done elsewhere, and it is
- possible that will continue to be the case.
- */
- this.register = function(el) {
- var jpcl = jsPlumb.CurrentLibrary,
- _el = jpcl.getElementObject(el),
- id = _currentInstance.getId(el),
- parentOffset = jpcl.getOffset(_el);
-
- if (!_draggables[id]) {
- _draggables[id] = el;
- _dlist.push(el);
- _delements[id] = {};
- }
-
- // look for child elements that have endpoints and register them against this draggable.
- var _oneLevel = function(p, startOffset) {
- if (p) {
- for (var i = 0; i < p.childNodes.length; i++) {
- if (p.childNodes[i].nodeType != 3 && p.childNodes[i].nodeType != 8) {
- var cEl = jpcl.getElementObject(p.childNodes[i]),
- cid = _currentInstance.getId(p.childNodes[i], null, true);
- if (cid && _elementsWithEndpoints[cid] && _elementsWithEndpoints[cid] > 0) {
- var cOff = jpcl.getOffset(cEl);
- _delements[id][cid] = {
- id:cid,
- offset:{
- left:cOff.left - parentOffset.left,
- top:cOff.top - parentOffset.top
- }
- };
- _draggablesForElements[cid] = id;
- }
- _oneLevel(p.childNodes[i]);
- }
- }
- }
- };
-
- _oneLevel(el);
- };
-
- // refresh the offsets for child elements of this element.
- this.updateOffsets = function(elId) {
- var jpcl = jsPlumb.CurrentLibrary,
- el = jpcl.getElementObject(elId),
- domEl = jpcl.getDOMElement(el),
- id = _currentInstance.getId(domEl),
- children = _delements[id],
- parentOffset = jpcl.getOffset(el);
-
- if (children) {
- for (var i in children) {
- var cel = jpcl.getElementObject(i),
- cOff = jpcl.getOffset(cel);
-
- _delements[id][i] = {
- id:i,
- offset:{
- left:cOff.left - parentOffset.left,
- top:cOff.top - parentOffset.top
- }
- };
- _draggablesForElements[i] = id;
- }
- }
- };
-
- /**
- notification that an endpoint was added to the given el. we go up from that el's parent
- node, looking for a parent that has been registered as a draggable. if we find one, we add this
- el to that parent's list of elements to update on drag (if it is not there already)
- */
- this.endpointAdded = function(el) {
- var jpcl = jsPlumb.CurrentLibrary, b = document.body, id = _currentInstance.getId(el),
- c = jpcl.getElementObject(el),
- cLoc = jsPlumb.CurrentLibrary.getOffset(c),
- p = el.parentNode, done = p == b;
-
- _elementsWithEndpoints[id] = _elementsWithEndpoints[id] ? _elementsWithEndpoints[id] + 1 : 1;
-
- while (p != null && p != b) {
- var pid = _currentInstance.getId(p, null, true);
- if (pid && _draggables[pid]) {
- var idx = -1, pEl = jpcl.getElementObject(p), pLoc = jpcl.getOffset(pEl);
-
- if (_delements[pid][id] == null) {
- _delements[pid][id] = {
- id:id,
- offset:{
- left:cLoc.left - pLoc.left,
- top:cLoc.top - pLoc.top
- }
- };
- _draggablesForElements[id] = pid;
- }
- break;
- }
- p = p.parentNode;
- }
- };
-
- this.endpointDeleted = function(endpoint) {
- if (_elementsWithEndpoints[endpoint.elementId]) {
- _elementsWithEndpoints[endpoint.elementId]--;
- if (_elementsWithEndpoints[endpoint.elementId] <= 0) {
- for (var i in _delements) {
- if (_delements[i]) {
- delete _delements[i][endpoint.elementId];
- delete _draggablesForElements[endpoint.elementId];
- }
- }
- }
- }
- };
-
- this.changeId = function(oldId, newId) {
- _delements[newId] = _delements[oldId];
- _delements[oldId] = {};
- _draggablesForElements[newId] = _draggablesForElements[oldId];
- _draggablesForElements[oldId] = null;
- };
-
- this.getElementsForDraggable = function(id) {
- return _delements[id];
- };
-
- this.elementRemoved = function(elementId) {
- var elId = _draggablesForElements[elementId];
- if (elId) {
- delete _delements[elId][elementId];
- delete _draggablesForElements[elementId];
- }
- };
-
- this.reset = function() {
- _draggables = {};
- _dlist = [];
- _delements = {};
- _elementsWithEndpoints = {};
- };
-
- };
-
- // for those browsers that dont have it. they still don't have it! but at least they won't crash.
- if (!window.console)
- window.console = { time:function(){}, timeEnd:function(){}, group:function(){}, groupEnd:function(){}, log:function(){} };
-
- window.jsPlumbAdapter = {
-
- headless:false,
-
- getAttribute:function(el, attName) {
- return el.getAttribute(attName);
- },
-
- setAttribute:function(el, a, v) {
- el.setAttribute(a, v);
- },
-
- appendToRoot : function(node) {
- document.body.appendChild(node);
- },
- getRenderModes : function() {
- return [ "canvas", "svg", "vml" ];
- },
- isRenderModeAvailable : function(m) {
- return {
- "canvas":canvasAvailable,
- "svg":svgAvailable,
- "vml":vmlAvailable()
- }[m];
- },
- getDragManager : function(_jsPlumb) {
- return new DragManager(_jsPlumb);
- },
- setRenderMode : function(mode) {
- var renderMode;
-
- if (mode) {
- mode = mode.toLowerCase();
-
- var canvasAvailable = this.isRenderModeAvailable("canvas"),
- svgAvailable = this.isRenderModeAvailable("svg"),
- vmlAvailable = this.isRenderModeAvailable("vml");
-
- // now test we actually have the capability to do this.
- if (mode === "svg") {
- if (svgAvailable) renderMode = "svg";
- else if (canvasAvailable) renderMode = "canvas";
- else if (vmlAvailable) renderMode = "vml";
- }
- else if (mode === "canvas" && canvasAvailable) renderMode = "canvas";
- else if (vmlAvailable) renderMode = "vml";
- }
-
- return renderMode;
- }
- };
-
-
- /*
-
- addClass:
-
- add: function( elem, classNames ) {
- jQuery.each((classNames || "").split(/\s+/), function(i, className){
- if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) )
- elem.className += (elem.className ? " " : "") + className;
- });
- },
- */
-
- /*
-
- removeClass:
-
- elem.className = classNames !== undefined ?
- jQuery.grep(elem.className.split(/\s+/), function(className){
- return !jQuery.className.has( classNames, className );
- }).join(" ") :
-
-*/
-
-})();
-/**
- * @module jsPlumb
- * @description Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * - [Demo Site](http://jsplumb.org)
- * - [GitHub](http://github.com/sporritt/jsplumb)
- *
- * Dual licensed under the MIT and GPL2 licenses.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt ([email protected])
- */
-;(function() {
-
- var _ju = jsPlumbUtil,
- _addClass = function(el, clazz) { jsPlumb.CurrentLibrary.addClass(_gel(el), clazz); },
- _hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(_gel(el), clazz); },
- _removeClass = function(el, clazz) { jsPlumb.CurrentLibrary.removeClass(_gel(el), clazz); },
- _gel = function(el) { return jsPlumb.CurrentLibrary.getElementObject(el); },
- _dom = function(el) { return jsPlumb.CurrentLibrary.getDOMElement(el); },
- _getOffset = function(el, _instance) {
- var o = jsPlumb.CurrentLibrary.getOffset(_gel(el));
- if (_instance != null) {
- var z = _instance.getZoom();
- return {left:o.left / z, top:o.top / z };
- }
- else
- return o;
- },
- _getSize = function(el) {
- return jsPlumb.CurrentLibrary.getSize(_gel(el));
- },
-
- /**
- * creates a timestamp, using milliseconds since 1970, but as a string.
- */
- _timestamp = function() { return "" + (new Date()).getTime(); },
-
- // helper method to update the hover style whenever it, or paintStyle, changes.
- // we use paintStyle as the foundation and merge hoverPaintStyle over the
- // top.
- _updateHoverStyle = function(component) {
- if (component._jsPlumb.paintStyle && component._jsPlumb.hoverPaintStyle) {
- var mergedHoverStyle = {};
- jsPlumb.extend(mergedHoverStyle, component._jsPlumb.paintStyle);
- jsPlumb.extend(mergedHoverStyle, component._jsPlumb.hoverPaintStyle);
- delete component._jsPlumb.hoverPaintStyle;
- // we want the fillStyle of paintStyle to override a gradient, if possible.
- if (mergedHoverStyle.gradient && component._jsPlumb.paintStyle.fillStyle)
- delete mergedHoverStyle.gradient;
- component._jsPlumb.hoverPaintStyle = mergedHoverStyle;
- }
- },
- events = [ "click", "dblclick", "mouseenter", "mouseout", "mousemove", "mousedown", "mouseup", "contextmenu" ],
- eventFilters = { "mouseout":"mouseexit" },
- _updateAttachedElements = function(component, state, timestamp, sourceElement) {
- var affectedElements = component.getAttachedElements();
- if (affectedElements) {
- for (var i = 0, j = affectedElements.length; i < j; i++) {
- if (!sourceElement || sourceElement != affectedElements[i])
- affectedElements[i].setHover(state, true, timestamp); // tell the attached elements not to inform their own attached elements.
- }
- }
- },
- _splitType = function(t) { return t == null ? null : t.split(" "); },
- _applyTypes = function(component, params, doNotRepaint) {
- if (component.getDefaultType) {
- var td = component.getTypeDescriptor();
-
- var o = _ju.merge({}, component.getDefaultType());
- for (var i = 0, j = component._jsPlumb.types.length; i < j; i++)
- o = _ju.merge(o, component._jsPlumb.instance.getType(component._jsPlumb.types[i], td));
-
- if (params) {
- o = _ju.populate(o, params);
- }
-
- component.applyType(o, doNotRepaint);
- if (!doNotRepaint) component.repaint();
- }
- },
-
-// ------------------------------ BEGIN jsPlumbUIComponent --------------------------------------------
-
- jsPlumbUIComponent = window.jsPlumbUIComponent = function(params) {
-
- jsPlumbUtil.EventGenerator.apply(this, arguments);
-
- var self = this,
- a = arguments,
- idPrefix = self.idPrefix,
- id = idPrefix + (new Date()).getTime(),
- jpcl = jsPlumb.CurrentLibrary;
-
- this._jsPlumb = {
- instance: params._jsPlumb,
- parameters:params.parameters || {},
- paintStyle:null,
- hoverPaintStyle:null,
- paintStyleInUse:null,
- hover:false,
- beforeDetach:params.beforeDetach,
- beforeDrop:params.beforeDrop,
- overlayPlacements : [],
- hoverClass: params.hoverClass || params._jsPlumb.Defaults.HoverClass || jsPlumb.Defaults.HoverClass,
- types:[]
- };
-
- this.getId = function() { return id; };
-
- // all components can generate events
-
- if (params.events) {
- for (var i in params.events)
- self.bind(i, params.events[i]);
- }
-
- // all components get this clone function.
- // TODO issue 116 showed a problem with this - it seems 'a' that is in
- // the clone function's scope is shared by all invocations of it, the classic
- // JS closure problem. for now, jsPlumb does a version of this inline where
- // it used to call clone. but it would be nice to find some time to look
- // further at this.
- this.clone = function() {
- var o = {};//new Object();
- this.constructor.apply(o, a);
- return o;
- }.bind(this);
-
-
- // user can supply a beforeDetach callback, which will be executed before a detach
- // is performed; returning false prevents the detach.
- this.isDetachAllowed = function(connection) {
- var r = true;
- if (this._jsPlumb.beforeDetach) {
- try {
- r = this._jsPlumb.beforeDetach(connection);
- }
- catch (e) { _ju.log("jsPlumb: beforeDetach callback failed", e); }
- }
- return r;
- };
-
- // user can supply a beforeDrop callback, which will be executed before a dropped
- // connection is confirmed. user can return false to reject connection.
- this.isDropAllowed = function(sourceId, targetId, scope, connection, dropEndpoint) {
- var r = this._jsPlumb.instance.checkCondition("beforeDrop", {
- sourceId:sourceId,
- targetId:targetId,
- scope:scope,
- connection:connection,
- dropEndpoint:dropEndpoint
- });
- if (this._jsPlumb.beforeDrop) {
- try {
- r = this._jsPlumb.beforeDrop({
- sourceId:sourceId,
- targetId:targetId,
- scope:scope,
- connection:connection,
- dropEndpoint:dropEndpoint
- });
- }
- catch (e) { _ju.log("jsPlumb: beforeDrop callback failed", e); }
- }
- return r;
- };
-
- var boundListeners = [],
- bindAListener = function(obj, type, fn) {
- boundListeners.push([obj, type, fn]);
- obj.bind(type, fn);
- },
- domListeners = [],
- bindOne = function(o, c, evt) {
- var filteredEvent = eventFilters[evt] || evt,
- fn = function(ee) {
- c.fire(filteredEvent, c, ee);
- };
- domListeners.push([o, evt, fn]);
- jpcl.bind(o, evt, fn);
- },
- unbindOne = function(o, evt, fn) {
- var filteredEvent = eventFilters[evt] || evt;
- jpcl.unbind(o, evt, fn);
- };
-
- this.bindListeners = function(obj, _self, _hoverFunction) {
- bindAListener(obj, "click", function(ep, e) { _self.fire("click", _self, e); });
- bindAListener(obj, "dblclick", function(ep, e) { _self.fire("dblclick", _self, e); });
- bindAListener(obj, "contextmenu", function(ep, e) { _self.fire("contextmenu", _self, e); });
- bindAListener(obj, "mouseenter", function(ep, e) {
- if (!_self.isHover()) {
- _hoverFunction(true);
- _self.fire("mouseenter", _self, e);
- }
- });
- bindAListener(obj, "mouseexit", function(ep, e) {
- if (_self.isHover()) {
- _hoverFunction(false);
- _self.fire("mouseexit", _self, e);
- }
- });
- bindAListener(obj, "mousedown", function(ep, e) { _self.fire("mousedown", _self, e); });
- bindAListener(obj, "mouseup", function(ep, e) { _self.fire("mouseup", _self, e); });
- };
-
- this.unbindListeners = function() {
- for (var i = 0; i < boundListeners.length; i++) {
- var o = boundListeners[i];
- o[0].unbind(o[1], o[2]);
- }
- boundListeners = null;
- };
-
- this.attachListeners = function(o, c) {
- for (var i = 0, j = events.length; i < j; i++) {
- bindOne(o, c, events[i]);
- }
- };
- this.detachListeners = function() {
- for (var i = 0; i < domListeners.length; i++) {
- unbindOne(domListeners[i][0], domListeners[i][1], domListeners[i][2]);
- }
- domListeners = null;
- };
-
- this.reattachListenersForElement = function(o) {
- if (arguments.length > 1) {
- for (var i = 0, j = events.length; i < j; i++)
- unbindOne(o, events[i]);
- for (i = 1, j = arguments.length; i < j; i++)
- this.attachListeners(o, arguments[i]);
- }
- };
- };
-
- jsPlumbUtil.extend(jsPlumbUIComponent, jsPlumbUtil.EventGenerator, {
-
- getParameter : function(name) {
- return this._jsPlumb.parameters[name];
- },
-
- setParameter : function(name, value) {
- this._jsPlumb.parameters[name] = value;
- },
-
- getParameters : function() {
- return this._jsPlumb.parameters;
- },
-
- setParameters : function(p) {
- this._jsPlumb.parameters = p;
- },
-
- addClass : function(clazz) {
- if (this.canvas != null)
- _addClass(this.canvas, clazz);
- },
-
- removeClass : function(clazz) {
- if (this.canvas != null)
- _removeClass(this.canvas, clazz);
- },
-
- setType : function(typeId, params, doNotRepaint) {
- this._jsPlumb.types = _splitType(typeId) || [];
- _applyTypes(this, params, doNotRepaint);
- },
-
- getType : function() {
- return this._jsPlumb.types;
- },
-
- reapplyTypes : function(params, doNotRepaint) {
- _applyTypes(this, params, doNotRepaint);
- },
-
- hasType : function(typeId) {
- return jsPlumbUtil.indexOf(this._jsPlumb.types, typeId) != -1;
- },
-
- addType : function(typeId, params, doNotRepaint) {
- var t = _splitType(typeId), _cont = false;
- if (t != null) {
- for (var i = 0, j = t.length; i < j; i++) {
- if (!this.hasType(t[i])) {
- this._jsPlumb.types.push(t[i]);
- _cont = true;
- }
- }
- if (_cont) _applyTypes(this, params, doNotRepaint);
- }
- },
-
- removeType : function(typeId, doNotRepaint) {
- var t = _splitType(typeId), _cont = false, _one = function(tt) {
- var idx = _ju.indexOf(this._jsPlumb.types, tt);
- if (idx != -1) {
- this._jsPlumb.types.splice(idx, 1);
- return true;
- }
- return false;
- }.bind(this);
-
- if (t != null) {
- for (var i = 0,j = t.length; i < j; i++) {
- _cont = _one(t[i]) || _cont;
- }
- if (_cont) _applyTypes(this, null, doNotRepaint);
- }
- },
-
- toggleType : function(typeId, params, doNotRepaint) {
- var t = _splitType(typeId);
- if (t != null) {
- for (var i = 0, j = t.length; i < j; i++) {
- var idx = jsPlumbUtil.indexOf(this._jsPlumb.types, t[i]);
- if (idx != -1)
- this._jsPlumb.types.splice(idx, 1);
- else
- this._jsPlumb.types.push(t[i]);
- }
-
- _applyTypes(this, params, doNotRepaint);
- }
- },
- applyType : function(t, doNotRepaint) {
- this.setPaintStyle(t.paintStyle, doNotRepaint);
- this.setHoverPaintStyle(t.hoverPaintStyle, doNotRepaint);
- if (t.parameters){
- for (var i in t.parameters)
- this.setParameter(i, t.parameters[i]);
- }
- },
- setPaintStyle : function(style, doNotRepaint) {
-// this._jsPlumb.paintStyle = jsPlumb.extend({}, style);
-// TODO figure out if we want components to clone paintStyle so as not to share it.
- this._jsPlumb.paintStyle = style;
- this._jsPlumb.paintStyleInUse = this._jsPlumb.paintStyle;
- _updateHoverStyle(this);
- if (!doNotRepaint) this.repaint();
- },
- getPaintStyle : function() {
- return this._jsPlumb.paintStyle;
- },
- setHoverPaintStyle : function(style, doNotRepaint) {
- //this._jsPlumb.hoverPaintStyle = jsPlumb.extend({}, style);
-// TODO figure out if we want components to clone paintStyle so as not to share it.
- this._jsPlumb.hoverPaintStyle = style;
- _updateHoverStyle(this);
- if (!doNotRepaint) this.repaint();
- },
- getHoverPaintStyle : function() {
- return this._jsPlumb.hoverPaintStyle;
- },
- cleanup:function() {
- this.unbindListeners();
- this.detachListeners();
- },
- destroy:function() {
- this.cleanupListeners();
- this.clone = null;
- this._jsPlumb = null;
- },
-
- isHover : function() { return this._jsPlumb.hover; },
-
- setHover : function(hover, ignoreAttachedElements, timestamp) {
- var jpcl = jsPlumb.CurrentLibrary;
- // while dragging, we ignore these events. this keeps the UI from flashing and
- // swishing and whatevering.
- if (this._jsPlumb && !this._jsPlumb.instance.currentlyDragging && !this._jsPlumb.instance.isHoverSuspended()) {
-
- this._jsPlumb.hover = hover;
-
- if (this.canvas != null) {
- if (this._jsPlumb.instance.hoverClass != null) {
- jpcl[hover ? "addClass" : "removeClass"](this.canvas, this._jsPlumb.instance.hoverClass);
- }
- }
- if (this._jsPlumb.hoverPaintStyle != null) {
- this._jsPlumb.paintStyleInUse = hover ? this._jsPlumb.hoverPaintStyle : this._jsPlumb.paintStyle;
- if (!this._jsPlumb.instance.isSuspendDrawing()) {
- timestamp = timestamp || _timestamp();
- this.repaint({timestamp:timestamp, recalc:false});
- }
- }
- // get the list of other affected elements, if supported by this component.
- // for a connection, its the endpoints. for an endpoint, its the connections! surprise.
- if (this.getAttachedElements && !ignoreAttachedElements)
- _updateAttachedElements(this, hover, _timestamp(), this);
- }
- }
- });
-
-// ------------------------------ END jsPlumbUIComponent --------------------------------------------
-
-// ------------------------------ BEGIN OverlayCapablejsPlumbUIComponent --------------------------------------------
-
- var _internalLabelOverlayId = "__label",
- // helper to get the index of some overlay
- _getOverlayIndex = function(component, id) {
- var idx = -1;
- for (var i = 0, j = component._jsPlumb.overlays.length; i < j; i++) {
- if (id === component._jsPlumb.overlays[i].id) {
- idx = i;
- break;
- }
- }
- return idx;
- },
- // this is a shortcut helper method to let people add a label as
- // overlay.
- _makeLabelOverlay = function(component, params) {
-
- var _params = {
- cssClass:params.cssClass,
- labelStyle : component.labelStyle,
- id:_internalLabelOverlayId,
- component:component,
- _jsPlumb:component._jsPlumb.instance // TODO not necessary, since the instance can be accessed through the component.
- },
- mergedParams = jsPlumb.extend(_params, params);
-
- return new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()].Label( mergedParams );
- },
- _processOverlay = function(component, o) {
- var _newOverlay = null;
- if (_ju.isArray(o)) { // this is for the shorthand ["Arrow", { width:50 }] syntax
- // there's also a three arg version:
- // ["Arrow", { width:50 }, {location:0.7}]
- // which merges the 3rd arg into the 2nd.
- var type = o[0],
- // make a copy of the object so as not to mess up anyone else's reference...
- p = jsPlumb.extend({component:component, _jsPlumb:component._jsPlumb.instance}, o[1]);
- if (o.length == 3) jsPlumb.extend(p, o[2]);
- _newOverlay = new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()][type](p);
- } else if (o.constructor == String) {
- _newOverlay = new jsPlumb.Overlays[component._jsPlumb.instance.getRenderMode()][o]({component:component, _jsPlumb:component._jsPlumb.instance});
- } else {
- _newOverlay = o;
- }
-
- component._jsPlumb.overlays.push(_newOverlay);
- },
- _calculateOverlaysToAdd = function(component, params) {
- var defaultKeys = component.defaultOverlayKeys || [], o = params.overlays,
- checkKey = function(k) {
- return component._jsPlumb.instance.Defaults[k] || jsPlumb.Defaults[k] || [];
- };
-
- if (!o) o = [];
-
- for (var i = 0, j = defaultKeys.length; i < j; i++)
- o.unshift.apply(o, checkKey(defaultKeys[i]));
-
- return o;
- },
- OverlayCapableJsPlumbUIComponent = window.OverlayCapableJsPlumbUIComponent = function(params) {
-
- jsPlumbUIComponent.apply(this, arguments);
- this._jsPlumb.overlays = [];
-
- var _overlays = _calculateOverlaysToAdd(this, params);
- if (_overlays) {
- for (var i = 0, j = _overlays.length; i < j; i++) {
- _processOverlay(this, _overlays[i]);
- }
- }
-
- if (params.label) {
- var loc = params.labelLocation || this.defaultLabelLocation || 0.5,
- labelStyle = params.labelStyle || this._jsPlumb.instance.Defaults.LabelStyle || jsPlumb.Defaults.LabelStyle;
-
- this._jsPlumb.overlays.push(_makeLabelOverlay(this, {
- label:params.label,
- location:loc,
- labelStyle:labelStyle
- }));
- }
- };
-
- jsPlumbUtil.extend(OverlayCapableJsPlumbUIComponent, jsPlumbUIComponent, {
- applyType : function(t, doNotRepaint) {
- this.removeAllOverlays(doNotRepaint);
- if (t.overlays) {
- for (var i = 0, j = t.overlays.length; i < j; i++)
- this.addOverlay(t.overlays[i], true);
- }
- },
- setHover : function(hover, ignoreAttachedElements, timestamp) {
- if (this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) {
- for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++) {
- this._jsPlumb.overlays[i][hover ? "addClass":"removeClass"](this._jsPlumb.instance.hoverClass);
- }
- }
- },
- addOverlay : function(overlay, doNotRepaint) {
- _processOverlay(this, overlay);
- if (!doNotRepaint) this.repaint();
- },
- getOverlay : function(id) {
- var idx = _getOverlayIndex(this, id);
- return idx >= 0 ? this._jsPlumb.overlays[idx] : null;
- },
- getOverlays : function() {
- return this._jsPlumb.overlays;
- },
- hideOverlay : function(id) {
- var o = this.getOverlay(id);
- if (o) o.hide();
- },
- hideOverlays : function() {
- for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++)
- this._jsPlumb.overlays[i].hide();
- },
- showOverlay : function(id) {
- var o = this.getOverlay(id);
- if (o) o.show();
- },
- showOverlays : function() {
- for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++)
- this._jsPlumb.overlays[i].show();
- },
- removeAllOverlays : function(doNotRepaint) {
- for (var i = 0, j = this._jsPlumb.overlays.length; i < j; i++) {
- if (this._jsPlumb.overlays[i].cleanup) this._jsPlumb.overlays[i].cleanup();
- }
-
- this._jsPlumb.overlays.splice(0, this._jsPlumb.overlays.length);
- if (!doNotRepaint)
- this.repaint();
- },
- removeOverlay : function(overlayId) {
- var idx = _getOverlayIndex(this, overlayId);
- if (idx != -1) {
- var o = this._jsPlumb.overlays[idx];
- if (o.cleanup) o.cleanup();
- this._jsPlumb.overlays.splice(idx, 1);
- }
- },
- removeOverlays : function() {
- for (var i = 0, j = arguments.length; i < j; i++)
- this.removeOverlay(arguments[i]);
- },
- getLabel : function() {
- var lo = this.getOverlay(_internalLabelOverlayId);
- return lo != null ? lo.getLabel() : null;
- },
- getLabelOverlay : function() {
- return this.getOverlay(_internalLabelOverlayId);
- },
- setLabel : function(l) {
- var lo = this.getOverlay(_internalLabelOverlayId);
- if (!lo) {
- var params = l.constructor == String || l.constructor == Function ? { label:l } : l;
- lo = _makeLabelOverlay(this, params);
- this._jsPlumb.overlays.push(lo);
- }
- else {
- if (l.constructor == String || l.constructor == Function) lo.setLabel(l);
- else {
- if (l.label) lo.setLabel(l.label);
- if (l.location) lo.setLocation(l.location);
- }
- }
-
- if (!this._jsPlumb.instance.isSuspendDrawing())
- this.repaint();
- },
- cleanup:function() {
- for (var i = 0; i < this._jsPlumb.overlays.length; i++) {
- this._jsPlumb.overlays[i].cleanup();
- this._jsPlumb.overlays[i].destroy();
- }
- this._jsPlumb.overlays.splice(0);
- }
- });
-
-// ------------------------------ END OverlayCapablejsPlumbUIComponent --------------------------------------------
-
- var _jsPlumbInstanceIndex = 0,
- getInstanceIndex = function() {
- var i = _jsPlumbInstanceIndex + 1;
- _jsPlumbInstanceIndex++;
- return i;
- };
-
- var jsPlumbInstance = window.jsPlumbInstance = function(_defaults) {
-
- this.Defaults = {
- Anchor : "BottomCenter",
- Anchors : [ null, null ],
- ConnectionsDetachable : true,
- ConnectionOverlays : [ ],
- Connector : "Bezier",
- Container : null,
- DoNotThrowErrors:false,
- DragOptions : { },
- DropOptions : { },
- Endpoint : "Dot",
- EndpointOverlays : [ ],
- Endpoints : [ null, null ],
- EndpointStyle : { fillStyle : "#456" },
- EndpointStyles : [ null, null ],
- EndpointHoverStyle : null,
- EndpointHoverStyles : [ null, null ],
- HoverPaintStyle : null,
- LabelStyle : { color : "black" },
- LogEnabled : false,
- Overlays : [ ],
- MaxConnections : 1,
- PaintStyle : { lineWidth : 8, strokeStyle : "#456" },
- ReattachConnections:false,
- RenderMode : "svg",
- Scope : "jsPlumb_DefaultScope"
- };
- if (_defaults) jsPlumb.extend(this.Defaults, _defaults);
-
- this.logEnabled = this.Defaults.LogEnabled;
- this._connectionTypes = {};
- this._endpointTypes = {};
-
- jsPlumbUtil.EventGenerator.apply(this);
-
- var _currentInstance = this,
- _instanceIndex = getInstanceIndex(),
- _bb = _currentInstance.bind,
- _initialDefaults = {},
- _zoom = 1,
- _info = function(el) {
- var _el = _dom(el);
- return { el:_el, id:(jsPlumbUtil.isString(el) && _el == null) ? el : _getId(_el) };
- };
-
- this.getInstanceIndex = function() { return _instanceIndex; };
-
- this.setZoom = function(z, repaintEverything) {
- _zoom = z;
- if (repaintEverything) _currentInstance.repaintEverything();
- };
- this.getZoom = function() { return _zoom; };
-
- for (var i in this.Defaults)
- _initialDefaults[i] = this.Defaults[i];
-
- this.bind = function(event, fn) {
- if ("ready" === event && initialized) fn();
- else _bb.apply(_currentInstance,[event, fn]);
- };
-
- _currentInstance.importDefaults = function(d) {
- for (var i in d) {
- _currentInstance.Defaults[i] = d[i];
- }
- return _currentInstance;
- };
-
- _currentInstance.restoreDefaults = function() {
- _currentInstance.Defaults = jsPlumb.extend({}, _initialDefaults);
- return _currentInstance;
- };
-
- var log = null,
- resizeTimer = null,
- initialized = false,
- // TODO remove from window scope
- connections = [],
- // map of element id -> endpoint lists. an element can have an arbitrary
- // number of endpoints on it, and not all of them have to be connected
- // to anything.
- endpointsByElement = {},
- endpointsByUUID = {},
- offsets = {},
- offsetTimestamps = {},
- floatingConnections = {},
- draggableStates = {},
- connectionBeingDragged = false,
- sizes = [],
- _suspendDrawing = false,
- _suspendedAt = null,
- DEFAULT_SCOPE = this.Defaults.Scope,
- renderMode = null, // will be set in init()
- _curIdStamp = 1,
- _idstamp = function() { return "" + _curIdStamp++; },
-
- //
- // appends an element to some other element, which is calculated as follows:
- //
- // 1. if _currentInstance.Defaults.Container exists, use that element.
- // 2. if the 'parent' parameter exists, use that.
- // 3. otherwise just use the root element (for DOM usage, the document body).
- //
- //
- _appendElement = function(el, parent) {
- if (_currentInstance.Defaults.Container)
- jsPlumb.CurrentLibrary.appendElement(el, _currentInstance.Defaults.Container);
- else if (!parent)
- jsPlumbAdapter.appendToRoot(el);
- else
- jsPlumb.CurrentLibrary.appendElement(el, parent);
- },
-
- //
- // YUI, for some reason, put the result of a Y.all call into an object that contains
- // a '_nodes' array, instead of handing back an array-like object like the other
- // libraries do.
- //
- _convertYUICollection = function(c) {
- return c._nodes ? c._nodes : c;
- },
-
- //
- // Draws an endpoint and its connections. this is the main entry point into drawing connections as well
- // as endpoints, since jsPlumb is endpoint-centric under the hood.
- //
- // @param element element to draw (of type library specific element object)
- // @param ui UI object from current library's event system. optional.
- // @param timestamp timestamp for this paint cycle. used to speed things up a little by cutting down the amount of offset calculations we do.
- // @param clearEdits defaults to false; indicates that mouse edits for connectors should be cleared
- ///
- _draw = function(element, ui, timestamp, clearEdits) {
-
- // TODO is it correct to filter by headless at this top level? how would a headless adapter ever repaint?
- if (!jsPlumbAdapter.headless && !_suspendDrawing) {
- var id = _getId(element),
- repaintEls = _currentInstance.dragManager.getElementsForDraggable(id);
-
- if (timestamp == null) timestamp = _timestamp();
-
- // update the offset of everything _before_ we try to draw anything.
- var o = _updateOffset( { elId : id, offset : ui, recalc : false, timestamp : timestamp });
-
- if (repaintEls) {
- for (var i in repaintEls) {
- _updateOffset( {
- elId : repaintEls[i].id,
- offset : {
- left:o.o.left + repaintEls[i].offset.left,
- top:o.o.top + repaintEls[i].offset.top
- },
- recalc : false,
- timestamp : timestamp
- });
- }
- }
-
-
- _currentInstance.anchorManager.redraw(id, ui, timestamp, null, clearEdits);
-
- if (repaintEls) {
- for (var j in repaintEls) {
- _currentInstance.anchorManager.redraw(repaintEls[j].id, ui, timestamp, repaintEls[j].offset, clearEdits, true);
- }
- }
- }
- },
-
- //
- // executes the given function against the given element if the first
- // argument is an object, or the list of elements, if the first argument
- // is a list. the function passed in takes (element, elementId) as
- // arguments.
- //
- _elementProxy = function(element, fn) {
- var retVal = null, el, id;
- if (_ju.isArray(element)) {
- retVal = [];
- for ( var i = 0, j = element.length; i < j; i++) {
- el = _gel(element[i]);
- id = _currentInstance.getAttribute(el, "id");
- retVal.push(fn(el, id)); // append return values to what we will return
- }
- } else {
- el = _gel(element);
- id = _currentInstance.getAttribute(el, "id");
- retVal = fn(el, id);
- }
- return retVal;
- },
-
- //
- // gets an Endpoint by uuid.
- //
- _getEndpoint = function(uuid) { return endpointsByUUID[uuid]; },
-
- /**
- * inits a draggable if it's not already initialised.
- * TODO: somehow abstract this to the adapter, because the concept of "draggable" has no
- * place on the server.
- */
- _initDraggableIfNecessary = function(element, isDraggable, dragOptions) {
- // TODO move to DragManager?
- if (!jsPlumbAdapter.headless) {
- var _draggable = isDraggable == null ? false : isDraggable, jpcl = jsPlumb.CurrentLibrary;
- if (_draggable) {
- if (jpcl.isDragSupported(element) && !jpcl.isAlreadyDraggable(element)) {
- var options = dragOptions || _currentInstance.Defaults.DragOptions || jsPlumb.Defaults.DragOptions;
- options = jsPlumb.extend( {}, options); // make a copy.
- var dragEvent = jpcl.dragEvents.drag,
- stopEvent = jpcl.dragEvents.stop,
- startEvent = jpcl.dragEvents.start;
-
- options[startEvent] = _ju.wrap(options[startEvent], function() {
- _currentInstance.setHoverSuspended(true);
- _currentInstance.select({source:element}).addClass(_currentInstance.elementDraggingClass + " " + _currentInstance.sourceElementDraggingClass, true);
- _currentInstance.select({target:element}).addClass(_currentInstance.elementDraggingClass + " " + _currentInstance.targetElementDraggingClass, true);
- _currentInstance.setConnectionBeingDragged(true);
- });
-
- options[dragEvent] = _ju.wrap(options[dragEvent], function() {
- var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
- _draw(element, ui, null, true);
- _addClass(element, "jsPlumb_dragged");
- });
- options[stopEvent] = _ju.wrap(options[stopEvent], function() {
- var ui = jpcl.getUIPosition(arguments, _currentInstance.getZoom());
- _draw(element, ui);
- _removeClass(element, "jsPlumb_dragged");
- _currentInstance.setHoverSuspended(false);
- _currentInstance.select({source:element}).removeClass(_currentInstance.elementDraggingClass + " " + _currentInstance.sourceElementDraggingClass, true);
- _currentInstance.select({target:element}).removeClass(_currentInstance.elementDraggingClass + " " + _currentInstance.targetElementDraggingClass, true);
- _currentInstance.setConnectionBeingDragged(false);
- });
- var elId = _getId(element); // need ID
- draggableStates[elId] = true;
- var draggable = draggableStates[elId];
- options.disabled = draggable == null ? false : !draggable;
- jpcl.initDraggable(element, options, false, _currentInstance);
- _currentInstance.dragManager.register(element);
- }
- }
- }
- },
-
- /*
- * prepares a final params object that can be passed to _newConnection, taking into account defaults, events, etc.
- */
- _prepareConnectionParams = function(params, referenceParams) {
- var _p = jsPlumb.extend( { }, params);
- if (referenceParams) jsPlumb.extend(_p, referenceParams);
-
- // hotwire endpoints passed as source or target to sourceEndpoint/targetEndpoint, respectively.
- if (_p.source) {
- if (_p.source.endpoint)
- _p.sourceEndpoint = _p.source;
- else
- _p.source = _dom(_p.source);
- }
- if (_p.target) {
- if (_p.target.endpoint)
- _p.targetEndpoint = _p.target;
- else
- _p.target = _dom(_p.target);
- }
-
- // test for endpoint uuids to connect
- if (params.uuids) {
- _p.sourceEndpoint = _getEndpoint(params.uuids[0]);
- _p.targetEndpoint = _getEndpoint(params.uuids[1]);
- }
-
- // now ensure that if we do have Endpoints already, they're not full.
- // source:
- if (_p.sourceEndpoint && _p.sourceEndpoint.isFull()) {
- _ju.log(_currentInstance, "could not add connection; source endpoint is full");
- return;
- }
-
- // target:
- if (_p.targetEndpoint && _p.targetEndpoint.isFull()) {
- _ju.log(_currentInstance, "could not add connection; target endpoint is full");
- return;
- }
-
- // if source endpoint mandates connection type and nothing specified in our params, use it.
- if (!_p.type && _p.sourceEndpoint)
- _p.type = _p.sourceEndpoint.connectionType;
-
- // copy in any connectorOverlays that were specified on the source endpoint.
- // it doesnt copy target endpoint overlays. i'm not sure if we want it to or not.
- if (_p.sourceEndpoint && _p.sourceEndpoint.connectorOverlays) {
- _p.overlays = _p.overlays || [];
- for (var i = 0, j = _p.sourceEndpoint.connectorOverlays.length; i < j; i++) {
- _p.overlays.push(_p.sourceEndpoint.connectorOverlays[i]);
- }
- }
-
- // pointer events
- if (!_p["pointer-events"] && _p.sourceEndpoint && _p.sourceEndpoint.connectorPointerEvents)
- _p["pointer-events"] = _p.sourceEndpoint.connectorPointerEvents;
-
- // if there's a target specified (which of course there should be), and there is no
- // target endpoint specified, and 'newConnection' was not set to true, then we check to
- // see if a prior call to makeTarget has provided us with the specs for the target endpoint, and
- // we use those if so. additionally, if the makeTarget call was specified with 'uniqueEndpoint' set
- // to true, then if that target endpoint has already been created, we re-use it.
-
- var tid, tep, existingUniqueEndpoint, newEndpoint;
-
- // TODO: this code can be refactored to be a little dry.
- if (_p.target && !_p.target.endpoint && !_p.targetEndpoint && !_p.newConnection) {
- tid = _getId(_p.target);
- tep =_targetEndpointDefinitions[tid];
- existingUniqueEndpoint = _targetEndpoints[tid];
-
- if (tep) {
- // if target not enabled, return.
- if (!_targetsEnabled[tid]) return;
- tep.isTarget = true;
-
- // check for max connections??
- newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.target, tep);
- if (_targetEndpointsUnique[tid]) _targetEndpoints[tid] = newEndpoint;
- _p.targetEndpoint = newEndpoint;
- // TODO test options to makeTarget to see if we should do this?
- newEndpoint._doNotDeleteOnDetach = false; // reset.
- newEndpoint._deleteOnDetach = true;
- }
- }
-
- // same thing, but for source.
- if (_p.source && !_p.source.endpoint && !_p.sourceEndpoint && !_p.newConnection) {
- tid = _getId(_p.source);
- tep = _sourceEndpointDefinitions[tid];
- existingUniqueEndpoint = _sourceEndpoints[tid];
-
- if (tep) {
- // if source not enabled, return.
- if (!_sourcesEnabled[tid]) return;
- tep.isSource = true;
-
- newEndpoint = existingUniqueEndpoint != null ? existingUniqueEndpoint : _currentInstance.addEndpoint(_p.source, tep);
- if (_sourceEndpointsUnique[tid]) _sourceEndpoints[tid] = newEndpoint;
- _p.sourceEndpoint = newEndpoint;
- // TODO test options to makeSource to see if we should do this?
- newEndpoint._doNotDeleteOnDetach = false; // reset.
- newEndpoint._deleteOnDetach = true;
- }
- }
-
- return _p;
- },
-
- _newConnection = function(params) {
- var connectionFunc = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
- endpointFunc = _currentInstance.Defaults.EndpointType || jsPlumb.Endpoint,
- parent = jsPlumb.CurrentLibrary.getParent;
-
- if (params.container)
- params.parent = params.container;
- else {
- if (params.sourceEndpoint)
- params.parent = params.sourceEndpoint.parent;
- else if (params.source.constructor == endpointFunc)
- params.parent = params.source.parent;
- else params.parent = parent(params.source);
- }
-
- params._jsPlumb = _currentInstance;
- params.newConnection = _newConnection;
- params.newEndpoint = _newEndpoint;
- params.endpointsByUUID = endpointsByUUID;
- params.endpointsByElement = endpointsByElement;
- params.finaliseConnection = _finaliseConnection;
- var con = new connectionFunc(params);
- con.id = "con_" + _idstamp();
- _eventFireProxy("click", "click", con);
- _eventFireProxy("dblclick", "dblclick", con);
- _eventFireProxy("contextmenu", "contextmenu", con);
- return con;
- },
-
- //
- // adds the connection to the backing model, fires an event if necessary and then redraws
- //
- _finaliseConnection = function(jpc, params, originalEvent, doInformAnchorManager) {
- params = params || {};
- // add to list of connections (by scope).
- if (!jpc.suspendedEndpoint)
- connections.push(jpc);
-
- // always inform the anchor manager
- // except that if jpc has a suspended endpoint it's not true to say the
- // connection is new; it has just (possibly) moved. the question is whether
- // to make that call here or in the anchor manager. i think perhaps here.
- if (jpc.suspendedEndpoint == null || doInformAnchorManager)
- _currentInstance.anchorManager.newConnection(jpc);
-
- // force a paint
- _draw(jpc.source);
-
- // fire an event
- if (!params.doNotFireConnectionEvent && params.fireEvent !== false) {
-
- var eventArgs = {
- connection:jpc,
- source : jpc.source, target : jpc.target,
- sourceId : jpc.sourceId, targetId : jpc.targetId,
- sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
- };
-
- _currentInstance.fire("connection", eventArgs, originalEvent);
- }
- },
-
- _eventFireProxy = function(event, proxyEvent, obj) {
- obj.bind(event, function(originalObject, originalEvent) {
- _currentInstance.fire(proxyEvent, obj, originalEvent);
- });
- },
-
- /*
- * for the given endpoint params, returns an appropriate parent element for the UI elements that will be added.
- * this function is used by _newEndpoint (directly below), and also in the makeSource function in jsPlumb.
- *
- * the logic is to first look for a "container" member of params, and pass that back if found. otherwise we
- * handoff to the 'getParent' function in the current library.
- */
- _getParentFromParams = function(params) {
- if (params.container)
- return params.container;
- else {
- var tag = jsPlumb.CurrentLibrary.getTagName(params.source),
- p = jsPlumb.CurrentLibrary.getParent(params.source);
- if (tag && tag.toLowerCase() === "td")
- return jsPlumb.CurrentLibrary.getParent(p);
- else return p;
- }
- },
-
- /*
- factory method to prepare a new endpoint. this should always be used instead of creating Endpoints
- manually, since this method attaches event listeners and an id.
- */
- _newEndpoint = function(params) {
- var endpointFunc = _currentInstance.Defaults.EndpointType || jsPlumb.Endpoint;
- var _p = jsPlumb.extend({}, params);
- _p.parent = _getParentFromParams(_p);
- _p._jsPlumb = _currentInstance;
- _p.newConnection = _newConnection;
- _p.newEndpoint = _newEndpoint;
- _p.endpointsByUUID = endpointsByUUID;
- _p.endpointsByElement = endpointsByElement;
- _p.finaliseConnection = _finaliseConnection;
- _p.fireDetachEvent = fireDetachEvent;
- _p.floatingConnections = floatingConnections;
- _p.getParentFromParams = _getParentFromParams;
- _p.elementId = _getId(_p.source);
- var ep = new endpointFunc(_p);
- ep.id = "ep_" + _idstamp();
- _eventFireProxy("click", "endpointClick", ep);
- _eventFireProxy("dblclick", "endpointDblClick", ep);
- _eventFireProxy("contextmenu", "contextmenu", ep);
- if (!jsPlumbAdapter.headless)
- _currentInstance.dragManager.endpointAdded(_p.source);
- return ep;
- },
-
- /*
- * performs the given function operation on all the connections found
- * for the given element id; this means we find all the endpoints for
- * the given element, and then for each endpoint find the connectors
- * connected to it. then we pass each connection in to the given
- * function.
- */
- _operation = function(elId, func, endpointFunc) {
- var endpoints = endpointsByElement[elId];
- if (endpoints && endpoints.length) {
- for ( var i = 0, ii = endpoints.length; i < ii; i++) {
- for ( var j = 0, jj = endpoints[i].connections.length; j < jj; j++) {
- var retVal = func(endpoints[i].connections[j]);
- // if the function passed in returns true, we exit.
- // most functions return false.
- if (retVal) return;
- }
- if (endpointFunc) endpointFunc(endpoints[i]);
- }
- }
- },
-
- _setDraggable = function(element, draggable) {
- return _elementProxy(element, function(el, id) {
- draggableStates[id] = draggable;
- if (jsPlumb.CurrentLibrary.isDragSupported(el)) {
- jsPlumb.CurrentLibrary.setDraggable(el, draggable);
- }
- });
- },
- /*
- * private method to do the business of hiding/showing.
- *
- * @param el
- * either Id of the element in question or a library specific
- * object for the element.
- * @param state
- * String specifying a value for the css 'display' property
- * ('block' or 'none').
- */
- _setVisible = function(el, state, alsoChangeEndpoints) {
- state = state === "block";
- var endpointFunc = null;
- if (alsoChangeEndpoints) {
- if (state) endpointFunc = function(ep) {
- ep.setVisible(true, true, true);
- };
- else endpointFunc = function(ep) {
- ep.setVisible(false, true, true);
- };
- }
- var info = _info(el);
- _operation(info.id, function(jpc) {
- if (state && alsoChangeEndpoints) {
- // this test is necessary because this functionality is new, and i wanted to maintain backwards compatibility.
- // this block will only set a connection to be visible if the other endpoint in the connection is also visible.
- var oidx = jpc.sourceId === info.id ? 1 : 0;
- if (jpc.endpoints[oidx].isVisible()) jpc.setVisible(true);
- }
- else // the default behaviour for show, and what always happens for hide, is to just set the visibility without getting clever.
- jpc.setVisible(state);
- }, endpointFunc);
- },
- /*
- * toggles the draggable state of the given element(s).
- * el is either an id, or an element object, or a list of ids/element objects.
- */
- _toggleDraggable = function(el) {
- return _elementProxy(el, function(el, elId) {
- var state = draggableStates[elId] == null ? false : draggableStates[elId];
- state = !state;
- draggableStates[elId] = state;
- jsPlumb.CurrentLibrary.setDraggable(el, state);
- return state;
- });
- },
- /**
- * private method to do the business of toggling hiding/showing.
- */
- _toggleVisible = function(elId, changeEndpoints) {
- var endpointFunc = null;
- if (changeEndpoints) {
- endpointFunc = function(ep) {
- var state = ep.isVisible();
- ep.setVisible(!state);
- };
- }
- _operation(elId, function(jpc) {
- var state = jpc.isVisible();
- jpc.setVisible(!state);
- }, endpointFunc);
- // todo this should call _elementProxy, and pass in the
- // _operation(elId, f) call as a function. cos _toggleDraggable does
- // that.
- },
- /**
- * updates the offset and size for a given element, and stores the
- * values. if 'offset' is not null we use that (it would have been
- * passed in from a drag call) because it's faster; but if it is null,
- * or if 'recalc' is true in order to force a recalculation, we get the current values.
- */
- _updateOffset = function(params) {
- var timestamp = params.timestamp, recalc = params.recalc, offset = params.offset, elId = params.elId, s;
- if (_suspendDrawing && !timestamp) timestamp = _suspendedAt;
- if (!recalc) {
- if (timestamp && timestamp === offsetTimestamps[elId]) {
- return {o:params.offset || offsets[elId], s:sizes[elId]};
- }
- }
- if (recalc || !offset) { // if forced repaint or no offset available, we recalculate.
- // get the current size and offset, and store them
- s = _gel(elId);
- if (s != null) {
- sizes[elId] = _getSize(s);
- offsets[elId] = _getOffset(s, _currentInstance);
- offsetTimestamps[elId] = timestamp;
- }
- } else {
- offsets[elId] = offset;
- if (sizes[elId] == null) {
- s = _gel(elId);
- if (s != null) sizes[elId] = _getSize(s);
- }
- offsetTimestamps[elId] = timestamp;
- }
-
- if(offsets[elId] && !offsets[elId].right) {
- offsets[elId].right = offsets[elId].left + sizes[elId][0];
- offsets[elId].bottom = offsets[elId].top + sizes[elId][1];
- offsets[elId].width = sizes[elId][0];
- offsets[elId].height = sizes[elId][1];
- offsets[elId].centerx = offsets[elId].left + (offsets[elId].width / 2);
- offsets[elId].centery = offsets[elId].top + (offsets[elId].height / 2);
- }
- return {o:offsets[elId], s:sizes[elId]};
- },
-
- // TODO comparison performance
- _getCachedData = function(elId) {
- var o = offsets[elId];
- if (!o)
- return _updateOffset({elId:elId});
- else
- return {o:o, s:sizes[elId]};
- },
-
- /**
- * gets an id for the given element, creating and setting one if
- * necessary. the id is of the form
- *
- * jsPlumb_<instance index>_<index in instance>
- *
- * where "index in instance" is a monotonically increasing integer that starts at 0,
- * for each instance. this method is used not only to assign ids to elements that do not
- * have them but also to connections and endpoints.
- */
- _getId = function(element, uuid, doNotCreateIfNotFound) {
- if (jsPlumbUtil.isString(element)) return element;
- if (element == null) return null;
- var id = jsPlumbAdapter.getAttribute(element, "id");
- if (!id || id === "undefined") {
- // check if fixed uuid parameter is given
- if (arguments.length == 2 && arguments[1] !== undefined)
- id = uuid;
- else if (arguments.length == 1 || (arguments.length == 3 && !arguments[2]))
- id = "jsPlumb_" + _instanceIndex + "_" + _idstamp();
-
- if (!doNotCreateIfNotFound) jsPlumbAdapter.setAttribute(element, "id", id);
- }
- return id;
- };
-
- this.setConnectionBeingDragged = function(v) {
- connectionBeingDragged = v;
- };
- this.isConnectionBeingDragged = function() {
- return connectionBeingDragged;
- };
-
- this.connectorClass = "_jsPlumb_connector";
- this.hoverClass = "_jsPlumb_hover";
- this.endpointClass = "_jsPlumb_endpoint";
- this.endpointConnectedClass = "_jsPlumb_endpoint_connected";
- this.endpointFullClass = "_jsPlumb_endpoint_full";
- this.endpointDropAllowedClass = "_jsPlumb_endpoint_drop_allowed";
- this.endpointDropForbiddenClass = "_jsPlumb_endpoint_drop_forbidden";
- this.overlayClass = "_jsPlumb_overlay";
- this.draggingClass = "_jsPlumb_dragging";
- this.elementDraggingClass = "_jsPlumb_element_dragging";
- this.sourceElementDraggingClass = "_jsPlumb_source_element_dragging";
- this.targetElementDraggingClass = "_jsPlumb_target_element_dragging";
- this.endpointAnchorClassPrefix = "_jsPlumb_endpoint_anchor";
- this.hoverSourceClass = "_jsPlumb_source_hover";
- this.hoverTargetClass = "_jsPlumb_target_hover";
- this.dragSelectClass = "_jsPlumb_drag_select";
-
- this.Anchors = {};
- this.Connectors = { "canvas":{}, "svg":{}, "vml":{} };
- this.Endpoints = { "canvas":{}, "svg":{}, "vml":{} };
- this.Overlays = { "canvas":{}, "svg":{}, "vml":{}};
- this.ConnectorRenderers = {};
- this.SVG = "svg";
- this.CANVAS = "canvas";
- this.VML = "vml";
-
-
-// --------------------------- jsPLumbInstance public API ---------------------------------------------------------
-
-
- this.addEndpoint = function(el, params, referenceParams) {
- referenceParams = referenceParams || {};
- var p = jsPlumb.extend({}, referenceParams);
- jsPlumb.extend(p, params);
- p.endpoint = p.endpoint || _currentInstance.Defaults.Endpoint || jsPlumb.Defaults.Endpoint;
- p.paintStyle = p.paintStyle || _currentInstance.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle;
- // YUI wrapper
- el = _convertYUICollection(el);
-
- var results = [],
- inputs = (_ju.isArray(el) || (el.length != null && !_ju.isString(el))) ? el : [ el ];
-
- for (var i = 0, j = inputs.length; i < j; i++) {
- var _el = _dom(inputs[i]), id = _getId(_el);
- p.source = _el;
-
- _updateOffset({ elId : id, timestamp:_suspendedAt });
- var e = _newEndpoint(p);
- if (p.parentAnchor) e.parentAnchor = p.parentAnchor;
- _ju.addToList(endpointsByElement, id, e);
- var myOffset = offsets[id],
- myWH = sizes[id],
- anchorLoc = e.anchor.compute( { xy : [ myOffset.left, myOffset.top ], wh : myWH, element : e, timestamp:_suspendedAt }),
- endpointPaintParams = { anchorLoc : anchorLoc, timestamp:_suspendedAt };
-
- if (_suspendDrawing) endpointPaintParams.recalc = false;
- if (!_suspendDrawing) e.paint(endpointPaintParams);
-
- results.push(e);
- e._doNotDeleteOnDetach = true; // mark this as being added via addEndpoint.
- }
-
- return results.length == 1 ? results[0] : results;
- };
-
-
- this.addEndpoints = function(el, endpoints, referenceParams) {
- var results = [];
- for ( var i = 0, j = endpoints.length; i < j; i++) {
- var e = _currentInstance.addEndpoint(el, endpoints[i], referenceParams);
- if (_ju.isArray(e))
- Array.prototype.push.apply(results, e);
- else results.push(e);
- }
- return results;
- };
-
- this.animate = function(el, properties, options) {
- options = options || {};
- var ele = _gel(el),
- id = _getId(el),
- stepFunction = jsPlumb.CurrentLibrary.dragEvents.step,
- completeFunction = jsPlumb.CurrentLibrary.dragEvents.complete;
-
- options[stepFunction] = _ju.wrap(options[stepFunction], function() {
- _currentInstance.repaint(id);
- });
-
- // onComplete repaints, just to make sure everything looks good at the end of the animation.
- options[completeFunction] = _ju.wrap(options[completeFunction], function() {
- _currentInstance.repaint(id);
- });
-
- jsPlumb.CurrentLibrary.animate(ele, properties, options);
- };
-
- /**
- * checks for a listener for the given condition, executing it if found, passing in the given value.
- * condition listeners would have been attached using "bind" (which is, you could argue, now overloaded, since
- * firing click events etc is a bit different to what this does). i thought about adding a "bindCondition"
- * or something, but decided against it, for the sake of simplicity. jsPlumb will never fire one of these
- * condition events anyway.
- */
- this.checkCondition = function(conditionName, value) {
- var l = _currentInstance.getListener(conditionName),
- r = true;
-
- if (l && l.length > 0) {
- try {
- for (var i = 0, j = l.length; i < j; i++) {
- r = r && l[i](value);
- }
- }
- catch (e) {
- _ju.log(_currentInstance, "cannot check condition [" + conditionName + "]" + e);
- }
- }
- return r;
- };
-
- /**
- * checks a condition asynchronously: fires the event handler and passes the handler
- * a 'proceed' function and a 'stop' function. The handler MUST execute one or other
- * of these once it has made up its mind.
- *
- * Note that although this reads the listener list for the given condition, it
- * does not loop through and hit each listener, because that, with asynchronous
- * callbacks, would be messy. so it uses only the first listener registered.
- */
- this.checkASyncCondition = function(conditionName, value, proceed, stop) {
- var l = _currentInstance.getListener(conditionName);
-
- if (l && l.length > 0) {
- try {
- l[0](value, proceed, stop);
- }
- catch (e) {
- _ju.log(_currentInstance, "cannot asynchronously check condition [" + conditionName + "]" + e);
- }
- }
- };
-
-
- this.connect = function(params, referenceParams) {
- // prepare a final set of parameters to create connection with
- var _p = _prepareConnectionParams(params, referenceParams), jpc;
- // TODO probably a nicer return value if the connection was not made. _prepareConnectionParams
- // will return null (and log something) if either endpoint was full. what would be nicer is to
- // create a dedicated 'error' object.
- if (_p) {
- // create the connection. it is not yet registered
- jpc = _newConnection(_p);
- // now add it the model, fire an event, and redraw
- _finaliseConnection(jpc, _p);
- }
- return jpc;
- };
-
- this.deleteEndpoint = function(object, doNotRepaintAfterwards) {
- var _is = _currentInstance.setSuspendDrawing(true);
- var endpoint = (typeof object == "string") ? endpointsByUUID[object] : object;
- if (endpoint) {
- // Make the delete silent if this is a floating endpoint so detach
- // events are only fired when established connections are detached
- _currentInstance.deleteObject({
- endpoint:endpoint,
- fireEvent: !endpoint.isFloating()
- });
- }
- if(!_is) _currentInstance.setSuspendDrawing(false, doNotRepaintAfterwards);
- return _currentInstance;
- };
-
-
- this.deleteEveryEndpoint = function() {
- var _is = _currentInstance.setSuspendDrawing(true);
- for ( var id in endpointsByElement) {
- var endpoints = endpointsByElement[id];
- if (endpoints && endpoints.length) {
- for ( var i = 0, j = endpoints.length; i < j; i++) {
- _currentInstance.deleteEndpoint(endpoints[i], true);
- }
- }
- }
- endpointsByElement = {};
- endpointsByUUID = {};
- _currentInstance.anchorManager.reset();
- _currentInstance.dragManager.reset();
- if(!_is) _currentInstance.setSuspendDrawing(false);
- return _currentInstance;
- };
-
- var fireDetachEvent = function(jpc, doFireEvent, originalEvent) {
- // may have been given a connection, or in special cases, an object
- var connType = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
- argIsConnection = jpc.constructor == connType,
- params = argIsConnection ? {
- connection:jpc,
- source : jpc.source, target : jpc.target,
- sourceId : jpc.sourceId, targetId : jpc.targetId,
- sourceEndpoint : jpc.endpoints[0], targetEndpoint : jpc.endpoints[1]
- } : jpc;
-
- if (doFireEvent)
- _currentInstance.fire("connectionDetached", params, originalEvent);
-
- _currentInstance.anchorManager.connectionDetached(params);
- };
-
- this.unregisterEndpoint = function(endpoint) {
- if (endpoint._jsPlumb.uuid) endpointsByUUID[endpoint._jsPlumb.uuid] = null;
- _currentInstance.anchorManager.deleteEndpoint(endpoint);
- // TODO at least replace this with a removeWithFunction call.
- for (var e in endpointsByElement) {
- var endpoints = endpointsByElement[e];
- if (endpoints) {
- var newEndpoints = [];
- for (var i = 0, j = endpoints.length; i < j; i++)
- if (endpoints[i] != endpoint) newEndpoints.push(endpoints[i]);
-
- endpointsByElement[e] = newEndpoints;
- }
- if(endpointsByElement[e].length <1){
- delete endpointsByElement[e];
- }
- }
- };
-
- this.detach = function() {
-
- if (arguments.length === 0) return;
- var connType = _currentInstance.Defaults.ConnectionType || _currentInstance.getDefaultConnectionType(),
- firstArgIsConnection = arguments[0].constructor == connType,
- params = arguments.length == 2 ? firstArgIsConnection ? (arguments[1] || {}) : arguments[0] : arguments[0],
- fireEvent = (params.fireEvent !== false),
- forceDetach = params.forceDetach,
- conn = firstArgIsConnection ? arguments[0] : params.connection;
-
- if (conn) {
- if (forceDetach || jsPlumbUtil.functionChain(true, false, [
- [ conn.endpoints[0], "isDetachAllowed", [ conn ] ],
- [ conn.endpoints[1], "isDetachAllowed", [ conn ] ],
- [ conn, "isDetachAllowed", [ conn ] ],
- [ _currentInstance, "checkCondition", [ "beforeDetach", conn ] ] ])) {
-
- conn.endpoints[0].detach(conn, false, true, fireEvent);
- }
- }
- else {
- var _p = jsPlumb.extend( {}, params); // a backwards compatibility hack: source should be thought of as 'params' in this case.
- // test for endpoint uuids to detach
- if (_p.uuids) {
- _getEndpoint(_p.uuids[0]).detachFrom(_getEndpoint(_p.uuids[1]), fireEvent);
- } else if (_p.sourceEndpoint && _p.targetEndpoint) {
- _p.sourceEndpoint.detachFrom(_p.targetEndpoint);
- } else {
- var sourceId = _getId(_dom(_p.source)),
- targetId = _getId(_dom(_p.target));
- _operation(sourceId, function(jpc) {
- if ((jpc.sourceId == sourceId && jpc.targetId == targetId) || (jpc.targetId == sourceId && jpc.sourceId == targetId)) {
- if (_currentInstance.checkCondition("beforeDetach", jpc)) {
- jpc.endpoints[0].detach(jpc, false, true, fireEvent);
- }
- }
- });
- }
- }
- };
-
- this.detachAllConnections = function(el, params) {
- params = params || {};
- el = _dom(el);
- var id = _getId(el),
- endpoints = endpointsByElement[id];
- if (endpoints && endpoints.length) {
- for ( var i = 0, j = endpoints.length; i < j; i++) {
- endpoints[i].detachAll(params.fireEvent !== false);
- }
- }
- return _currentInstance;
- };
-
- this.detachEveryConnection = function(params) {
- params = params || {};
- _currentInstance.doWhileSuspended(function() {
- for ( var id in endpointsByElement) {
- var endpoints = endpointsByElement[id];
- if (endpoints && endpoints.length) {
- for ( var i = 0, j = endpoints.length; i < j; i++) {
- endpoints[i].detachAll(params.fireEvent !== false);
- }
- }
- }
- connections.splice(0);
- });
- return _currentInstance;
- };
-
- /// not public. but of course its exposed. how to change this.
- this.deleteObject = function(params) {
- var result = {
- endpoints : {},
- connections : {},
- endpointCount:0,
- connectionCount:0
- },
- fireEvent = params.fireEvent !== false,
- deleteAttachedObjects = params.deleteAttachedObjects !== false;
-
- var unravelConnection = function(connection) {
- if(connection != null && result.connections[connection.id] == null) {
- connection._jsPlumb && connection.setHover(false);
- result.connections[connection.id] = connection;
- result.connectionCount++;
- if (deleteAttachedObjects) {
- for (var j = 0; j < connection.endpoints.length; j++) {
- if (connection.endpoints[j]._deleteOnDetach)
- unravelEndpoint(connection.endpoints[j]);
- }
- }
- }
- };
- var unravelEndpoint = function(endpoint) {
- if(endpoint != null && result.endpoints[endpoint.id] == null) {
- endpoint._jsPlumb && endpoint.setHover(false);
- result.endpoints[endpoint.id] = endpoint;
- result.endpointCount++;
-
- if (deleteAttachedObjects) {
- for (var i = 0; i < endpoint.connections.length; i++) {
- var c = endpoint.connections[i];
- unravelConnection(c);
- }
- }
- }
- };
-
- if (params.connection)
- unravelConnection(params.connection);
- else unravelEndpoint(params.endpoint);
-
- // loop through connections
- for (var i in result.connections) {
- var c = result.connections[i];
- c.endpoints[0].detachFromConnection(c);
- c.endpoints[1].detachFromConnection(c);
- //_currentInstance.unregisterConnection(c);
- jsPlumbUtil.removeWithFunction(connections, function(_c) {
- return c.id == _c.id;
- });
- fireDetachEvent(c, fireEvent, params.originalEvent);
- c.cleanup();
- c.destroy();
- }
-
- // loop through endpoints
- for (var j in result.endpoints) {
- var e = result.endpoints[j];
- _currentInstance.unregisterEndpoint(e);
- // FIRE some endpoint deleted event?
- e.cleanup();
- e.destroy();
- }
-
- return result;
- };
-
- this.draggable = function(el, options) {
- var i,j,ele;
- // allows for array or jquery/mootools selector
- if (typeof el == 'object' && el.length) {
- for (i = 0, j = el.length; i < j; i++) {
- ele = _dom(el[i]);
- if (ele) _initDraggableIfNecessary(ele, true, options);
- }
- }
- // allows for YUI selector
- else if (el._nodes) { // TODO this is YUI specific; really the logic should be forced
- // into the library adapters (for jquery and mootools aswell)
- for (i = 0, j = el._nodes.length; i < j; i++) {
- ele = _dom(el._nodes[i]);
- if (ele) _initDraggableIfNecessary(ele, true, options);
- }
- }
- else {
- ele = _dom(el);
- if (ele) _initDraggableIfNecessary(ele, true, options);
- }
- return _currentInstance;
- };
-
-
- // just a library-agnostic wrapper.
- this.extend = function(o1, o2) {
- return jsPlumb.CurrentLibrary.extend(o1, o2);
- };
-
- // helpers for select/selectEndpoints
- var _setOperation = function(list, func, args, selector) {
- for (var i = 0, j = list.length; i < j; i++) {
- list[i][func].apply(list[i], args);
- }
- return selector(list);
- },
- _getOperation = function(list, func, args) {
- var out = [];
- for (var i = 0, j = list.length; i < j; i++) {
- out.push([ list[i][func].apply(list[i], args), list[i] ]);
- }
- return out;
- },
- setter = function(list, func, selector) {
- return function() {
- return _setOperation(list, func, arguments, selector);
- };
- },
- getter = function(list, func) {
- return function() {
- return _getOperation(list, func, arguments);
- };
- },
- prepareList = function(input, doNotGetIds) {
- var r = [];
- if (input) {
- if (typeof input == 'string') {
- if (input === "*") return input;
- r.push(input);
- }
- else {
- input = _gel(input);
- if (doNotGetIds) r = input;
- else {
- for (var i = 0, j = input.length; i < j; i++)
- r.push(_info(input[i]).id);
- }
- }
- }
- return r;
- },
- filterList = function(list, value, missingIsFalse) {
- if (list === "*") return true;
- return list.length > 0 ? jsPlumbUtil.indexOf(list, value) != -1 : !missingIsFalse;
- };
-
- // get some connections, specifying source/target/scope
- this.getConnections = function(options, flat) {
- if (!options) {
- options = {};
- } else if (options.constructor == String) {
- options = { "scope": options };
- }
- var scope = options.scope || _currentInstance.getDefaultScope(),
- scopes = prepareList(scope, true),
- sources = prepareList(options.source),
- targets = prepareList(options.target),
- results = (!flat && scopes.length > 1) ? {} : [],
- _addOne = function(scope, obj) {
- if (!flat && scopes.length > 1) {
- var ss = results[scope];
- if (ss == null) {
- ss = results[scope] = [];
- }
- ss.push(obj);
- } else results.push(obj);
- };
-
- for ( var j = 0, jj = connections.length; j < jj; j++) {
- var c = connections[j];
- if (filterList(scopes, c.scope) && filterList(sources, c.sourceId) && filterList(targets, c.targetId))
- _addOne(c.scope, c);
- }
-
- return results;
- };
-
- var _curryEach = function(list, executor) {
- return function(f) {
- for (var i = 0, ii = list.length; i < ii; i++) {
- f(list[i]);
- }
- return executor(list);
- };
- },
- _curryGet = function(list) {
- return function(idx) {
- return list[idx];
- };
- };
-
- var _makeCommonSelectHandler = function(list, executor) {
- var out = {
- length:list.length,
- each:_curryEach(list, executor),
- get:_curryGet(list)
- },
- setters = ["setHover", "removeAllOverlays", "setLabel", "addClass", "addOverlay", "removeOverlay",
- "removeOverlays", "showOverlay", "hideOverlay", "showOverlays", "hideOverlays", "setPaintStyle",
- "setHoverPaintStyle", "setSuspendEvents", "setParameter", "setParameters", "setVisible",
- "repaint", "addType", "toggleType", "removeType", "removeClass", "setType", "bind", "unbind" ],
-
- getters = ["getLabel", "getOverlay", "isHover", "getParameter", "getParameters", "getPaintStyle",
- "getHoverPaintStyle", "isVisible", "hasType", "getType", "isSuspendEvents" ],
- i, ii;
-
- for (i = 0, ii = setters.length; i < ii; i++)
- out[setters[i]] = setter(list, setters[i], executor);
-
- for (i = 0, ii = getters.length; i < ii; i++)
- out[getters[i]] = getter(list, getters[i]);
-
- return out;
- };
-
- var _makeConnectionSelectHandler = function(list) {
- var common = _makeCommonSelectHandler(list, _makeConnectionSelectHandler);
- return jsPlumb.CurrentLibrary.extend(common, {
- // setters
- setDetachable:setter(list, "setDetachable", _makeConnectionSelectHandler),
- setReattach:setter(list, "setReattach", _makeConnectionSelectHandler),
- setConnector:setter(list, "setConnector", _makeConnectionSelectHandler),
- detach:function() {
- for (var i = 0, ii = list.length; i < ii; i++)
- _currentInstance.detach(list[i]);
- },
- // getters
- isDetachable:getter(list, "isDetachable"),
- isReattach:getter(list, "isReattach")
- });
- };
-
- var _makeEndpointSelectHandler = function(list) {
- var common = _makeCommonSelectHandler(list, _makeEndpointSelectHandler);
- return jsPlumb.CurrentLibrary.extend(common, {
- setEnabled:setter(list, "setEnabled", _makeEndpointSelectHandler),
- setAnchor:setter(list, "setAnchor", _makeEndpointSelectHandler),
- isEnabled:getter(list, "isEnabled"),
- detachAll:function() {
- for (var i = 0, ii = list.length; i < ii; i++)
- list[i].detachAll();
- },
- "remove":function() {
- for (var i = 0, ii = list.length; i < ii; i++)
- _currentInstance.deleteObject({endpoint:list[i]});
- }
- });
- };
-
-
- this.select = function(params) {
- params = params || {};
- params.scope = params.scope || "*";
- return _makeConnectionSelectHandler(params.connections || _currentInstance.getConnections(params, true));
- };
-
- this.selectEndpoints = function(params) {
- params = params || {};
- params.scope = params.scope || "*";
- var noElementFilters = !params.element && !params.source && !params.target,
- elements = noElementFilters ? "*" : prepareList(params.element),
- sources = noElementFilters ? "*" : prepareList(params.source),
- targets = noElementFilters ? "*" : prepareList(params.target),
- scopes = prepareList(params.scope, true);
-
- var ep = [];
-
- for (var el in endpointsByElement) {
- var either = filterList(elements, el, true),
- source = filterList(sources, el, true),
- sourceMatchExact = sources != "*",
- target = filterList(targets, el, true),
- targetMatchExact = targets != "*";
-
- // if they requested 'either' then just match scope. otherwise if they requested 'source' (not as a wildcard) then we have to match only endpoints that have isSource set to to true, and the same thing with isTarget.
- if ( either || source || target ) {
- inner:
- for (var i = 0, ii = endpointsByElement[el].length; i < ii; i++) {
- var _ep = endpointsByElement[el][i];
- if (filterList(scopes, _ep.scope, true)) {
-
- var noMatchSource = (sourceMatchExact && sources.length > 0 && !_ep.isSource),
- noMatchTarget = (targetMatchExact && targets.length > 0 && !_ep.isTarget);
-
- if (noMatchSource || noMatchTarget)
- continue inner;
-
- ep.push(_ep);
- }
- }
- }
- }
-
- return _makeEndpointSelectHandler(ep);
- };
-
- // get all connections managed by the instance of jsplumb.
- this.getAllConnections = function() { return connections; };
- this.getDefaultScope = function() { return DEFAULT_SCOPE; };
- // get an endpoint by uuid.
- this.getEndpoint = _getEndpoint;
- // get endpoints for some element.
- this.getEndpoints = function(el) { return endpointsByElement[_info(el).id]; };
- // gets the default endpoint type. used when subclassing. see wiki.
- this.getDefaultEndpointType = function() { return jsPlumb.Endpoint; };
- // gets the default connection type. used when subclassing. see wiki.
- this.getDefaultConnectionType = function() { return jsPlumb.Connection; };
- /*
- * Gets an element's id, creating one if necessary. really only exposed
- * for the lib-specific functionality to access; would be better to pass
- * the current instance into the lib-specific code (even though this is
- * a static call. i just don't want to expose it to the public API).
- */
- this.getId = _getId;
- this.getOffset = function(id) {
- var o = offsets[id];
- return _updateOffset({elId:id});
- };
-
- this.getSelector = function() {
- return jsPlumb.CurrentLibrary.getSelector.apply(null, arguments);
- };
-
- // get the size of the element with the given id, perhaps from cache.
- this.getSize = function(id) {
- var s = sizes[id];
- if (!s) _updateOffset({elId:id});
- return sizes[id];
- };
-
- this.appendElement = _appendElement;
-
- var _hoverSuspended = false;
- this.isHoverSuspended = function() { return _hoverSuspended; };
- this.setHoverSuspended = function(s) { _hoverSuspended = s; };
-
- var _isAvailable = function(m) {
- return function() {
- return jsPlumbAdapter.isRenderModeAvailable(m);
- };
- };
- this.isCanvasAvailable = _isAvailable("canvas");
- this.isSVGAvailable = _isAvailable("svg");
- this.isVMLAvailable = _isAvailable("vml");
-
- // set an element's connections to be hidden
- this.hide = function(el, changeEndpoints) {
- _setVisible(el, "none", changeEndpoints);
- return _currentInstance;
- };
-
- // exposed for other objects to use to get a unique id.
- this.idstamp = _idstamp;
-
- this.connectorsInitialized = false;
- var connectorTypes = [], rendererTypes = ["canvas", "svg", "vml"];
- this.registerConnectorType = function(connector, name) {
- connectorTypes.push([connector, name]);
- };
-
- /**
- * callback from the current library to tell us to prepare ourselves (attach
- * mouse listeners etc; can't do that until the library has provided a bind method)
- */
- this.init = function() {
- var _oneType = function(renderer, name, fn) {
- jsPlumb.Connectors[renderer][name] = function() {
- fn.apply(this, arguments);
- jsPlumb.ConnectorRenderers[renderer].apply(this, arguments);
- };
- jsPlumbUtil.extend(jsPlumb.Connectors[renderer][name], [ fn, jsPlumb.ConnectorRenderers[renderer]]);
- };
-
- if (!jsPlumb.connectorsInitialized) {
- for (var i = 0; i < connectorTypes.length; i++) {
- for (var j = 0; j < rendererTypes.length; j++) {
- _oneType(rendererTypes[j], connectorTypes[i][1], connectorTypes[i][0]);
- }
-
- }
- jsPlumb.connectorsInitialized = true;
- }
-
- if (!initialized) {
- _currentInstance.anchorManager = new jsPlumb.AnchorManager({jsPlumbInstance:_currentInstance});
- _currentInstance.setRenderMode(_currentInstance.Defaults.RenderMode); // calling the method forces the capability logic to be run.
- initialized = true;
- _currentInstance.fire("ready", _currentInstance);
- }
- }.bind(this);
-
- this.log = log;
- this.jsPlumbUIComponent = jsPlumbUIComponent;
-
- /*
- * Creates an anchor with the given params.
- *
- *
- * Returns: The newly created Anchor.
- * Throws: an error if a named anchor was not found.
- */
- this.makeAnchor = function() {
- var _a = function(t, p) {
- if (jsPlumb.Anchors[t]) return new jsPlumb.Anchors[t](p);
- if (!_currentInstance.Defaults.DoNotThrowErrors)
- throw { msg:"jsPlumb: unknown anchor type '" + t + "'" };
- };
- if (arguments.length === 0) return null;
- var specimen = arguments[0], elementId = arguments[1], jsPlumbInstance = arguments[2], newAnchor = null;
- // if it appears to be an anchor already...
- if (specimen.compute && specimen.getOrientation) return specimen; //TODO hazy here about whether it should be added or is already added somehow.
- // is it the name of an anchor type?
- else if (typeof specimen == "string") {
- newAnchor = _a(arguments[0], {elementId:elementId, jsPlumbInstance:_currentInstance});
- }
- // is it an array? it will be one of:
- // an array of [name, params] - this defines a single anchor
- // an array of arrays - this defines some dynamic anchors
- // an array of numbers - this defines a single anchor.
- else if (_ju.isArray(specimen)) {
- if (_ju.isArray(specimen[0]) || _ju.isString(specimen[0])) {
- if (specimen.length == 2 && _ju.isString(specimen[0]) && _ju.isObject(specimen[1])) {
- var pp = jsPlumb.extend({elementId:elementId, jsPlumbInstance:_currentInstance}, specimen[1]);
- newAnchor = _a(specimen[0], pp);
- }
- else
- newAnchor = new jsPlumb.DynamicAnchor({anchors:specimen, selector:null, elementId:elementId, jsPlumbInstance:jsPlumbInstance});
- }
- else {
- var anchorParams = {
- x:specimen[0], y:specimen[1],
- orientation : (specimen.length >= 4) ? [ specimen[2], specimen[3] ] : [0,0],
- offsets : (specimen.length >= 6) ? [ specimen[4], specimen[5] ] : [ 0, 0 ],
- elementId:elementId,
- jsPlumbInstance:jsPlumbInstance,
- cssClass:specimen.length == 7 ? specimen[6] : null
- };
- newAnchor = new jsPlumb.Anchor(anchorParams);
- newAnchor.clone = function() { return new jsPlumb.Anchor(anchorParams); };
- }
- }
-
- if (!newAnchor.id) newAnchor.id = "anchor_" + _idstamp();
- return newAnchor;
- };
-
- /**
- * makes a list of anchors from the given list of types or coords, eg
- * ["TopCenter", "RightMiddle", "BottomCenter", [0, 1, -1, -1] ]
- */
- this.makeAnchors = function(types, elementId, jsPlumbInstance) {
- var r = [];
- for ( var i = 0, ii = types.length; i < ii; i++) {
- if (typeof types[i] == "string")
- r.push(jsPlumb.Anchors[types[i]]({elementId:elementId, jsPlumbInstance:jsPlumbInstance}));
- else if (_ju.isArray(types[i]))
- r.push(_currentInstance.makeAnchor(types[i], elementId, jsPlumbInstance));
- }
- return r;
- };
-
- /**
- * Makes a dynamic anchor from the given list of anchors (which may be in shorthand notation as strings or dimension arrays, or Anchor
- * objects themselves) and the given, optional, anchorSelector function (jsPlumb uses a default if this is not provided; most people will
- * not need to provide this - i think).
- */
- this.makeDynamicAnchor = function(anchors, anchorSelector) {
- return new jsPlumb.DynamicAnchor({anchors:anchors, selector:anchorSelector, elementId:null, jsPlumbInstance:_currentInstance});
- };
-
-// --------------------- makeSource/makeTarget ----------------------------------------------
-
- var _targetEndpointDefinitions = {},
- _targetEndpoints = {},
- _targetEndpointsUnique = {},
- _targetMaxConnections = {},
- _setEndpointPaintStylesAndAnchor = function(ep, epIndex) {
- ep.paintStyle = ep.paintStyle ||
- _currentInstance.Defaults.EndpointStyles[epIndex] ||
- _currentInstance.Defaults.EndpointStyle ||
- jsPlumb.Defaults.EndpointStyles[epIndex] ||
- jsPlumb.Defaults.EndpointStyle;
- ep.hoverPaintStyle = ep.hoverPaintStyle ||
- _currentInstance.Defaults.EndpointHoverStyles[epIndex] ||
- _currentInstance.Defaults.EndpointHoverStyle ||
- jsPlumb.Defaults.EndpointHoverStyles[epIndex] ||
- jsPlumb.Defaults.EndpointHoverStyle;
-
- ep.anchor = ep.anchor ||
- _currentInstance.Defaults.Anchors[epIndex] ||
- _currentInstance.Defaults.Anchor ||
- jsPlumb.Defaults.Anchors[epIndex] ||
- jsPlumb.Defaults.Anchor;
-
- ep.endpoint = ep.endpoint ||
- _currentInstance.Defaults.Endpoints[epIndex] ||
- _currentInstance.Defaults.Endpoint ||
- jsPlumb.Defaults.Endpoints[epIndex] ||
- jsPlumb.Defaults.Endpoint;
- },
- // TODO put all the source stuff inside one parent, keyed by id.
- _sourceEndpointDefinitions = {},
- _sourceEndpoints = {},
- _sourceEndpointsUnique = {},
- _sourcesEnabled = {},
- _sourceTriggers = {},
- _sourceMaxConnections = {},
- _targetsEnabled = {},
- selectorFilter = function(evt, _el, selector) {
- var t = evt.target || evt.srcElement, ok = false,
- sel = _currentInstance.getSelector(_el, selector);
- for (var j = 0; j < sel.length; j++) {
- if (sel[j] == t) {
- ok = true;
- break;
- }
- }
- return ok;
- };
-
- // see API docs
- this.makeTarget = function(el, params, referenceParams) {
-
- // put jsplumb ref into params without altering the params passed in
- var p = jsPlumb.extend({_jsPlumb:_currentInstance}, referenceParams);
- jsPlumb.extend(p, params);
- p.isTarget = true;
-
- // calculate appropriate paint styles and anchor from the params given
- _setEndpointPaintStylesAndAnchor(p, 1);
-
- var jpcl = jsPlumb.CurrentLibrary,
- targetScope = p.scope || _currentInstance.Defaults.Scope,
- deleteEndpointsOnDetach = !(p.deleteEndpointsOnDetach === false),
- maxConnections = p.maxConnections || -1,
- onMaxConnections = p.onMaxConnections;
-
- var _doOne = function(el) {
-
- // get the element's id and store the endpoint definition for it. jsPlumb.connect calls will look for one of these,
- // and use the endpoint definition if found.
- // decode the info for this element (id and element)
- var elInfo = _info(el),
- elid = elInfo.id,
- proxyComponent = new jsPlumbUIComponent(p),
- dropOptions = jsPlumb.extend({}, p.dropOptions || {});
-
- // store the definitions keyed against the element id.
- _targetEndpointDefinitions[elid] = p;
- _targetEndpointsUnique[elid] = p.uniqueEndpoint;
- _targetMaxConnections[elid] = maxConnections;
- _targetsEnabled[elid] = true;
-
- var _drop = function() {
- _currentInstance.currentlyDragging = false;
- var originalEvent = jsPlumb.CurrentLibrary.getDropEvent(arguments),
- targetCount = _currentInstance.select({target:elid}).length,
- draggable = _gel(jpcl.getDragObject(arguments)),
- id = _currentInstance.getAttribute(draggable, "dragId"),
- scope = _currentInstance.getAttribute(draggable, "originalScope"),
- jpc = floatingConnections[id],
- idx = jpc.endpoints[0].isFloating() ? 0 : 1,
- // this is not necessarily correct. if the source is being dragged,
- // then the source endpoint is actually the currently suspended endpoint.
- source = jpc.endpoints[0],
- _endpoint = p.endpoint ? jsPlumb.extend({}, p.endpoint) : {};
-
- if (!_targetsEnabled[elid] || _targetMaxConnections[elid] > 0 && targetCount >= _targetMaxConnections[elid]){
- if (onMaxConnections) {
- // TODO here we still have the id of the floating element, not the
- // actual target.
- onMaxConnections({
- element:elInfo.el,
- connection:jpc
- }, originalEvent);
- }
- return false;
- }
-
- // unlock the source anchor to allow it to refresh its position if necessary
- source.anchor.locked = false;
-
- // restore the original scope if necessary (issue 57)
- if (scope) jpcl.setDragScope(draggable, scope);
-
- // check if drop is allowed here.
- // if the source is being dragged then in fact
- // the source and target ids to pass into the drop interceptor are
- // source - elid
- // target - jpc's targetId
- //
- // otherwise the ids are
- // source - jpc.sourceId
- // target - elid
- //
- var _continue = proxyComponent.isDropAllowed(idx === 0 ? elid : jpc.sourceId, idx === 0 ? jpc.targetId : elid, jpc.scope, jpc, null);
-
- // reinstate any suspended endpoint; this just puts the connection back into
- // a state in which it will report sensible values if someone asks it about
- // its target. we're going to throw this connection away shortly so it doesnt matter
- // if we manipulate it a bit.
- if (jpc.suspendedEndpoint) {
- jpc[idx ? "targetId" : "sourceId"] = jpc.suspendedEndpoint.elementId;
- jpc[idx ? "target" : "source"] = jpc.suspendedEndpoint.element;
- jpc.endpoints[idx] = jpc.suspendedEndpoint;
- }
-
- if (_continue) {
-
- // make a new Endpoint for the target
- var _el = jpcl.getElementObject(elInfo.el),
- newEndpoint = _targetEndpoints[elid] || _currentInstance.addEndpoint(_el, p);
-
- if (p.uniqueEndpoint) _targetEndpoints[elid] = newEndpoint; // may of course just store what it just pulled out. that's ok.
- // TODO test options to makeTarget to see if we should do this?
- newEndpoint._doNotDeleteOnDetach = false; // reset.
- newEndpoint._deleteOnDetach = true;
-
- // if the anchor has a 'positionFinder' set, then delegate to that function to find
- // out where to locate the anchor.
- if (newEndpoint.anchor.positionFinder != null) {
- var dropPosition = jpcl.getUIPosition(arguments, _currentInstance.getZoom()),
- elPosition = _getOffset(_el, _currentInstance),
- elSize = _getSize(_el),
- ap = newEndpoint.anchor.positionFinder(dropPosition, elPosition, elSize, newEndpoint.anchor.constructorParams);
- newEndpoint.anchor.x = ap[0];
- newEndpoint.anchor.y = ap[1];
- // now figure an orientation for it..kind of hard to know what to do actually. probably the best thing i can do is to
- // support specifying an orientation in the anchor's spec. if one is not supplied then i will make the orientation
- // be what will cause the most natural link to the source: it will be pointing at the source, but it needs to be
- // specified in one axis only, and so how to make that choice? i think i will use whichever axis is the one in which
- // the target is furthest away from the source.
- }
-
- // change the target endpoint and target element information. really this should be
- // done on a method on connection
- jpc[idx ? "target" : "source"] = newEndpoint.element;
- jpc[idx ? "targetId" : "sourceId"] = newEndpoint.elementId;
- jpc.endpoints[idx].detachFromConnection(jpc);
- if (jpc.endpoints[idx]._deleteOnDetach)
- jpc.endpoints[idx].deleteAfterDragStop = true; // tell this endpoint to delet itself after drag stop.
- // set new endpoint, and configure the settings for endpoints to delete on detach
- newEndpoint.addConnection(jpc);
- jpc.endpoints[idx] = newEndpoint;
- jpc.deleteEndpointsOnDetach = deleteEndpointsOnDetach;
-
- // inform the anchor manager to update its target endpoint for this connection.
- // TODO refactor to make this a single method.
- if (idx == 1)
- _currentInstance.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.suspendedElementId, jpc.targetId, jpc);
- else
- _currentInstance.anchorManager.sourceChanged(jpc.suspendedEndpoint.elementId, jpc.sourceId, jpc);
-
- _finaliseConnection(jpc, null, originalEvent);
-
- }
- // if not allowed to drop...
- else {
- // TODO this code is identical (pretty much) to what happens when a connection
- // dragged from a normal endpoint is in this situation. refactor.
- // is this an existing connection, and will we reattach?
- // TODO also this assumes the source needs to detach - is that always valid?
- if (jpc.suspendedEndpoint) {
- if (jpc.isReattach()) {
- jpc.setHover(false);
- jpc.floatingAnchorIndex = null;
- jpc.suspendedEndpoint.addConnection(jpc);
- _currentInstance.repaint(source.elementId);
- }
- else
- source.detach(jpc, false, true, true, originalEvent); // otherwise, detach the connection and tell everyone about it.
- }
-
- }
- };
-
- // wrap drop events as needed and initialise droppable
- var dropEvent = jpcl.dragEvents.drop;
- dropOptions.scope = dropOptions.scope || targetScope;
- dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], _drop);
- jpcl.initDroppable(_gel(elInfo.el), dropOptions, true);
- };
-
- // YUI collection fix
- el = _convertYUICollection(el);
- // make an array if only given one element
- var inputs = el.length && el.constructor != String ? el : [ el ];
-
- // register each one in the list.
- for (var i = 0, ii = inputs.length; i < ii; i++) {
- _doOne(inputs[i]);
- }
-
- return _currentInstance;
- };
-
- // see api docs
- this.unmakeTarget = function(el, doNotClearArrays) {
- var info = _info(el);
-
- jsPlumb.CurrentLibrary.destroyDroppable(info.el);
- // TODO this is not an exhaustive unmake of a target, since it does not remove the droppable stuff from
- // the element. the effect will be to prevent it from behaving as a target, but it's not completely purged.
- if (!doNotClearArrays) {
- delete _targetEndpointDefinitions[info.id];
- delete _targetEndpointsUnique[info.id];
- delete _targetMaxConnections[info.id];
- delete _targetsEnabled[info.id];
- }
-
- return _currentInstance;
- };
-
- // see api docs
- this.makeSource = function(el, params, referenceParams) {
- var p = jsPlumb.extend({}, referenceParams);
- jsPlumb.extend(p, params);
- _setEndpointPaintStylesAndAnchor(p, 0);
- var jpcl = jsPlumb.CurrentLibrary,
- maxConnections = p.maxConnections || -1,
- onMaxConnections = p.onMaxConnections,
- _doOne = function(elInfo) {
- // get the element's id and store the endpoint definition for it. jsPlumb.connect calls will look for one of these,
- // and use the endpoint definition if found.
- var elid = elInfo.id,
- _el = _gel(elInfo.el),
- parentElement = function() {
- return p.parent == null ? null : p.parent === "parent" ? elInfo.el.parentNode : _dom(p.parent);
- },
- idToRegisterAgainst = p.parent != null ? _currentInstance.getId(parentElement()) : elid;
-
- _sourceEndpointDefinitions[idToRegisterAgainst] = p;
- _sourceEndpointsUnique[idToRegisterAgainst] = p.uniqueEndpoint;
- _sourcesEnabled[idToRegisterAgainst] = true;
-
- var stopEvent = jpcl.dragEvents.stop,
- dragEvent = jpcl.dragEvents.drag,
- dragOptions = jsPlumb.extend({ }, p.dragOptions || {}),
- existingDrag = dragOptions.drag,
- existingStop = dragOptions.stop,
- ep = null,
- endpointAddedButNoDragYet = false;
-
- _sourceMaxConnections[idToRegisterAgainst] = maxConnections;
-
- // set scope if its not set in dragOptions but was passed in in params
- dragOptions.scope = dragOptions.scope || p.scope;
-
- dragOptions[dragEvent] = _ju.wrap(dragOptions[dragEvent], function() {
- if (existingDrag) existingDrag.apply(this, arguments);
- endpointAddedButNoDragYet = false;
- });
-
- dragOptions[stopEvent] = _ju.wrap(dragOptions[stopEvent], function() {
-
- if (existingStop) existingStop.apply(this, arguments);
- _currentInstance.currentlyDragging = false;
- if (ep._jsPlumb != null) { // if not cleaned up...
- // reset the anchor to the anchor that was initially provided. the one we were using to drag
- // the connection was just a placeholder that was located at the place the user pressed the
- // mouse button to initiate the drag.
- var anchorDef = p.anchor || _currentInstance.Defaults.Anchor,
- oldAnchor = ep.anchor,
- oldConnection = ep.connections[0];
-
- ep.setAnchor(_currentInstance.makeAnchor(anchorDef, elid, _currentInstance), true);
-
- if (p.parent) {
- var parent = parentElement();
- if (parent) {
- var currentId = ep.elementId,
- potentialParent = p.container || _currentInstance.Defaults.Container || jsPlumb.Defaults.Container;
-
- ep.setElement(parent, potentialParent);
- ep.endpointWillMoveAfterConnection = false;
- //_currentInstance.anchorManager.rehomeEndpoint(ep, currentId, parent);
- oldConnection.previousConnection = null;
- // remove from connectionsByScope
- jsPlumbUtil.removeWithFunction(connections, function(c) {
- return c.id === oldConnection.id;
- });
- _currentInstance.anchorManager.connectionDetached({
- sourceId:oldConnection.sourceId,
- targetId:oldConnection.targetId,
- connection:oldConnection
- });
- _finaliseConnection(oldConnection);
- }
- }
-
- ep.repaint();
- _currentInstance.repaint(ep.elementId);
- _currentInstance.repaint(oldConnection.targetId);
- }
- });
- // when the user presses the mouse, add an Endpoint, if we are enabled.
- var mouseDownListener = function(e) {
-
- // if disabled, return.
- if (!_sourcesEnabled[idToRegisterAgainst]) return;
-
- // if a filter was given, run it, and return if it says no.
- if (p.filter) {
- var evt = jpcl.getOriginalEvent(e),
- r = jsPlumbUtil.isString(p.filter) ? selectorFilter(evt, _el, p.filter) : p.filter(evt, _el);
-
- if (r === false) return;
- }
-
- // if maxConnections reached
- var sourceCount = _currentInstance.select({source:idToRegisterAgainst}).length;
- if (_sourceMaxConnections[idToRegisterAgainst] >= 0 && sourceCount >= _sourceMaxConnections[idToRegisterAgainst]) {
- if (onMaxConnections) {
- onMaxConnections({
- element:_el,
- maxConnections:maxConnections
- }, e);
- }
- return false;
- }
-
- // make sure we have the latest offset for this div
- var myOffsetInfo = _updateOffset({elId:elid}).o,
- z = _currentInstance.getZoom(),
- x = ( ((e.pageX || e.page.x) / z) - myOffsetInfo.left) / myOffsetInfo.width,
- y = ( ((e.pageY || e.page.y) / z) - myOffsetInfo.top) / myOffsetInfo.height,
- parentX = x,
- parentY = y;
-
- // if there is a parent, the endpoint will actually be added to it now, rather than the div
- // that was the source. in that case, we have to adjust the anchor position so it refers to
- // the parent.
- if (p.parent) {
- var pEl = parentElement(), pId = _getId(pEl);
- myOffsetInfo = _updateOffset({elId:pId}).o;
- parentX = ((e.pageX || e.page.x) - myOffsetInfo.left) / myOffsetInfo.width;
- parentY = ((e.pageY || e.page.y) - myOffsetInfo.top) / myOffsetInfo.height;
- }
-
- // we need to override the anchor in here, and force 'isSource', but we don't want to mess with
- // the params passed in, because after a connection is established we're going to reset the endpoint
- // to have the anchor we were given.
- var tempEndpointParams = {};
- jsPlumb.extend(tempEndpointParams, p);
- tempEndpointParams.isSource = true;
- tempEndpointParams.anchor = [x,y,0,0];
- tempEndpointParams.parentAnchor = [ parentX, parentY, 0, 0 ];
- tempEndpointParams.dragOptions = dragOptions;
- // if a parent was given we need to turn that into a "container" argument. this is, by default,
- // the parent of the element we will move to, so parent of p.parent in this case. however, if
- // the user has specified a 'container' on the endpoint definition or on
- // the defaults, we should use that.
- if (p.parent) {
- var potentialParent = tempEndpointParams.container || _currentInstance.Defaults.Container || jsPlumb.Defaults.Container;
- if (potentialParent)
- tempEndpointParams.container = potentialParent;
- else
- tempEndpointParams.container = jsPlumb.CurrentLibrary.getParent(parentElement());
- }
-
- ep = _currentInstance.addEndpoint(elid, tempEndpointParams);
-
- endpointAddedButNoDragYet = true;
- // we set this to prevent connections from firing attach events before this function has had a chance
- // to move the endpoint.
- ep.endpointWillMoveAfterConnection = p.parent != null;
- ep.endpointWillMoveTo = p.parent ? parentElement() : null;
-
- // TODO test options to makeSource to see if we should do this?
- ep._doNotDeleteOnDetach = false; // reset.
- ep._deleteOnDetach = true;
-
- var _delTempEndpoint = function() {
- // this mouseup event is fired only if no dragging occurred, by jquery and yui, but for mootools
- // it is fired even if dragging has occurred, in which case we would blow away a perfectly
- // legitimate endpoint, were it not for this check. the flag is set after adding an
- // endpoint and cleared in a drag listener we set in the dragOptions above.
- if(endpointAddedButNoDragYet) {
- endpointAddedButNoDragYet = false;
- _currentInstance.deleteEndpoint(ep);
- }
- };
-
- _currentInstance.registerListener(ep.canvas, "mouseup", _delTempEndpoint);
- _currentInstance.registerListener(_el, "mouseup", _delTempEndpoint);
-
- // and then trigger its mousedown event, which will kick off a drag, which will start dragging
- // a new connection from this endpoint.
- jpcl.trigger(ep.canvas, "mousedown", e);
-
- };
-
- // register this on jsPlumb so that it can be cleared by a reset.
- _currentInstance.registerListener(_el, "mousedown", mouseDownListener);
- _sourceTriggers[elid] = mouseDownListener;
-
- // lastly, if a filter was provided, set it as a dragFilter on the element,
- // to prevent the element drag function from kicking in when we want to
- // drag a new connection
- if (p.filter && jsPlumbUtil.isString(p.filter)) {
- jpcl.setDragFilter(_el, p.filter);
- }
- };
-
- el = _convertYUICollection(el);
-
- var inputs = el.length && el.constructor != String ? el : [ el ];
-
- for (var i = 0, ii = inputs.length; i < ii; i++) {
- _doOne(_info(inputs[i]));
- }
-
- return _currentInstance;
- };
-
- // see api docs
- this.unmakeSource = function(el, doNotClearArrays) {
- var info = _info(el),
- mouseDownListener = _sourceTriggers[info.id];
-
- if (mouseDownListener)
- _currentInstance.unregisterListener(info.el, "mousedown", mouseDownListener);
-
- if (!doNotClearArrays) {
- delete _sourceEndpointDefinitions[info.id];
- delete _sourceEndpointsUnique[info.id];
- delete _sourcesEnabled[info.id];
- delete _sourceTriggers[info.id];
- delete _sourceMaxConnections[info.id];
- }
-
- return _currentInstance;
- };
-
- // see api docs
- this.unmakeEverySource = function() {
- for (var i in _sourcesEnabled)
- _currentInstance.unmakeSource(i, true);
-
- _sourceEndpointDefinitions = {};
- _sourceEndpointsUnique = {};
- _sourcesEnabled = {};
- _sourceTriggers = {};
- };
-
- // see api docs
- this.unmakeEveryTarget = function() {
- for (var i in _targetsEnabled)
- _currentInstance.unmakeTarget(i, true);
-
- _targetEndpointDefinitions = {};
- _targetEndpointsUnique = {};
- _targetMaxConnections = {};
- _targetsEnabled = {};
-
- return _currentInstance;
- };
-
- // does the work of setting a source enabled or disabled.
- var _setEnabled = function(type, el, state, toggle) {
- var a = type == "source" ? _sourcesEnabled : _targetsEnabled;
- el = _convertYUICollection(el);
-
- if (_ju.isString(el)) a[el] = toggle ? !a[el] : state;
- else if (el.length) {
- for (var i = 0, ii = el.length; i < ii; i++) {
- var info = _info(el[i]);
- a[info.id] = toggle ? !a[info.id] : state;
- }
- }
- return _currentInstance;
- };
-
- this.toggleSourceEnabled = function(el) {
- _setEnabled("source", el, null, true);
- return _currentInstance.isSourceEnabled(el);
- };
-
- this.setSourceEnabled = function(el, state) { return _setEnabled("source", el, state); };
- this.isSource = function(el) { return _sourcesEnabled[_info(el).id] != null; };
- this.isSourceEnabled = function(el) { return _sourcesEnabled[_info(el).id] === true; };
-
- this.toggleTargetEnabled = function(el) {
- _setEnabled("target", el, null, true);
- return _currentInstance.isTargetEnabled(el);
- };
-
- this.isTarget = function(el) { return _targetsEnabled[_info(el).id] != null; };
- this.isTargetEnabled = function(el) { return _targetsEnabled[_info(el).id] === true; };
- this.setTargetEnabled = function(el, state) { return _setEnabled("target", el, state); };
-
-// --------------------- end makeSource/makeTarget ----------------------------------------------
-
- this.ready = function(fn) {
- _currentInstance.bind("ready", fn);
- };
-
- // repaint some element's endpoints and connections
- this.repaint = function(el, ui, timestamp) {
- // support both lists...
- if (typeof el == 'object' && el.length)
- for ( var i = 0, ii = el.length; i < ii; i++) {
- _draw(el[i], ui, timestamp);
- }
- else // ...and single strings.
- _draw(el, ui, timestamp);
-
- return _currentInstance;
- };
-
- // repaint every endpoint and connection.
- this.repaintEverything = function() {
- // TODO this timestamp causes continuous anchors to not repaint properly.
- // fix this. do not just take out the timestamp. it runs a lot faster with
- // the timestamp included.
- var timestamp = _timestamp();
- for ( var elId in endpointsByElement) {
- _draw(elId, null, timestamp);
- }
- return _currentInstance;
- /*for (var i = 0; i < connections.length; i++)
- connections[i].repaint({timestamp:timestamp});*/
- };
-
-
- this.removeAllEndpoints = function(el, recurse) {
- var _one = function(_el) {
- var info = _info(_el),
- ebe = endpointsByElement[info.id],
- i, ii;
-
- if (ebe) {
- for ( i = 0, ii = ebe.length; i < ii; i++)
- _currentInstance.deleteEndpoint(ebe[i]);
- }
- delete endpointsByElement[info.id];
-
- if (recurse) {
- if (info.el && info.el.nodeType != 3 && info.el.nodeType != 8 ) {
- for ( i = 0, ii = info.el.childNodes.length; i < ii; i++) {
- _one(info.el.childNodes[i]);
- }
- }
- }
-
- };
- _one(el);
- return _currentInstance;
- };
-
- /**
- * Remove the given element, including cleaning up all endpoints registered for it.
- * This is exposed in the public API but also used internally by jsPlumb when removing the
- * element associated with a connection drag.
- */
- this.remove = function(el, doNotRepaint) {
- var info = _info(el);
- _currentInstance.doWhileSuspended(function() {
- _currentInstance.removeAllEndpoints(info.id, true);
- _currentInstance.dragManager.elementRemoved(info.id);
- delete floatingConnections[info.id];
- _currentInstance.anchorManager.clearFor(info.id);
- _currentInstance.anchorManager.removeFloatingConnection(info.id);
- }, doNotRepaint === false);
- if(info.el) jsPlumb.CurrentLibrary.removeElement(info.el);
- };
-
- var _registeredListeners = {},
- _unbindRegisteredListeners = function() {
- for (var i in _registeredListeners) {
- for (var j = 0, jj = _registeredListeners[i].length; j < jj; j++) {
- var info = _registeredListeners[i][j];
- jsPlumb.CurrentLibrary.unbind(info.el, info.event, info.listener);
- }
- }
- _registeredListeners = {};
- };
-
- // internal register listener method. gives us a hook to clean things up
- // with if the user calls jsPlumb.reset.
- this.registerListener = function(el, type, listener) {
- jsPlumb.CurrentLibrary.bind(el, type, listener);
- jsPlumbUtil.addToList(_registeredListeners, type, {el:el, event:type, listener:listener});
- };
-
- this.unregisterListener = function(el, type, listener) {
- jsPlumb.CurrentLibrary.unbind(el, type, listener);
- jsPlumbUtil.removeWithFunction(_registeredListeners, function(rl) {
- return rl.type == type && rl.listener == listener;
- });
- };
-
- this.reset = function() {
- _currentInstance.deleteEveryEndpoint();
- _currentInstance.unbind();
- _targetEndpointDefinitions = {};
- _targetEndpoints = {};
- _targetEndpointsUnique = {};
- _targetMaxConnections = {};
- _sourceEndpointDefinitions = {};
- _sourceEndpoints = {};
- _sourceEndpointsUnique = {};
- _sourceMaxConnections = {};
- connections.splice(0);
- _unbindRegisteredListeners();
- _currentInstance.anchorManager.reset();
- if (!jsPlumbAdapter.headless)
- _currentInstance.dragManager.reset();
- };
-
-
- this.setDefaultScope = function(scope) {
- DEFAULT_SCOPE = scope;
- return _currentInstance;
- };
-
- // sets whether or not some element should be currently draggable.
- this.setDraggable = _setDraggable;
-
- // sets the id of some element, changing whatever we need to to keep track.
- this.setId = function(el, newId, doNotSetAttribute) {
- //
- var id;
-
- if (jsPlumbUtil.isString(el)) {
- id = el;
- }
- else {
- el = _dom(el);
- id = _currentInstance.getId(el);
- }
-
- var sConns = _currentInstance.getConnections({source:id, scope:'*'}, true),
- tConns = _currentInstance.getConnections({target:id, scope:'*'}, true);
-
- newId = "" + newId;
-
- if (!doNotSetAttribute) {
- el = _dom(id);
- jsPlumbAdapter.setAttribute(el, "id", newId);
- }
- else
- el = _dom(newId);
-
- endpointsByElement[newId] = endpointsByElement[id] || [];
- for (var i = 0, ii = endpointsByElement[newId].length; i < ii; i++) {
- endpointsByElement[newId][i].setElementId(newId);
- endpointsByElement[newId][i].setReferenceElement(el);
- }
- delete endpointsByElement[id];
-
- _currentInstance.anchorManager.changeId(id, newId);
- if (!jsPlumbAdapter.headless)
- _currentInstance.dragManager.changeId(id, newId);
-
- var _conns = function(list, epIdx, type) {
- for (var i = 0, ii = list.length; i < ii; i++) {
- list[i].endpoints[epIdx].setElementId(newId);
- list[i].endpoints[epIdx].setReferenceElement(el);
- list[i][type + "Id"] = newId;
- list[i][type] = el;
- }
- };
- _conns(sConns, 0, "source");
- _conns(tConns, 1, "target");
-
- _currentInstance.repaint(newId);
- };
-
- this.setDebugLog = function(debugLog) {
- log = debugLog;
- };
-
- this.setSuspendDrawing = function(val, repaintAfterwards) {
- var curVal = _suspendDrawing;
- _suspendDrawing = val;
- if (val) _suspendedAt = new Date().getTime(); else _suspendedAt = null;
- if (repaintAfterwards) _currentInstance.repaintEverything();
- return curVal;
- };
-
- // returns whether or not drawing is currently suspended.
- this.isSuspendDrawing = function() {
- return _suspendDrawing;
- };
-
- // return timestamp for when drawing was suspended.
- this.getSuspendedAt = function() { return _suspendedAt; };
-
- /**
- * @doc function
- * @name jsPlumb.class:doWhileSuspended
- * @param {function} fn Function to run while suspended.
- * @param {boolean} doNotRepaintAfterwards If true, jsPlumb won't run a full repaint. Otherwise it will.
- * @description Suspends drawing, runs the given function, then re-enables drawing (and repaints, unless you tell it not to)
- */
- this.doWhileSuspended = function(fn, doNotRepaintAfterwards) {
- var _wasSuspended = _currentInstance.isSuspendDrawing();
- if (!_wasSuspended)
- _currentInstance.setSuspendDrawing(true);
- try {
- fn();
- }
- catch (e) {
- _ju.log("Function run while suspended failed", e);
- }
- if (!_wasSuspended)
- _currentInstance.setSuspendDrawing(false, !doNotRepaintAfterwards);
- };
-
- this.updateOffset = _updateOffset;
- this.getOffset = function(elId) { return offsets[elId]; };
- this.getSize = function(elId) { return sizes[elId]; };
- this.getCachedData = _getCachedData;
- this.timestamp = _timestamp;
-
-
-
- /**
- * @doc function
- * @name jsPlumb.class:setRenderMode
- * @param {string} mode One of `jsPlumb.SVG, `jsPlumb.VML` or `jsPlumb.CANVAS`.
- * @description Sets render mode. jsPlumb will fall back to VML if it determines that
- * what you asked for is not supported (and that VML is). If you asked for VML but the browser does
- * not support it, jsPlumb uses SVG.
- * @return {string} The render mode that jsPlumb set, which of course may be different from that requested.
- */
- this.setRenderMode = function(mode) {
- renderMode = jsPlumbAdapter.setRenderMode(mode);
- var i, ii;
- // only add this if the renderer is canvas; we dont want these listeners registered on te
- // entire document otherwise.
- if (renderMode == jsPlumb.CANVAS) {
- var bindOne = function(event) {
- jsPlumb.CurrentLibrary.bind(document, event, function(e) {
- if (!_currentInstance.currentlyDragging && renderMode == jsPlumb.CANVAS) {
- // try connections first
- for (i = 0, ii = connections.length; i < ii; i++ ) {
- var t = connections[i].getConnector()[event](e);
- if (t) return;
- }
- for (var el in endpointsByElement) {
- var ee = endpointsByElement[el];
- for ( i = 0, ii = ee.length; i < ii; i++ ) {
- if (ee[i].endpoint[event] && ee[i].endpoint[event](e)) return;
- }
- }
- }
- });
- };
- bindOne("click");bindOne("dblclick");bindOne("mousemove");bindOne("mousedown");bindOne("mouseup");bindOne("contextmenu");
- }
-
- return renderMode;
- };
-
- /**
- * @doc function
- * @name jsPlumb.class:getRenderMode
- * @description Gets the current render mode for this instance of jsPlumb.
- * @return {string} The current render mode - "canvas", "svg" or "vml".
- */
- this.getRenderMode = function() { return renderMode; };
-
- this.show = function(el, changeEndpoints) {
- _setVisible(el, "block", changeEndpoints);
- return _currentInstance;
- };
-
- /**
- * gets some test hooks. nothing writable.
- */
- this.getTestHarness = function() {
- return {
- endpointsByElement : endpointsByElement,
- endpointCount : function(elId) {
- var e = endpointsByElement[elId];
- return e ? e.length : 0;
- },
- connectionCount : function(scope) {
- scope = scope || DEFAULT_SCOPE;
- var c = _currentInstance.getConnections({scope:scope});
- return c ? c.length : 0;
- },
- getId : _getId,
- makeAnchor:self.makeAnchor,
- makeDynamicAnchor:self.makeDynamicAnchor
- };
- };
-
-
- // TODO: update this method to return the current state.
- this.toggleVisible = _toggleVisible;
- this.toggleDraggable = _toggleDraggable;
- this.addListener = this.bind;
-
- /*
- helper method to take an xy location and adjust it for the parent's offset and scroll.
- */
- this.adjustForParentOffsetAndScroll = function(xy, el) {
-
- var offsetParent = null, result = xy;
- if (el.tagName.toLowerCase() === "svg" && el.parentNode) {
- offsetParent = el.parentNode;
- }
- else if (el.offsetParent) {
- offsetParent = el.offsetParent;
- }
- if (offsetParent != null) {
- var po = offsetParent.tagName.toLowerCase() === "body" ? {left:0,top:0} : _getOffset(offsetParent, _currentInstance),
- so = offsetParent.tagName.toLowerCase() === "body" ? {left:0,top:0} : {left:offsetParent.scrollLeft, top:offsetParent.scrollTop};
-
- // i thought it might be cool to do this:
- // lastReturnValue[0] = lastReturnValue[0] - offsetParent.offsetLeft + offsetParent.scrollLeft;
- // lastReturnValue[1] = lastReturnValue[1] - offsetParent.offsetTop + offsetParent.scrollTop;
- // but i think it ignores margins. my reasoning was that it's quicker to not hand off to some underlying
- // library.
-
- result[0] = xy[0] - po.left + so.left;
- result[1] = xy[1] - po.top + so.top;
- }
-
- return result;
-
- };
-
- if (!jsPlumbAdapter.headless) {
- _currentInstance.dragManager = jsPlumbAdapter.getDragManager(_currentInstance);
- _currentInstance.recalculateOffsets = _currentInstance.dragManager.updateOffsets;
- }
-
- };
-
- jsPlumbUtil.extend(jsPlumbInstance, jsPlumbUtil.EventGenerator, {
- setAttribute : function(el, a, v) {
- jsPlumbAdapter.setAttribute(el, a, v);
- },
- getAttribute : function(el, a) {
- return jsPlumbAdapter.getAttribute(jsPlumb.CurrentLibrary.getDOMElement(el), a);
- },
- registerConnectionType : function(id, type) {
- this._connectionTypes[id] = jsPlumb.extend({}, type);
- },
- /**
- * @doc function
- * @name jsPlumb.class:registerConnectionTypes
- * @param {object} types Object containing the type specifications.
- * @description
- * Registers all of the given connection types on this instance of jsPlumb. `types` is expected
- * to contain keys with typeids and values with type specification objects.
- */
- registerConnectionTypes : function(types) {
- for (var i in types)
- this._connectionTypes[i] = jsPlumb.extend({}, types[i]);
- },
- /**
- * @doc function
- * @name jsPlumb.class:registerEndpointType
- * @param {string} typeId Id of the type
- * @param {object} type Object containing the type specification.
- * @description
- * Registers the given endpoint type on this instance of jsPlumb.
- */
- registerEndpointType : function(id, type) {
- this._endpointTypes[id] = jsPlumb.extend({}, type);
- },
- /**
- * @doc function
- * @name jsPlumb.class:registerEndpointTypes
- * @param {object} types Object containing the type specifications.
- * @description
- * Registers all of the given endpoint types on this instance of jsPlumb. `types` is expected
- * to contain keys with typeids and values with type specification objects.
- */
- registerEndpointTypes : function(types) {
- for (var i in types)
- this._endpointTypes[i] = jsPlumb.extend({}, types[i]);
- },
- /**
- * @doc function
- * @name jsPlumb.class:getType
- * @param {string} id Id of the type to retrieve
- * @param {string} typeDescriptor `"connection"` or `"endpoint"` - the type of Type to get.
- * @description
- * Returns the given type's specification.
- * @return {object} Type specification, it if exists, null otherwise.
- */
- getType : function(id, typeDescriptor) {
- return typeDescriptor === "connection" ? this._connectionTypes[id] : this._endpointTypes[id];
- },
- /**
- * @doc function
- * @name jsPlumb.class:setIdChanged
- * @param {oldId} string Previous id
- * @param {newId} string Element's new id.
- * @description called to notify us that an id WAS changed, and we should do our changes, but we
- * dont need to change the element's DOM attribute. note that this does not work if the an element with
- * the new id is not in the DOM.
- */
- setIdChanged : function(oldId, newId) {
- this.setId(oldId, newId, true);
- }
- });
-
-// --------------------- static instance + AMD registration -------------------------------------------
-
-// create static instance and assign to window if window exists.
- var jsPlumb = new jsPlumbInstance();
- // register on window if defined (lets us run on server)
- if (typeof window != 'undefined') window.jsPlumb = jsPlumb;
- // add 'getInstance' method to static instance
- /**
- * @name jsPlumb.getInstance
- * @param {object} [_defaults] Optional default settings for the new instance.
- * @desc Gets a new instance of jsPlumb.
- */
- jsPlumb.getInstance = function(_defaults) {
- var j = new jsPlumbInstance(_defaults);
- j.init();
- return j;
- };
-// maybe register static instance as an AMD module, and getInstance method too.
- if ( typeof define === "function") {
- define( "jsplumb", [], function () { return jsPlumb; } );
- define( "jsplumbinstance", [], function () { return jsPlumb.getInstance(); } );
- }
- // CommonJS
- if (typeof exports !== 'undefined') {
- exports.jsPlumb = jsPlumb;
- }
-
-
-// --------------------- end static instance + AMD registration -------------------------------------------
-
-})();
-
-
-;(function() {
-
- // create the drag handler for a connection
- var _makeConnectionDragHandler = function(placeholder, _jsPlumb) {
- var stopped = false;
- return {
- drag : function() {
- if (stopped) {
- stopped = false;
- return true;
- }
- var _ui = jsPlumb.CurrentLibrary.getUIPosition(arguments, _jsPlumb.getZoom());
-
- if (placeholder.element) {
- jsPlumb.CurrentLibrary.setOffset(placeholder.element, _ui);
- _jsPlumb.repaint(placeholder.element, _ui);
- }
- },
- stopDrag : function() {
- stopped = true;
- }
- };
- };
-
- // creates a placeholder div for dragging purposes, adds it to the DOM, and pre-computes its offset.
- var _makeDraggablePlaceholder = function(placeholder, parent, _jsPlumb) {
- var n = document.createElement("div");
- n.style.position = "absolute";
- var placeholderDragElement = jsPlumb.CurrentLibrary.getElementObject(n);
- jsPlumb.CurrentLibrary.appendElement(n, parent);
- var id = _jsPlumb.getId(n);
- _jsPlumb.updateOffset( { elId : id });
- // create and assign an id, and initialize the offset.
- placeholder.id = id;
- placeholder.element = n;//placeholderDragElement;
- };
-
- // create a floating endpoint (for drag connections)
- var _makeFloatingEndpoint = function(paintStyle, referenceAnchor, endpoint, referenceCanvas, sourceElement, _jsPlumb, _newEndpoint) {
- var floatingAnchor = new jsPlumb.FloatingAnchor( { reference : referenceAnchor, referenceCanvas : referenceCanvas, jsPlumbInstance:_jsPlumb });
- //setting the scope here should not be the way to fix that mootools issue. it should be fixed by not
- // adding the floating endpoint as a droppable. that makes more sense anyway!
- return _newEndpoint({ paintStyle : paintStyle, endpoint : endpoint, anchor : floatingAnchor, source : sourceElement, scope:"__floating" });
- };
-
- var typeParameters = [ "connectorStyle", "connectorHoverStyle", "connectorOverlays",
- "connector", "connectionType", "connectorClass", "connectorHoverClass" ];
-
- // a helper function that tries to find a connection to the given element, and returns it if so. if elementWithPrecedence is null,
- // or no connection to it is found, we return the first connection in our list.
- var findConnectionToUseForDynamicAnchor = function(ep, elementWithPrecedence) {
- var idx = 0;
- if (elementWithPrecedence != null) {
- for (var i = 0; i < ep.connections.length; i++) {
- if (ep.connections[i].sourceId == elementWithPrecedence || ep.connections[i].targetId == elementWithPrecedence) {
- idx = i;
- break;
- }
- }
- }
-
- return ep.connections[idx];
- };
-
- var findConnectionIndex = function(conn, ep) {
- return jsPlumbUtil.findWithFunction(ep.connections, function(c) { return c.id == conn.id; });
- };
-
- jsPlumb.Endpoint = function(params) {
- var _jsPlumb = params._jsPlumb,
- jpcl = jsPlumb.CurrentLibrary,
- _att = jsPlumbAdapter.getAttribute,
- _gel = jpcl.getElementObject,
- _dom = jpcl.getDOMElement,
- _ju = jsPlumbUtil,
- _newConnection = params.newConnection,
- _newEndpoint = params.newEndpoint,
- _finaliseConnection = params.finaliseConnection,
- _fireDetachEvent = params.fireDetachEvent,
- floatingConnections = params.floatingConnections;
-
- this.idPrefix = "_jsplumb_e_";
- this.defaultLabelLocation = [ 0.5, 0.5 ];
- this.defaultOverlayKeys = ["Overlays", "EndpointOverlays"];
- this.parent = params.parent;
- OverlayCapableJsPlumbUIComponent.apply(this, arguments);
-
-// TYPE
-
- this.getDefaultType = function() {
- return {
- parameters:{},
- scope:null,
- maxConnections:this._jsPlumb.instance.Defaults.MaxConnections,
- paintStyle:this._jsPlumb.instance.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle,
- endpoint:this._jsPlumb.instance.Defaults.Endpoint || jsPlumb.Defaults.Endpoint,
- hoverPaintStyle:this._jsPlumb.instance.Defaults.EndpointHoverStyle || jsPlumb.Defaults.EndpointHoverStyle,
- overlays:this._jsPlumb.instance.Defaults.EndpointOverlays || jsPlumb.Defaults.EndpointOverlays,
- connectorStyle:params.connectorStyle,
- connectorHoverStyle:params.connectorHoverStyle,
- connectorClass:params.connectorClass,
- connectorHoverClass:params.connectorHoverClass,
- connectorOverlays:params.connectorOverlays,
- connector:params.connector,
- connectorTooltip:params.connectorTooltip
- };
- };
-
-// END TYPE
-
- this._jsPlumb.enabled = !(params.enabled === false);
- this._jsPlumb.visible = true;
- this.element = _dom(params.source);
- this._jsPlumb.uuid = params.uuid;
- this._jsPlumb.floatingEndpoint = null;
- var inPlaceCopy = null;
- if (this._jsPlumb.uuid) params.endpointsByUUID[this._jsPlumb.uuid] = this;
- this.elementId = params.elementId;
-
- this._jsPlumb.connectionCost = params.connectionCost;
- this._jsPlumb.connectionsDirected = params.connectionsDirected;
- this._jsPlumb.currentAnchorClass = "";
- this._jsPlumb.events = {};
-
- var _updateAnchorClass = function() {
- jpcl.removeClass(this.element, _jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- this.removeClass(_jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- this._jsPlumb.currentAnchorClass = this.anchor.getCssClass();
- this.addClass(_jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- jpcl.addClass(this.element, _jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- }.bind(this);
-
- this.setAnchor = function(anchorParams, doNotRepaint) {
- this._jsPlumb.instance.continuousAnchorFactory.clear(this.elementId);
- this.anchor = this._jsPlumb.instance.makeAnchor(anchorParams, this.elementId, _jsPlumb);
- _updateAnchorClass();
- this.anchor.bind("anchorChanged", function(currentAnchor) {
- this.fire("anchorChanged", {endpoint:this, anchor:currentAnchor});
- _updateAnchorClass();
- }.bind(this));
- if (!doNotRepaint)
- this._jsPlumb.instance.repaint(this.elementId);
- return this;
- };
-
- var anchorParamsToUse = params.anchor ? params.anchor : params.anchors ? params.anchors : (_jsPlumb.Defaults.Anchor || "Top");
- this.setAnchor(anchorParamsToUse, true);
-
- // endpoint delegates to first connection for hover, if there is one.
- var internalHover = function(state) {
- if (this.connections.length > 0)
- this.connections[0].setHover(state, false);
- else
- this.setHover(state);
- }.bind(this);
-
- // ANCHOR MANAGER
- if (!params._transient) // in place copies, for example, are transient. they will never need to be retrieved during a paint cycle, because they dont move, and then they are deleted.
- this._jsPlumb.instance.anchorManager.add(this, this.elementId);
-
- this.setEndpoint = function(ep) {
-
- if (this.endpoint != null) {
- this.endpoint.cleanup();
- this.endpoint.destroy();
- }
-
- var _e = function(t, p) {
- var rm = _jsPlumb.getRenderMode();
- if (jsPlumb.Endpoints[rm][t]) return new jsPlumb.Endpoints[rm][t](p);
- if (!_jsPlumb.Defaults.DoNotThrowErrors)
- throw { msg:"jsPlumb: unknown endpoint type '" + t + "'" };
- };
-
- var endpointArgs = {
- _jsPlumb:this._jsPlumb.instance,
- cssClass:params.cssClass,
- parent:params.parent,
- container:params.container,
- tooltip:params.tooltip,
- connectorTooltip:params.connectorTooltip,
- endpoint:this
- };
- if (_ju.isString(ep))
- this.endpoint = _e(ep, endpointArgs);
- else if (_ju.isArray(ep)) {
- endpointArgs = _ju.merge(ep[1], endpointArgs);
- this.endpoint = _e(ep[0], endpointArgs);
- }
- else {
- this.endpoint = ep.clone();
- }
-
- // assign a clone function using a copy of endpointArgs. this is used when a drag starts: the endpoint that was dragged is cloned,
- // and the clone is left in its place while the original one goes off on a magical journey.
- // the copy is to get around a closure problem, in which endpointArgs ends up getting shared by
- // the whole world.
- var argsForClone = jsPlumb.extend({}, endpointArgs);
- this.endpoint.clone = function() {
- // TODO this, and the code above, can be refactored to be more dry.
- if (_ju.isString(ep))
- return _e(ep, endpointArgs);
- else if (_ju.isArray(ep)) {
- endpointArgs = _ju.merge(ep[1], endpointArgs);
- return _e(ep[0], endpointArgs);
- }
- }.bind(this);
-
- this.type = this.endpoint.type;
- // bind listeners from endpoint to self, with the internal hover function defined above.
- this.bindListeners(this.endpoint, this, internalHover);
- };
-
- this.setEndpoint(params.endpoint || _jsPlumb.Defaults.Endpoint || jsPlumb.Defaults.Endpoint || "Dot");
- this.setPaintStyle(params.paintStyle || params.style || _jsPlumb.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle, true);
- this.setHoverPaintStyle(params.hoverPaintStyle || _jsPlumb.Defaults.EndpointHoverStyle || jsPlumb.Defaults.EndpointHoverStyle, true);
- this._jsPlumb.paintStyleInUse = this.getPaintStyle();
-
- _ju.copyValues(typeParameters, params, this);
-
- this.isSource = params.isSource || false;
- this.isTarget = params.isTarget || false;
- this._jsPlumb.maxConnections = params.maxConnections || _jsPlumb.Defaults.MaxConnections; // maximum number of connections this endpoint can be the source of.
- this.canvas = this.endpoint.canvas;
- // add anchor class (need to do this on construction because we set anchor first)
- this.addClass(_jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- jpcl.addClass(this.element, _jsPlumb.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- this.connections = params.connections || [];
- this.connectorPointerEvents = params["connector-pointer-events"];
-
- this.scope = params.scope || _jsPlumb.getDefaultScope();
- this.timestamp = null;
- this.reattachConnections = params.reattach || _jsPlumb.Defaults.ReattachConnections;
- this.connectionsDetachable = _jsPlumb.Defaults.ConnectionsDetachable;
- if (params.connectionsDetachable === false || params.detachable === false)
- this.connectionsDetachable = false;
- this.dragAllowedWhenFull = params.dragAllowedWhenFull || true;
-
- if (params.onMaxConnections)
- this.bind("maxConnections", params.onMaxConnections);
-
- //
- // add a connection. not part of public API.
- //
- this.addConnection = function(connection) {
- this.connections.push(connection);
- this[(this.connections.length > 0 ? "add" : "remove") + "Class"](_jsPlumb.endpointConnectedClass);
- this[(this.isFull() ? "add" : "remove") + "Class"](_jsPlumb.endpointFullClass);
- };
-
- this.detachFromConnection = function(connection, idx) {
- idx = idx == null ? findConnectionIndex(connection, this) : idx;
- if (idx >= 0) {
- this.connections.splice(idx, 1);
- this[(this.connections.length > 0 ? "add" : "remove") + "Class"](_jsPlumb.endpointConnectedClass);
- this[(this.isFull() ? "add" : "remove") + "Class"](_jsPlumb.endpointFullClass);
- }
- };
-
- this.detach = function(connection, ignoreTarget, forceDetach, fireEvent, originalEvent, endpointBeingDeleted, connectionIndex) {
-
- var idx = connectionIndex == null ? findConnectionIndex(connection, this) : connectionIndex,
- actuallyDetached = false;
- fireEvent = (fireEvent !== false);
-
- if (idx >= 0) {
- if (forceDetach || connection._forceDetach || (connection.isDetachable() && connection.isDetachAllowed(connection) && this.isDetachAllowed(connection) )) {
-
- //connection.setHover(false);
-
- _jsPlumb.deleteObject({
- connection:connection,
- fireEvent:(!ignoreTarget && fireEvent),
- originalEvent:originalEvent
- });
- actuallyDetached = true;
- }
- }
- return actuallyDetached;
- };
-
- this.detachAll = function(fireEvent, originalEvent) {
- while (this.connections.length > 0) {
- // TODO this could pass the index in to the detach method to save some time (index will always be zero in this while loop)
- // TODO now it defaults to fireEvent true. will that mess with things?
- this.detach(this.connections[0], false, true, fireEvent !== false, originalEvent, this, 0);
- }
- return this;
- };
- this.detachFrom = function(targetEndpoint, fireEvent, originalEvent) {
- var c = [];
- for ( var i = 0; i < this.connections.length; i++) {
- if (this.connections[i].endpoints[1] == targetEndpoint || this.connections[i].endpoints[0] == targetEndpoint) {
- c.push(this.connections[i]);
- }
- }
- for ( var j = 0; j < c.length; j++) {
- this.detach(c[j], false, true, fireEvent, originalEvent);
- }
- return this;
- };
-
- this.getElement = function() {
- return this.element;
- };
-
- // container not supported in 1.5.2; you cannot change the container once it is set.
- // it might come back int a future release.
- this.setElement = function(el/*, container*/) {
- var parentId = this._jsPlumb.instance.getId(el),
- curId = this.elementId;
- // remove the endpoint from the list for the current endpoint's element
- _ju.removeWithFunction(params.endpointsByElement[this.elementId], function(e) {
- return e.id == this.id;
- }.bind(this));
- this.element = _dom(el);
- this.elementId = _jsPlumb.getId(this.element);
- _jsPlumb.anchorManager.rehomeEndpoint(this, curId, this.element);
- _jsPlumb.dragManager.endpointAdded(this.element);
- _ju.addToList(params.endpointsByElement, parentId, this);
- return this;
- };
-
- /**
- * private but must be exposed.
- */
- this.makeInPlaceCopy = function() {
- var loc = this.anchor.getCurrentLocation({element:this}),
- o = this.anchor.getOrientation(this),
- acc = this.anchor.getCssClass(),
- inPlaceAnchor = {
- bind:function() { },
- compute:function() { return [ loc[0], loc[1] ]; },
- getCurrentLocation : function() { return [ loc[0], loc[1] ]; },
- getOrientation:function() { return o; },
- getCssClass:function() { return acc; }
- };
-
- return _newEndpoint( {
- anchor : inPlaceAnchor,
- source : this.element,
- paintStyle : this.getPaintStyle(),
- endpoint : params.hideOnDrag ? "Blank" : this.endpoint,
- _transient:true,
- scope:this.scope
- });
- };
-
-
- /**
- * private but needs to be exposed.
- */
- this.isFloating = function() {
- return this.anchor != null && this.anchor.isFloating;
- };
-
- /**
- * returns a connection from the pool; used when dragging starts. just gets the head of the array if it can.
- */
- this.connectorSelector = function() {
- var candidate = this.connections[0];
- if (this.isTarget && candidate) return candidate;
- else {
- return (this.connections.length < this._jsPlumb.maxConnections) || this._jsPlumb.maxConnections == -1 ? null : candidate;
- }
- };
-
- this.setStyle = this.setPaintStyle;
-
- this.paint = function(params) {
- params = params || {};
- var timestamp = params.timestamp, recalc = !(params.recalc === false);
- if (!timestamp || this.timestamp !== timestamp) {
-
- // TODO check: is this is a safe performance enhancement?
- var info = _jsPlumb.updateOffset({ elId:this.elementId, timestamp:timestamp/*, recalc:recalc*/ });
- //var info = _jsPlumb.updateOffset({ elId:_elementId, timestamp:timestamp, recalc:recalc });
-
- var xy = params.offset ? params.offset.o : info.o;
- if(xy != null) {
- var ap = params.anchorPoint,connectorPaintStyle = params.connectorPaintStyle;
- if (ap == null) {
- var wh = params.dimensions || info.s,
- anchorParams = { xy : [ xy.left, xy.top ], wh : wh, element : this, timestamp : timestamp };
- if (recalc && this.anchor.isDynamic && this.connections.length > 0) {
- var c = findConnectionToUseForDynamicAnchor(this, params.elementWithPrecedence),
- oIdx = c.endpoints[0] == this ? 1 : 0,
- oId = oIdx === 0 ? c.sourceId : c.targetId,
- oInfo = _jsPlumb.getCachedData(oId),
- oOffset = oInfo.o, oWH = oInfo.s;
- anchorParams.txy = [ oOffset.left, oOffset.top ];
- anchorParams.twh = oWH;
- anchorParams.tElement = c.endpoints[oIdx];
- }
- ap = this.anchor.compute(anchorParams);
- }
-
- this.endpoint.compute(ap, this.anchor.getOrientation(this), this._jsPlumb.paintStyleInUse, connectorPaintStyle || this.paintStyleInUse);
- this.endpoint.paint(this._jsPlumb.paintStyleInUse, this.anchor);
- this.timestamp = timestamp;
-
- // paint overlays
- for ( var i = 0; i < this._jsPlumb.overlays.length; i++) {
- var o = this._jsPlumb.overlays[i];
- if (o.isVisible()) {
- this._jsPlumb.overlayPlacements[i] = o.draw(this.endpoint, this._jsPlumb.paintStyleInUse);
- o.paint(this._jsPlumb.overlayPlacements[i]);
- }
- }
- }
- }
- };
-
- this.repaint = this.paint;
-
- // is this a connection source? we make it draggable and have the
- // drag listener maintain a connection with a floating endpoint.
- if (jpcl.isDragSupported(this.element) && (this.isSource || this.isTarget)) {
- var placeholderInfo = { id:null, element:null },
- jpc = null,
- existingJpc = false,
- existingJpcParams = null,
- _dragHandler = _makeConnectionDragHandler(placeholderInfo, _jsPlumb);
-
- var start = function() {
- // drag might have started on an endpoint that is not actually a source, but which has
- // one or more connections.
- jpc = this.connectorSelector();
- var _continue = true;
- // if not enabled, return
- if (!this.isEnabled()) _continue = false;
- // if no connection and we're not a source, return.
- if (jpc == null && !this.isSource) _continue = false;
- // otherwise if we're full and not allowed to drag, also return false.
- if (this.isSource && this.isFull() && !this.dragAllowedWhenFull) _continue = false;
- // if the connection was setup as not detachable or one of its endpoints
- // was setup as connectionsDetachable = false, or Defaults.ConnectionsDetachable
- // is set to false...
- if (jpc != null && !jpc.isDetachable()) _continue = false;
-
- if (_continue === false) {
- // this is for mootools and yui. returning false from this causes jquery to stop drag.
- // the events are wrapped in both mootools and yui anyway, but i don't think returning
- // false from the start callback would stop a drag.
- if (jpcl.stopDrag) jpcl.stopDrag();
- _dragHandler.stopDrag();
- return false;
- }
-
- // clear hover for all connections for this endpoint before continuing.
- for (var i = 0; i < this.connections.length; i++)
- this.connections[i].setHover(false);
-
- this.addClass("endpointDrag");
- _jsPlumb.setConnectionBeingDragged(true);
-
- // if we're not full but there was a connection, make it null. we'll create a new one.
- if (jpc && !this.isFull() && this.isSource) jpc = null;
-
- _jsPlumb.updateOffset( { elId : this.elementId });
- inPlaceCopy = this.makeInPlaceCopy();
- inPlaceCopy.referenceEndpoint = this;
- inPlaceCopy.paint();
-
- _makeDraggablePlaceholder(placeholderInfo, this.parent, _jsPlumb);
-
- // set the offset of this div to be where 'inPlaceCopy' is, to start with.
- // TODO merge this code with the code in both Anchor and FloatingAnchor, because it
- // does the same stuff.
- var ipcoel = _gel(inPlaceCopy.canvas),
- ipco = jsPlumb.CurrentLibrary.getOffset(ipcoel, _jsPlumb),
- po = _jsPlumb.adjustForParentOffsetAndScroll([ipco.left, ipco.top], inPlaceCopy.canvas),
- canvasElement = _gel(this.canvas);
-
- jpcl.setOffset(placeholderInfo.element, {left:po[0], top:po[1]});
-
- // when using makeSource and a parent, we first draw the source anchor on the source element, then
- // move it to the parent. note that this happens after drawing the placeholder for the
- // first time.
- if (this.parentAnchor) this.anchor = _jsPlumb.makeAnchor(this.parentAnchor, this.elementId, _jsPlumb);
-
- // store the id of the dragging div and the source element. the drop function will pick these up.
- _jsPlumb.setAttribute(this.canvas, "dragId", placeholderInfo.id);
- _jsPlumb.setAttribute(this.canvas, "elId", this.elementId);
-
- this._jsPlumb.floatingEndpoint = _makeFloatingEndpoint(this.getPaintStyle(), this.anchor, this.endpoint, this.canvas, placeholderInfo.element, _jsPlumb, _newEndpoint);
- // TODO we should not know about DOM here. make the library adapter do this (or the
- // dom adapter)
- this.canvas.style.visibility = "hidden";
-
- if (jpc == null) {
- this.anchor.locked = true;
- this.setHover(false, false);
- // create a connection. one end is this endpoint, the other is a floating endpoint.
- jpc = _newConnection({
- sourceEndpoint : this,
- targetEndpoint : this._jsPlumb.floatingEndpoint,
- source : this.endpointWillMoveTo || this.element, // for makeSource with parent option. ensure source element is represented correctly.
- target : placeholderInfo.element,
- anchors : [ this.anchor, this._jsPlumb.floatingEndpoint.anchor ],
- paintStyle : params.connectorStyle, // this can be null. Connection will use the default.
- hoverPaintStyle:params.connectorHoverStyle,
- connector : params.connector, // this can also be null. Connection will use the default.
- overlays : params.connectorOverlays,
- type:this.connectionType,
- cssClass:this.connectorClass,
- hoverClass:this.connectorHoverClass
- });
- jpc.addClass(_jsPlumb.draggingClass);
- this._jsPlumb.floatingEndpoint.addClass(_jsPlumb.draggingClass);
- // fire an event that informs that a connection is being dragged
- _jsPlumb.fire("connectionDrag", jpc);
-
- } else {
- existingJpc = true;
- jpc.setHover(false);
- // if existing connection, allow to be dropped back on the source endpoint (issue 51).
- _initDropTarget(ipcoel, false, true);
- // new anchor idx
- var anchorIdx = jpc.endpoints[0].id == this.id ? 0 : 1;
- jpc.floatingAnchorIndex = anchorIdx; // save our anchor index as the connection's floating index.
- this.detachFromConnection(jpc); // detach from the connection while dragging is occurring.
-
- // store the original scope (issue 57)
- var dragScope = jsPlumb.CurrentLibrary.getDragScope(canvasElement);
- _jsPlumb.setAttribute(this.canvas, "originalScope", dragScope);
- // now we want to get this endpoint's DROP scope, and set it for now: we can only be dropped on drop zones
- // that have our drop scope (issue 57).
- var dropScope = jpcl.getDropScope(canvasElement);
- jpcl.setDragScope(canvasElement, dropScope);
-
- // fire an event that informs that a connection is being dragged. we do this before
- // replacing the original target with the floating element info.
- _jsPlumb.fire("connectionDrag", jpc);
-
- // now we replace ourselves with the temporary div we created above:
- if (anchorIdx === 0) {
- existingJpcParams = [ jpc.source, jpc.sourceId, canvasElement, dragScope ];
- jpc.source = placeholderInfo.element;
- jpc.sourceId = placeholderInfo.id;
- } else {
- existingJpcParams = [ jpc.target, jpc.targetId, canvasElement, dragScope ];
- jpc.target = placeholderInfo.element;
- jpc.targetId = placeholderInfo.id;
- }
-
- // lock the other endpoint; if it is dynamic it will not move while the drag is occurring.
- jpc.endpoints[anchorIdx === 0 ? 1 : 0].anchor.locked = true;
- // store the original endpoint and assign the new floating endpoint for the drag.
- jpc.suspendedEndpoint = jpc.endpoints[anchorIdx];
-
- // PROVIDE THE SUSPENDED ELEMENT, BE IT A SOURCE OR TARGET (ISSUE 39)
- jpc.suspendedElement = jpc.endpoints[anchorIdx].getElement();
- jpc.suspendedElementId = jpc.endpoints[anchorIdx].elementId;
- jpc.suspendedElementType = anchorIdx === 0 ? "source" : "target";
-
- jpc.suspendedEndpoint.setHover(false);
- this._jsPlumb.floatingEndpoint.referenceEndpoint = jpc.suspendedEndpoint;
- jpc.endpoints[anchorIdx] = this._jsPlumb.floatingEndpoint;
-
- jpc.addClass(_jsPlumb.draggingClass);
- this._jsPlumb.floatingEndpoint.addClass(_jsPlumb.draggingClass);
-
- }
- // register it and register connection on it.
- floatingConnections[placeholderInfo.id] = jpc;
- _jsPlumb.anchorManager.addFloatingConnection(placeholderInfo.id, jpc);
- // only register for the target endpoint; we will not be dragging the source at any time
- // before this connection is either discarded or made into a permanent connection.
- _ju.addToList(params.endpointsByElement, placeholderInfo.id, this._jsPlumb.floatingEndpoint);
- // tell jsplumb about it
- _jsPlumb.currentlyDragging = true;
- }.bind(this);
-
- var dragOptions = params.dragOptions || {},
- defaultOpts = jsPlumb.extend( {}, jpcl.defaultDragOptions),
- startEvent = jpcl.dragEvents.start,
- stopEvent = jpcl.dragEvents.stop,
- dragEvent = jpcl.dragEvents.drag;
-
- dragOptions = jsPlumb.extend(defaultOpts, dragOptions);
- dragOptions.scope = dragOptions.scope || this.scope;
- dragOptions[startEvent] = _ju.wrap(dragOptions[startEvent], start, false);
- // extracted drag handler function so can be used by makeSource
- dragOptions[dragEvent] = _ju.wrap(dragOptions[dragEvent], _dragHandler.drag);
- dragOptions[stopEvent] = _ju.wrap(dragOptions[stopEvent],
- function() {
-
- _jsPlumb.setConnectionBeingDragged(false);
-
- // If the connection has endpoints, we still need to handle whether to throw it away or
- // reattach it. If the connection does not have endpoints, it has already been detached
- // (possibly because a beforeDrop callback returned false), so we can skip this step.
- if (jpc.endpoints !== null) {
- // get the actual drop event (decode from library args to stop function)
- var originalEvent = jpcl.getDropEvent(arguments);
- // unlock the other endpoint (if it is dynamic, it would have been locked at drag start)
- var idx = jpc.floatingAnchorIndex == null ? 1 : jpc.floatingAnchorIndex;
- jpc.endpoints[idx === 0 ? 1 : 0].anchor.locked = false;
- // WHY does this need to happen? i suppose because the connection might not get
- // deleted. TODO: i dont want to know about css classes inside jsplumb, ideally.
- jpc.removeClass(_jsPlumb.draggingClass);
-
- // if we have the floating endpoint then the connection has not been dropped
- // on another endpoint. If it is a new connection we throw it away. If it is an
- // existing connection we check to see if we should reattach it, throwing it away
- // if not.
- if (jpc.endpoints[idx] == this._jsPlumb.floatingEndpoint) {
- // 6a. if the connection was an existing one...
- if (existingJpc && jpc.suspendedEndpoint) {
- // fix for issue35, thanks Sylvain Gizard: when firing the detach event make sure the
- // floating endpoint has been replaced.
- if (idx === 0) {
- jpc.source = existingJpcParams[0];
- jpc.sourceId = existingJpcParams[1];
- } else {
- jpc.target = existingJpcParams[0];
- jpc.targetId = existingJpcParams[1];
- }
-
- // restore the original scope (issue 57)
- jpcl.setDragScope(existingJpcParams[2], existingJpcParams[3]);
- jpc.endpoints[idx] = jpc.suspendedEndpoint;
- // IF the connection should be reattached, or the other endpoint refuses detach, then
- // reset the connection to its original state
- if (jpc.isReattach() || jpc._forceReattach || jpc._forceDetach || !jpc.endpoints[idx === 0 ? 1 : 0].detach(jpc, false, false, true, originalEvent)) {
- jpc.setHover(false);
- jpc.floatingAnchorIndex = null;
- jpc._forceDetach = null;
- jpc._forceReattach = null;
- this._jsPlumb.floatingEndpoint.detachFromConnection(jpc);
- jpc.suspendedEndpoint.addConnection(jpc);
- _jsPlumb.repaint(existingJpcParams[1]);
- }
- }
- }
- }
-
- // remove the element associated with the floating endpoint
- // (and its associated floating endpoint and visual artefacts)
- // TODO we need a way to say that the connection should be kept, if
- _jsPlumb.remove(placeholderInfo.element, false);
- // remove the inplace copy
- _jsPlumb.remove(inPlaceCopy.canvas, false);
-
- // makeTargets sets this flag, to tell us we have been replaced and should delete ourself.
- if (this.deleteAfterDragStop) {
- _jsPlumb.deleteObject({endpoint:this});
- }
- else {
- if (this._jsPlumb) {
- this._jsPlumb.floatingEndpoint = null;
- // repaint this endpoint.
- // make our canvas visible (TODO: hand off to library; we should not know about DOM)
- this.canvas.style.visibility = "visible";
- // unlock our anchor
- this.anchor.locked = false;
- this.paint({recalc:false});
- }
- }
-
- // TODO can this stay here? the connection is no longer valid.
- _jsPlumb.fire("connectionDragStop", jpc);
-
- // tell jsplumb that dragging is finished.
- _jsPlumb.currentlyDragging = false;
-
- jpc = null;
-
- }.bind(this));
-
- var i = _gel(this.canvas);
- jpcl.initDraggable(i, dragOptions, true, _jsPlumb);
- }
-
- // pulled this out into a function so we can reuse it for the inPlaceCopy canvas; you can now drop detached connections
- // back onto the endpoint you detached it from.
- var _initDropTarget = function(canvas, forceInit, isTransient, endpoint) {
- if ((this.isTarget || forceInit) && jpcl.isDropSupported(this.element)) {
- var dropOptions = params.dropOptions || _jsPlumb.Defaults.DropOptions || jsPlumb.Defaults.DropOptions;
- dropOptions = jsPlumb.extend( {}, dropOptions);
- dropOptions.scope = dropOptions.scope || this.scope;
- var dropEvent = jpcl.dragEvents.drop,
- overEvent = jpcl.dragEvents.over,
- outEvent = jpcl.dragEvents.out,
- drop = function() {
-
- this.removeClass(_jsPlumb.endpointDropAllowedClass);
- this.removeClass(_jsPlumb.endpointDropForbiddenClass);
-
- var originalEvent = jpcl.getDropEvent(arguments),
- draggable = _gel(jpcl.getDragObject(arguments)),
- id = _jsPlumb.getAttribute(draggable, "dragId"),
- elId = _jsPlumb.getAttribute(draggable, "elId"),
- scope = _jsPlumb.getAttribute(draggable, "originalScope"),
- jpc = floatingConnections[id];
-
- // if this is a drop back where the connection came from, mark it force rettach and
- // return; the stop handler will reattach. without firing an event.
- var redrop = jpc.suspendedEndpoint && (jpc.suspendedEndpoint.id == this.id ||
- this.referenceEndpoint && jpc.suspendedEndpoint.id == this.referenceEndpoint.id) ;
- if (redrop) {
- jpc._forceReattach = true;
- return;
- }
-
- if (jpc != null) {
- var idx = jpc.floatingAnchorIndex == null ? 1 : jpc.floatingAnchorIndex, oidx = idx === 0 ? 1 : 0;
-
- // restore the original scope if necessary (issue 57)
- if (scope) jsPlumb.CurrentLibrary.setDragScope(draggable, scope);
-
- var endpointEnabled = endpoint != null ? endpoint.isEnabled() : true;
-
- if (this.isFull()) {
- this.fire("maxConnections", {
- endpoint:this,
- connection:jpc,
- maxConnections:this._jsPlumb.maxConnections
- }, originalEvent);
- }
-
- if (!this.isFull() && !(idx === 0 && !this.isSource) && !(idx == 1 && !this.isTarget) && endpointEnabled) {
- var _doContinue = true;
-
- // the second check here is for the case that the user is dropping it back
- // where it came from.
- if (jpc.suspendedEndpoint && jpc.suspendedEndpoint.id != this.id) {
- if (idx === 0) {
- jpc.source = jpc.suspendedEndpoint.element;
- jpc.sourceId = jpc.suspendedEndpoint.elementId;
- } else {
- jpc.target = jpc.suspendedEndpoint.element;
- jpc.targetId = jpc.suspendedEndpoint.elementId;
- }
-
- if (!jpc.isDetachAllowed(jpc) || !jpc.endpoints[idx].isDetachAllowed(jpc) || !jpc.suspendedEndpoint.isDetachAllowed(jpc) || !_jsPlumb.checkCondition("beforeDetach", jpc))
- _doContinue = false;
- }
-
- // these have to be set before testing for beforeDrop.
- if (idx === 0) {
- jpc.source = this.element;
- jpc.sourceId = this.elementId;
- } else {
- jpc.target = this.element;
- jpc.targetId = this.elementId;
- }
-
-// ------------ wrap the execution path in a function so we can support asynchronous beforeDrop
-
- // we want to execute this regardless.
- var commonFunction = function() {
- jpc.floatingAnchorIndex = null;
- };
-
- var continueFunction = function() {
-
- // remove this jpc from the current endpoint
- jpc.endpoints[idx].detachFromConnection(jpc);
- if (jpc.suspendedEndpoint) jpc.suspendedEndpoint.detachFromConnection(jpc);
- jpc.endpoints[idx] = this;
- this.addConnection(jpc);
-
- // copy our parameters in to the connection:
- var params = this.getParameters();
- for (var aParam in params)
- jpc.setParameter(aParam, params[aParam]);
-
- if (!jpc.suspendedEndpoint) {
- if (params.draggable)
- jsPlumb.CurrentLibrary.initDraggable(this.element, dragOptions, true, _jsPlumb);
- }
- else {
- var suspendedElement = jpc.suspendedEndpoint.getElement(), suspendedElementId = jpc.suspendedEndpoint.elementId;
- // fire a detach event
- _fireDetachEvent({
- source : idx === 0 ? suspendedElement : jpc.source,
- target : idx == 1 ? suspendedElement : jpc.target,
- sourceId : idx === 0 ? suspendedElementId : jpc.sourceId,
- targetId : idx == 1 ? suspendedElementId : jpc.targetId,
- sourceEndpoint : idx === 0 ? jpc.suspendedEndpoint : jpc.endpoints[0],
- targetEndpoint : idx == 1 ? jpc.suspendedEndpoint : jpc.endpoints[1],
- connection : jpc
- }, true, originalEvent);
- }
-
- // TODO this is like the makeTarget drop code.
- if (idx == 1)
- _jsPlumb.anchorManager.updateOtherEndpoint(jpc.sourceId, jpc.suspendedElementId, jpc.targetId, jpc);
- else
- _jsPlumb.anchorManager.sourceChanged(jpc.suspendedEndpoint.elementId, jpc.sourceId, jpc);
-
- // finalise will inform the anchor manager and also add to
- // connectionsByScope if necessary.
- _finaliseConnection(jpc, null, originalEvent, true);
-
- commonFunction();
- }.bind(this);
-
- var dontContinueFunction = function() {
- // otherwise just put it back on the endpoint it was on before the drag.
- if (jpc.suspendedEndpoint) {
- jpc.endpoints[idx] = jpc.suspendedEndpoint;
- jpc.setHover(false);
- jpc._forceDetach = true;
- if (idx === 0) {
- jpc.source = jpc.suspendedEndpoint.element;
- jpc.sourceId = jpc.suspendedEndpoint.elementId;
- } else {
- jpc.target = jpc.suspendedEndpoint.element;
- jpc.targetId = jpc.suspendedEndpoint.elementId;
- }
- jpc.suspendedEndpoint.addConnection(jpc);
-
- jpc.endpoints[0].repaint();
- jpc.repaint();
- _jsPlumb.repaint(jpc.sourceId);
- jpc._forceDetach = false;
- }
-
- commonFunction();
- };
-
-// --------------------------------------
- // now check beforeDrop. this will be available only on Endpoints that are setup to
- // have a beforeDrop condition (although, secretly, under the hood all Endpoints and
- // the Connection have them, because they are on jsPlumbUIComponent. shhh!), because
- // it only makes sense to have it on a target endpoint.
- _doContinue = _doContinue && this.isDropAllowed(jpc.sourceId, jpc.targetId, jpc.scope, jpc, this);
-
- if (_doContinue) {
- continueFunction();
- }
- else {
- dontContinueFunction();
- }
- }
- _jsPlumb.currentlyDragging = false;
- }
- }.bind(this);
-
- dropOptions[dropEvent] = _ju.wrap(dropOptions[dropEvent], drop);
- dropOptions[overEvent] = _ju.wrap(dropOptions[overEvent], function() {
- var draggable = jpcl.getDragObject(arguments),
- id = _jsPlumb.getAttribute(draggable, "dragId"),
- _jpc = floatingConnections[id];
-
- if (_jpc != null) {
- var idx = _jpc.floatingAnchorIndex == null ? 1 : _jpc.floatingAnchorIndex;
- // here we should fire the 'over' event if we are a target and this is a new connection,
- // or we are the same as the floating endpoint.
- var _cont = (this.isTarget && _jpc.floatingAnchorIndex !== 0) || (_jpc.suspendedEndpoint && this.referenceEndpoint && this.referenceEndpoint.id == _jpc.suspendedEndpoint.id);
- if (_cont) {
- var bb = _jsPlumb.checkCondition("checkDropAllowed", {
- sourceEndpoint:_jpc.endpoints[idx],
- targetEndpoint:this,
- connection:_jpc
- });
- this[(bb ? "add" : "remove") + "Class"](_jsPlumb.endpointDropAllowedClass);
- this[(bb ? "remove" : "add") + "Class"](_jsPlumb.endpointDropForbiddenClass);
- _jpc.endpoints[idx].anchor.over(this.anchor, this);
- }
- }
- }.bind(this));
-
- dropOptions[outEvent] = _ju.wrap(dropOptions[outEvent], function() {
- var draggable = jpcl.getDragObject(arguments),
- id = _jsPlumb.getAttribute( draggable, "dragId"),
- _jpc = floatingConnections[id];
-
- if (_jpc != null) {
- var idx = _jpc.floatingAnchorIndex == null ? 1 : _jpc.floatingAnchorIndex;
- var _cont = (this.isTarget && _jpc.floatingAnchorIndex !== 0) || (_jpc.suspendedEndpoint && this.referenceEndpoint && this.referenceEndpoint.id == _jpc.suspendedEndpoint.id);
- if (_cont) {
- this.removeClass(_jsPlumb.endpointDropAllowedClass);
- this.removeClass(_jsPlumb.endpointDropForbiddenClass);
- _jpc.endpoints[idx].anchor.out();
- }
- }
- }.bind(this));
- jpcl.initDroppable(canvas, dropOptions, true, isTransient);
- }
- }.bind(this);
-
- // initialise the endpoint's canvas as a drop target. this will be ignored if the endpoint is not a target or drag is not supported.
- _initDropTarget(_gel(this.canvas), true, !(params._transient || this.anchor.isFloating), this);
-
- // finally, set type if it was provided
- if (params.type)
- this.addType(params.type, params.data, _jsPlumb.isSuspendDrawing());
-
- return this;
- };
-
- jsPlumbUtil.extend(jsPlumb.Endpoint, OverlayCapableJsPlumbUIComponent, {
- getTypeDescriptor : function() { return "endpoint"; },
- isVisible : function() { return this._jsPlumb.visible; },
- setVisible : function(v, doNotChangeConnections, doNotNotifyOtherEndpoint) {
- this._jsPlumb.visible = v;
- if (this.canvas) this.canvas.style.display = v ? "block" : "none";
- this[v ? "showOverlays" : "hideOverlays"]();
- if (!doNotChangeConnections) {
- for (var i = 0; i < this.connections.length; i++) {
- this.connections[i].setVisible(v);
- if (!doNotNotifyOtherEndpoint) {
- var oIdx = this === this.connections[i].endpoints[0] ? 1 : 0;
- // only change the other endpoint if this is its only connection.
- if (this.connections[i].endpoints[oIdx].connections.length == 1) this.connections[i].endpoints[oIdx].setVisible(v, true, true);
- }
- }
- }
- },
- getAttachedElements : function() {
- return this.connections;
- },
- applyType : function(t, doNotRepaint) {
- if (t.maxConnections != null) this._jsPlumb.maxConnections = t.maxConnections;
- if (t.scope) this.scope = t.scope;
- jsPlumbUtil.copyValues(typeParameters, t, this);
- },
- isEnabled : function() { return this._jsPlumb.enabled; },
- setEnabled : function(e) { this._jsPlumb.enabled = e; },
- cleanup : function() {
- jsPlumb.CurrentLibrary.removeClass(this.element, this._jsPlumb.instance.endpointAnchorClassPrefix + "_" + this._jsPlumb.currentAnchorClass);
- this.anchor = null;
- this.endpoint.cleanup();
- this.endpoint.destroy();
- this.endpoint = null;
- // drag/drop
- var i = jsPlumb.CurrentLibrary.getElementObject(this.canvas);
- jsPlumb.CurrentLibrary.destroyDraggable(i);
- jsPlumb.CurrentLibrary.destroyDroppable(i);
- },
- setHover : function(h) {
- if (this.endpoint && this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged())
- this.endpoint.setHover(h);
- },
- isFull : function() {
- return !(this.isFloating() || this._jsPlumb.maxConnections < 1 || this.connections.length < this._jsPlumb.maxConnections);
- },
- getConnectionCost : function() { return this._jsPlumb.connectionCost; },
- setConnectionCost : function(c) {
- this._jsPlumb.connectionCost = c;
- },
- areConnectionsDirected : function() { return this._jsPlumb.connectionsDirected; },
- setConnectionsDirected : function(b) { this._jsPlumb.connectionsDirected = b; },
- setElementId : function(_elId) {
- this.elementId = _elId;
- this.anchor.elementId = _elId;
- },
- setReferenceElement : function(_el) {
- this.element = jsPlumb.CurrentLibrary.getDOMElement(_el);
- },
- setDragAllowedWhenFull : function(allowed) {
- this.dragAllowedWhenFull = allowed;
- },
- equals : function(endpoint) {
- return this.anchor.equals(endpoint.anchor);
- },
- getUuid : function() {
- return this._jsPlumb.uuid;
- },
- computeAnchor : function(params) {
- return this.anchor.compute(params);
- }
- });
-})();
-
-;(function() {
-
- var makeConnector = function(_jsPlumb, renderMode, connectorName, connectorArgs) {
- if (!_jsPlumb.Defaults.DoNotThrowErrors && jsPlumb.Connectors[renderMode][connectorName] == null)
- throw { msg:"jsPlumb: unknown connector type '" + connectorName + "'" };
-
- return new jsPlumb.Connectors[renderMode][connectorName](connectorArgs);
- },
- _makeAnchor = function(anchorParams, elementId, _jsPlumb) {
- return (anchorParams) ? _jsPlumb.makeAnchor(anchorParams, elementId, _jsPlumb) : null;
- },
- prepareEndpoint = function(_jsPlumb, _newEndpoint, conn, existing, index, params, element, elementId, connectorPaintStyle, connectorHoverPaintStyle) {
- var e;
- if (existing) {
- conn.endpoints[index] = existing;
- existing.addConnection(conn);
- } else {
- if (!params.endpoints) params.endpoints = [ null, null ];
- var ep = params.endpoints[index] || params.endpoint || _jsPlumb.Defaults.Endpoints[index] || jsPlumb.Defaults.Endpoints[index] || _jsPlumb.Defaults.Endpoint || jsPlumb.Defaults.Endpoint;
- if (!params.endpointStyles) params.endpointStyles = [ null, null ];
- if (!params.endpointHoverStyles) params.endpointHoverStyles = [ null, null ];
- var es = params.endpointStyles[index] || params.endpointStyle || _jsPlumb.Defaults.EndpointStyles[index] || jsPlumb.Defaults.EndpointStyles[index] || _jsPlumb.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle;
- // Endpoints derive their fillStyle from the connector's strokeStyle, if no fillStyle was specified.
- if (es.fillStyle == null && connectorPaintStyle != null)
- es.fillStyle = connectorPaintStyle.strokeStyle;
-
- // TODO: decide if the endpoint should derive the connection's outline width and color. currently it does:
- //*
- if (es.outlineColor == null && connectorPaintStyle != null)
- es.outlineColor = connectorPaintStyle.outlineColor;
- if (es.outlineWidth == null && connectorPaintStyle != null)
- es.outlineWidth = connectorPaintStyle.outlineWidth;
- //*/
-
- var ehs = params.endpointHoverStyles[index] || params.endpointHoverStyle || _jsPlumb.Defaults.EndpointHoverStyles[index] || jsPlumb.Defaults.EndpointHoverStyles[index] || _jsPlumb.Defaults.EndpointHoverStyle || jsPlumb.Defaults.EndpointHoverStyle;
- // endpoint hover fill style is derived from connector's hover stroke style. TODO: do we want to do this by default? for sure?
- if (connectorHoverPaintStyle != null) {
- if (ehs == null) ehs = {};
- if (ehs.fillStyle == null) {
- ehs.fillStyle = connectorHoverPaintStyle.strokeStyle;
- }
- }
- var a = params.anchors ? params.anchors[index] :
- params.anchor ? params.anchor :
- _makeAnchor(_jsPlumb.Defaults.Anchors[index], elementId, _jsPlumb) ||
- _makeAnchor(jsPlumb.Defaults.Anchors[index], elementId,_jsPlumb) ||
- _makeAnchor(_jsPlumb.Defaults.Anchor, elementId,_jsPlumb) ||
- _makeAnchor(jsPlumb.Defaults.Anchor, elementId, _jsPlumb),
- u = params.uuids ? params.uuids[index] : null;
- e = _newEndpoint({
- paintStyle : es, hoverPaintStyle:ehs, endpoint : ep, connections : [ conn ],
- uuid : u, anchor : a, source : element, scope : params.scope, container:params.container,
- reattach:params.reattach || _jsPlumb.Defaults.ReattachConnections,
- detachable:params.detachable || _jsPlumb.Defaults.ConnectionsDetachable
- });
- conn.endpoints[index] = e;
-
- if (params.drawEndpoints === false) e.setVisible(false, true, true);
-
- }
- return e;
- };
-
- jsPlumb.Connection = function(params) {
- var _newConnection = params.newConnection,
- _newEndpoint = params.newEndpoint,
- jpcl = jsPlumb.CurrentLibrary,
- _att = jpcl.getAttribute,
- _gel = jpcl.getElementObject,
- _dom = jpcl.getDOMElement,
- _ju = jsPlumbUtil,
- _getOffset = jpcl.getOffset;
-
- this.connector = null;
- this.idPrefix = "_jsplumb_c_";
- this.defaultLabelLocation = 0.5;
- this.defaultOverlayKeys = ["Overlays", "ConnectionOverlays"];
- this.parent = params.parent;
- // if a new connection is the result of moving some existing connection, params.previousConnection
- // will have that Connection in it. listeners for the jsPlumbConnection event can look for that
- // member and take action if they need to.
- this.previousConnection = params.previousConnection;
- this.source = _dom(params.source);
- this.target = _dom(params.target);
- // sourceEndpoint and targetEndpoint override source/target, if they are present. but
- // source is not overridden if the Endpoint has declared it is not the final target of a connection;
- // instead we use the source that the Endpoint declares will be the final source element.
- if (params.sourceEndpoint) this.source = params.sourceEndpoint.endpointWillMoveTo || params.sourceEndpoint.getElement();
- if (params.targetEndpoint) this.target = params.targetEndpoint.getElement();
-
- OverlayCapableJsPlumbUIComponent.apply(this, arguments);
-
- this.sourceId = this._jsPlumb.instance.getId(this.source);
- this.targetId = this._jsPlumb.instance.getId(this.target);
- this.scope = params.scope; // scope may have been passed in to the connect call. if it wasn't, we will pull it from the source endpoint, after having initialised the endpoints.
- this.endpoints = [];
- this.endpointStyles = [];
-
- var _jsPlumb = this._jsPlumb.instance;
- this._jsPlumb.visible = true;
- this._jsPlumb.editable = params.editable === true;
- this._jsPlumb.params = {
- parent:params.parent,
- cssClass:params.cssClass,
- container:params.container,
- "pointer-events":params["pointer-events"],
- editorParams:params.editorParams
- };
- this._jsPlumb.lastPaintedAt = null;
- this.getDefaultType = function() {
- return {
- parameters:{},
- scope:null,
- detachable:this._jsPlumb.instance.Defaults.ConnectionsDetachable,
- rettach:this._jsPlumb.instance.Defaults.ReattachConnections,
- paintStyle:this._jsPlumb.instance.Defaults.PaintStyle || jsPlumb.Defaults.PaintStyle,
- connector:this._jsPlumb.instance.Defaults.Connector || jsPlumb.Defaults.Connector,
- hoverPaintStyle:this._jsPlumb.instance.Defaults.HoverPaintStyle || jsPlumb.Defaults.HoverPaintStyle,
- overlays:this._jsPlumb.instance.Defaults.ConnectorOverlays || jsPlumb.Defaults.ConnectorOverlays
- };
- };
-
-// INITIALISATION CODE
-
- // wrapped the main function to return null if no input given. this lets us cascade defaults properly.
-
- var eS = prepareEndpoint(_jsPlumb, _newEndpoint, this, params.sourceEndpoint, 0, params, this.source, this.sourceId, params.paintStyle, params.hoverPaintStyle);
- if (eS) _ju.addToList(params.endpointsByElement, this.sourceId, eS);
- var eT = prepareEndpoint(_jsPlumb, _newEndpoint, this, params.targetEndpoint, 1, params, this.target, this.targetId, params.paintStyle, params.hoverPaintStyle);
- if (eT) _ju.addToList(params.endpointsByElement, this.targetId, eT);
- // if scope not set, set it to be the scope for the source endpoint.
- if (!this.scope) this.scope = this.endpoints[0].scope;
-
- // if explicitly told to (or not to) delete endpoints on detach, override endpoint's preferences
- if (params.deleteEndpointsOnDetach != null) {
- this.endpoints[0]._deleteOnDetach = params.deleteEndpointsOnDetach;
- this.endpoints[1]._deleteOnDetach = params.deleteEndpointsOnDetach;
- }
- else {
- // otherwise, unless the endpoints say otherwise, mark them for deletion.
- if (!this.endpoints[0]._doNotDeleteOnDetach) this.endpoints[0]._deleteOnDetach = true;
- if (!this.endpoints[1]._doNotDeleteOnDetach) this.endpoints[1]._deleteOnDetach = true;
- }
-
- // TODO these could surely be refactored into some method that tries them one at a time until something exists
- this.setConnector(this.endpoints[0].connector ||
- this.endpoints[1].connector ||
- params.connector ||
- _jsPlumb.Defaults.Connector ||
- jsPlumb.Defaults.Connector, true);
-
- if (params.path)
- this.connector.setPath(params.path);
-
- this.setPaintStyle(this.endpoints[0].connectorStyle ||
- this.endpoints[1].connectorStyle ||
- params.paintStyle ||
- _jsPlumb.Defaults.PaintStyle ||
- jsPlumb.Defaults.PaintStyle, true);
-
- this.setHoverPaintStyle(this.endpoints[0].connectorHoverStyle ||
- this.endpoints[1].connectorHoverStyle ||
- params.hoverPaintStyle ||
- _jsPlumb.Defaults.HoverPaintStyle ||
- jsPlumb.Defaults.HoverPaintStyle, true);
-
- this._jsPlumb.paintStyleInUse = this.getPaintStyle();
-
- var _suspendedAt = _jsPlumb.getSuspendedAt();
- _jsPlumb.updateOffset( { elId : this.sourceId, timestamp:_suspendedAt });
- _jsPlumb.updateOffset( { elId : this.targetId, timestamp:_suspendedAt });
-
-//*
- if(!_jsPlumb.isSuspendDrawing()) {
- // paint the endpoints
- var myInfo = _jsPlumb.getCachedData(this.sourceId),
- myOffset = myInfo.o, myWH = myInfo.s,
- otherInfo = _jsPlumb.getCachedData(this.targetId),
- otherOffset = otherInfo.o,
- otherWH = otherInfo.s,
- initialTimestamp = _suspendedAt || _jsPlumb.timestamp(),
- anchorLoc = this.endpoints[0].anchor.compute( {
- xy : [ myOffset.left, myOffset.top ], wh : myWH, element : this.endpoints[0],
- elementId:this.endpoints[0].elementId,
- txy : [ otherOffset.left, otherOffset.top ], twh : otherWH, tElement : this.endpoints[1],
- timestamp:initialTimestamp
- });
-
- this.endpoints[0].paint( { anchorLoc : anchorLoc, timestamp:initialTimestamp });
-
- anchorLoc = this.endpoints[1].anchor.compute( {
- xy : [ otherOffset.left, otherOffset.top ], wh : otherWH, element : this.endpoints[1],
- elementId:this.endpoints[1].elementId,
- txy : [ myOffset.left, myOffset.top ], twh : myWH, tElement : this.endpoints[0],
- timestamp:initialTimestamp
- });
- this.endpoints[1].paint({ anchorLoc : anchorLoc, timestamp:initialTimestamp });
- }
- //*/
-
-// END INITIALISATION CODE
-
-// DETACHABLE
- this._jsPlumb.detachable = _jsPlumb.Defaults.ConnectionsDetachable;
- if (params.detachable === false) this._jsPlumb.detachable = false;
- if(this.endpoints[0].connectionsDetachable === false) this._jsPlumb.detachable = false;
- if(this.endpoints[1].connectionsDetachable === false) this._jsPlumb.detachable = false;
-// REATTACH
- this._jsPlumb.reattach = params.reattach || this.endpoints[0].reattachConnections || this.endpoints[1].reattachConnections || _jsPlumb.Defaults.ReattachConnections;
-// COST + DIRECTIONALITY
- // if cost not supplied, try to inherit from source endpoint
- this._jsPlumb.cost = params.cost || this.endpoints[0].getConnectionCost();
- this._jsPlumb.directed = params.directed;
- // inherit directed flag if set no source endpoint
- if (params.directed == null) this._jsPlumb.directed = this.endpoints[0].areConnectionsDirected();
-// END COST + DIRECTIONALITY
-
-// PARAMETERS
- // merge all the parameters objects into the connection. parameters set
- // on the connection take precedence; then source endpoint params, then
- // finally target endpoint params.
- // TODO jsPlumb.extend could be made to take more than two args, and it would
- // apply the second through nth args in order.
- var _p = jsPlumb.extend({}, this.endpoints[1].getParameters());
- jsPlumb.extend(_p, this.endpoints[0].getParameters());
- jsPlumb.extend(_p, this.getParameters());
- this.setParameters(_p);
-// END PARAMETERS
-
-// PAINTING
-
- // the very last thing we do is check to see if a 'type' was supplied in the params
- var _type = params.type || this.endpoints[0].connectionType || this.endpoints[1].connectionType;
- if (_type)
- this.addType(_type, params.data, true);
-
-// END PAINTING
- };
-
- jsPlumbUtil.extend(jsPlumb.Connection, OverlayCapableJsPlumbUIComponent, {
- applyType : function(t, doNotRepaint) {
- if (t.detachable != null) this.setDetachable(t.detachable);
- if (t.reattach != null) this.setReattach(t.reattach);
- if (t.scope) this.scope = t.scope;
- //editable = t.editable; // TODO
- this.setConnector(t.connector, doNotRepaint);
- },
- getTypeDescriptor : function() { return "connection"; },
- getAttachedElements : function() {
- return this.endpoints;
- },
- addClass : function(c, informEndpoints) {
- if (informEndpoints) {
- this.endpoints[0].addClass(c);
- this.endpoints[1].addClass(c);
- if (this.suspendedEndpoint) this.suspendedEndpoint.addClass(c);
- }
- if (this.connector) {
- this.connector.addClass(c);
- }
- },
- removeClass : function(c, informEndpoints) {
- if (informEndpoints) {
- this.endpoints[0].removeClass(c);
- this.endpoints[1].removeClass(c);
- if (this.suspendedEndpoint) this.suspendedEndpoint.removeClass(c);
- }
- if (this.connector) {
- this.connector.removeClass(c);
- }
- },
- isVisible : function() { return this._jsPlumb.visible; },
- setVisible : function(v) {
- this._jsPlumb.visible = v;
- this[v ? "showOverlays" : "hideOverlays"]();
- if (this.connector && this.connector.canvas) this.connector.canvas.style.display = v ? "block" : "none";
- this.repaint();
- },
- setEditable : function(e) {
- if (this.connector && this.connector.isEditable())
- this._jsPlumb.editable = e;
-
- return this._jsPlumb.editable;
- },
- isEditable : function() { return this._jsPlumb.editable; },
- editStarted : function() {
- this.setSuspendEvents(true);
- this.fire("editStarted", {
- path:this.connector.getPath()
- });
- this._jsPlumb.instance.setHoverSuspended(true);
- },
- editCompleted : function() {
- this.fire("editCompleted", {
- path:this.connector.getPath()
- });
- this.setSuspendEvents(false);
- this.setHover(false);
- this._jsPlumb.instance.setHoverSuspended(false);
- },
- editCanceled : function() {
- this.fire("editCanceled", {
- path:this.connector.getPath()
- });
- this.setHover(false);
- this._jsPlumb.instance.setHoverSuspended(false);
- },
- cleanup:function() {
- //this.endpointsToDeleteOnDetach = null;
- this.endpoints = null;
- this.source = null;
- this.target = null;
- if (this.connector != null) {
- this.connector.cleanup();
- this.connector.destroy();
- }
- this.connector = null;
- },
- isDetachable : function() {
- return this._jsPlumb.detachable === true;
- },
- setDetachable : function(detachable) {
- this._jsPlumb.detachable = detachable === true;
- },
- isReattach : function() {
- return this._jsPlumb.reattach === true;
- },
- setReattach : function(reattach) {
- this._jsPlumb.reattach = reattach === true;
- },
- setHover : function(state) {
- if (this.connector && this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) {
- this.connector.setHover(state);
- jsPlumb.CurrentLibrary[state ? "addClass" : "removeClass"](this.source, this._jsPlumb.instance.hoverSourceClass);
- jsPlumb.CurrentLibrary[state ? "addClass" : "removeClass"](this.target, this._jsPlumb.instance.hoverTargetClass);
- }
- },
- getCost : function() { return this._jsPlumb.cost; },
- setCost : function(c) { this._jsPlumb.cost = c; },
- isDirected : function() { return this._jsPlumb.directed === true; },
- //
- // changes the parent element of this connection to newParent. not exposed for the public API.
- //
- // TODO ensure moveParent method still works (the overlay stuff in particular)
- moveParent : function(newParent) {
- var jpcl = jsPlumb.CurrentLibrary, curParent = jpcl.getParent(this.connector.canvas);
- if (this.connector.bgCanvas) {
- jpcl.removeElement(this.connector.bgCanvas);
- jpcl.appendElement(this.connector.bgCanvas, newParent);
- }
- jpcl.removeElement(this.connector.canvas);
- jpcl.appendElement(this.connector.canvas, newParent);
- // this only applies for DOMOverlays
- for (var i = 0; i < this._jsPlumb.overlays.length; i++) {
- if (this._jsPlumb.overlays[i].isAppendedAtTopLevel) {
- jpcl.removeElement(this._jsPlumb.overlays[i].canvas);
- jpcl.appendElement(this._jsPlumb.overlays[i].canvas, newParent);
- if (this._jsPlumb.overlays[i].reattachListeners)
- this._jsPlumb.overlays[i].reattachListeners(this.connector);
- }
- }
- if (this.connector.reattachListeners) // this is for SVG/VML; change an element's parent and you have to reinit its listeners.
- this.connector.reattachListeners(); // the Canvas implementation doesn't have to care about this
- },
- getConnector : function() { return this.connector; },
- setConnector : function(connectorSpec, doNotRepaint) {
- var _ju = jsPlumbUtil;
- if (this.connector != null) {
- this.connector.cleanup();
- this.connector.destroy();
- }
-
- var connectorArgs = {
- _jsPlumb:this._jsPlumb.instance,
- parent:this._jsPlumb.params.parent,
- cssClass:this._jsPlumb.params.cssClass,
- container:this._jsPlumb.params.container,
- "pointer-events":this._jsPlumb.params["pointer-events"]
- },
- renderMode = this._jsPlumb.instance.getRenderMode();
-
- if (_ju.isString(connectorSpec))
- this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec, connectorArgs); // lets you use a string as shorthand.
- else if (_ju.isArray(connectorSpec)) {
- if (connectorSpec.length == 1)
- this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], connectorArgs);
- else
- this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], _ju.merge(connectorSpec[1], connectorArgs));
- }
- // binds mouse listeners to the current connector.
- this.bindListeners(this.connector, this, function(state) {
- this.setHover(state, false);
- }.bind(this));
-
- this.canvas = this.connector.canvas;
-
- if (this._jsPlumb.editable && jsPlumb.ConnectorEditors != null && jsPlumb.ConnectorEditors[this.connector.type] && this.connector.isEditable()) {
- new jsPlumb.ConnectorEditors[this.connector.type]({
- connector:this.connector,
- connection:this,
- params:this._jsPlumb.params.editorParams || { }
- });
- }
- else {
- var editable = false;
- }
-
- if (!doNotRepaint) this.repaint();
- },
- paint : function(params) {
-
- if (!this._jsPlumb.instance.isSuspendDrawing() && this._jsPlumb.visible) {
-
- params = params || {};
- var elId = params.elId, ui = params.ui, recalc = params.recalc, timestamp = params.timestamp,
- // if the moving object is not the source we must transpose the two references.
- swap = false,
- tId = swap ? this.sourceId : this.targetId, sId = swap ? this.targetId : this.sourceId,
- tIdx = swap ? 0 : 1, sIdx = swap ? 1 : 0;
-
- if (timestamp == null || timestamp != this._jsPlumb.lastPaintedAt) {
- var sourceInfo = this._jsPlumb.instance.updateOffset( { elId : sId, offset : ui, recalc : recalc, timestamp : timestamp }).o,
- targetInfo = this._jsPlumb.instance.updateOffset( { elId : tId, timestamp : timestamp }).o, // update the target if this is a forced repaint. otherwise, only the source has been moved.
- sE = this.endpoints[sIdx], tE = this.endpoints[tIdx];
-
- if (params.clearEdits) {
- sE.anchor.clearUserDefinedLocation();
- tE.anchor.clearUserDefinedLocation();
- this.connector.setEdited(false);
- }
-
- var sAnchorP = sE.anchor.getCurrentLocation({xy:[sourceInfo.left,sourceInfo.top], wh:[sourceInfo.width, sourceInfo.height], element:sE, timestamp:timestamp}),
- tAnchorP = tE.anchor.getCurrentLocation({xy:[targetInfo.left,targetInfo.top], wh:[targetInfo.width, targetInfo.height], element:tE, timestamp:timestamp});
-
- this.connector.resetBounds();
-
- this.connector.compute({
- sourcePos:sAnchorP,
- targetPos:tAnchorP,
- sourceEndpoint:this.endpoints[sIdx],
- targetEndpoint:this.endpoints[tIdx],
- lineWidth:this._jsPlumb.paintStyleInUse.lineWidth,
- sourceInfo:sourceInfo,
- targetInfo:targetInfo,
- clearEdits:params.clearEdits === true
- });
-
- var overlayExtents = { minX:Infinity, minY:Infinity, maxX:-Infinity, maxY:-Infinity };
-
- // compute overlays. we do this first so we can get their placements, and adjust the
- // container if needs be (if an overlay would be clipped)
- for ( var i = 0; i < this._jsPlumb.overlays.length; i++) {
- var o = this._jsPlumb.overlays[i];
- if (o.isVisible()) {
- this._jsPlumb.overlayPlacements[i] = o.draw(this.connector, this._jsPlumb.paintStyleInUse);
- overlayExtents.minX = Math.min(overlayExtents.minX, this._jsPlumb.overlayPlacements[i].minX);
- overlayExtents.maxX = Math.max(overlayExtents.maxX, this._jsPlumb.overlayPlacements[i].maxX);
- overlayExtents.minY = Math.min(overlayExtents.minY, this._jsPlumb.overlayPlacements[i].minY);
- overlayExtents.maxY = Math.max(overlayExtents.maxY, this._jsPlumb.overlayPlacements[i].maxY);
- }
- }
-
- var lineWidth = parseFloat(this._jsPlumb.paintStyleInUse.lineWidth || 1) / 2,
- outlineWidth = parseFloat(this._jsPlumb.paintStyleInUse.lineWidth || 0),
- extents = {
- xmin : Math.min(this.connector.bounds.minX - (lineWidth + outlineWidth), overlayExtents.minX),
- ymin : Math.min(this.connector.bounds.minY - (lineWidth + outlineWidth), overlayExtents.minY),
- xmax : Math.max(this.connector.bounds.maxX + (lineWidth + outlineWidth), overlayExtents.maxX),
- ymax : Math.max(this.connector.bounds.maxY + (lineWidth + outlineWidth), overlayExtents.maxY)
- };
-
- // paint the connector.
- this.connector.paint(this._jsPlumb.paintStyleInUse, null, extents);
- // and then the overlays
- for ( var j = 0; j < this._jsPlumb.overlays.length; j++) {
- var p = this._jsPlumb.overlays[j];
- if (p.isVisible()) {
- p.paint(this._jsPlumb.overlayPlacements[j], extents);
- }
- }
- }
- this._jsPlumb.lastPaintedAt = timestamp;
- }
- },
- /*
- * Function: repaint
- * Repaints the Connection. No parameters exposed to public API.
- */
- repaint : function(params) {
- params = params || {};
- this.paint({ elId : this.sourceId, recalc : !(params.recalc === false), timestamp:params.timestamp, clearEdits:params.clearEdits });
- }
-
- }); // END Connection class
-})();
-
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the code for creating and manipulating anchors.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt ([email protected])
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-;(function() {
-
- //
- // manages anchors for all elements.
- //
- jsPlumb.AnchorManager = function(params) {
- var _amEndpoints = {},
- continuousAnchors = {},
- continuousAnchorLocations = {},
- userDefinedContinuousAnchorLocations = {},
- continuousAnchorOrientations = {},
- Orientation = { HORIZONTAL : "horizontal", VERTICAL : "vertical", DIAGONAL : "diagonal", IDENTITY:"identity" },
- connectionsByElementId = {},
- self = this,
- anchorLists = {},
- jsPlumbInstance = params.jsPlumbInstance,
- jpcl = jsPlumb.CurrentLibrary,
- floatingConnections = {},
- // TODO this functions uses a crude method of determining orientation between two elements.
- // 'diagonal' should be chosen when the angle of the line between the two centers is around
- // one of 45, 135, 225 and 315 degrees. maybe +- 15 degrees.
- // used by AnchorManager.redraw
- calculateOrientation = function(sourceId, targetId, sd, td, sourceAnchor, targetAnchor) {
-
- if (sourceId === targetId) return {
- orientation:Orientation.IDENTITY,
- a:["top", "top"]
- };
-
- var theta = Math.atan2((td.centery - sd.centery) , (td.centerx - sd.centerx)),
- theta2 = Math.atan2((sd.centery - td.centery) , (sd.centerx - td.centerx)),
- h = ((sd.left <= td.left && sd.right >= td.left) || (sd.left <= td.right && sd.right >= td.right) ||
- (sd.left <= td.left && sd.right >= td.right) || (td.left <= sd.left && td.right >= sd.right)),
- v = ((sd.top <= td.top && sd.bottom >= td.top) || (sd.top <= td.bottom && sd.bottom >= td.bottom) ||
- (sd.top <= td.top && sd.bottom >= td.bottom) || (td.top <= sd.top && td.bottom >= sd.bottom)),
- possiblyTranslateEdges = function(edges) {
- // this function checks to see if either anchor is Continuous, and if so, runs the suggested edge
- // through the anchor: Continuous anchors can say which faces they support, and they get to choose
- // whether a certain face is honoured, or, if not, which face to replace it with. the behaviour when
- // choosing an alternate face is to try for the opposite face first, then the next one clockwise, and then
- // the opposite of that one.
- return [
- sourceAnchor.isContinuous ? sourceAnchor.verifyEdge(edges[0]) : edges[0],
- targetAnchor.isContinuous ? targetAnchor.verifyEdge(edges[1]) : edges[1]
- ];
- },
- out = {
- orientation:Orientation.DIAGONAL,
- theta:theta,
- theta2:theta2
- };
-
- if (! (h || v)) {
- if (td.left > sd.left && td.top > sd.top)
- out.a = ["right", "top"];
- else if (td.left > sd.left && sd.top > td.top)
- out.a = [ "top", "left"];
- else if (td.left < sd.left && td.top < sd.top)
- out.a = [ "top", "right"];
- else if (td.left < sd.left && td.top > sd.top)
- out.a = ["left", "top" ];
- }
- else if (h) {
- out.orientation = Orientation.HORIZONTAL;
- out.a = sd.top < td.top ? ["bottom", "top"] : ["top", "bottom"];
- }
- else {
- out.orientation = Orientation.VERTICAL;
- out.a = sd.left < td.left ? ["right", "left"] : ["left", "right"];
- }
-
- out.a = possiblyTranslateEdges(out.a);
- return out;
- },
- // used by placeAnchors function
- placeAnchorsOnLine = function(desc, elementDimensions, elementPosition,
- connections, horizontal, otherMultiplier, reverse) {
- var a = [], step = elementDimensions[horizontal ? 0 : 1] / (connections.length + 1);
-
- for (var i = 0; i < connections.length; i++) {
- var val = (i + 1) * step, other = otherMultiplier * elementDimensions[horizontal ? 1 : 0];
- if (reverse)
- val = elementDimensions[horizontal ? 0 : 1] - val;
-
- var dx = (horizontal ? val : other), x = elementPosition[0] + dx, xp = dx / elementDimensions[0],
- dy = (horizontal ? other : val), y = elementPosition[1] + dy, yp = dy / elementDimensions[1];
-
- a.push([ x, y, xp, yp, connections[i][1], connections[i][2] ]);
- }
-
- return a;
- },
- // used by edgeSortFunctions
- currySort = function(reverseAngles) {
- return function(a,b) {
- var r = true;
- if (reverseAngles) {
- /*if (a[0][0] < b[0][0])
- r = true;
- else
- r = a[0][1] > b[0][1];*/
- r = a[0][0] < b[0][0];
- }
- else {
- /*if (a[0][0] > b[0][0])
- r= true;
- else
- r =a[0][1] > b[0][1];
- */
- r = a[0][0] > b[0][0];
- }
- return r === false ? -1 : 1;
- };
- },
- // used by edgeSortFunctions
- leftSort = function(a,b) {
- // first get adjusted values
- var p1 = a[0][0] < 0 ? -Math.PI - a[0][0] : Math.PI - a[0][0],
- p2 = b[0][0] < 0 ? -Math.PI - b[0][0] : Math.PI - b[0][0];
- if (p1 > p2) return 1;
- else return a[0][1] > b[0][1] ? 1 : -1;
- },
- // used by placeAnchors
- edgeSortFunctions = {
- "top":function(a, b) { return a[0] > b[0] ? 1 : -1; },
- "right":currySort(true),
- "bottom":currySort(true),
- "left":leftSort
- },
- // used by placeAnchors
- _sortHelper = function(_array, _fn) { return _array.sort(_fn); },
- // used by AnchorManager.redraw
- placeAnchors = function(elementId, _anchorLists) {
- var cd = jsPlumbInstance.getCachedData(elementId), sS = cd.s, sO = cd.o,
- placeSomeAnchors = function(desc, elementDimensions, elementPosition, unsortedConnections, isHorizontal, otherMultiplier, orientation) {
- if (unsortedConnections.length > 0) {
- var sc = _sortHelper(unsortedConnections, edgeSortFunctions[desc]), // puts them in order based on the target element's pos on screen
- reverse = desc === "right" || desc === "top",
- anchors = placeAnchorsOnLine(desc, elementDimensions,
- elementPosition, sc,
- isHorizontal, otherMultiplier, reverse );
-
- // takes a computed anchor position and adjusts it for parent offset and scroll, then stores it.
- var _setAnchorLocation = function(endpoint, anchorPos) {
- var a = jsPlumbInstance.adjustForParentOffsetAndScroll([anchorPos[0], anchorPos[1]], endpoint.canvas);
- continuousAnchorLocations[endpoint.id] = [ a[0], a[1], anchorPos[2], anchorPos[3] ];
- continuousAnchorOrientations[endpoint.id] = orientation;
- };
-
- for (var i = 0; i < anchors.length; i++) {
- var c = anchors[i][4], weAreSource = c.endpoints[0].elementId === elementId, weAreTarget = c.endpoints[1].elementId === elementId;
- if (weAreSource)
- _setAnchorLocation(c.endpoints[0], anchors[i]);
- else if (weAreTarget)
- _setAnchorLocation(c.endpoints[1], anchors[i]);
- }
- }
- };
-
- placeSomeAnchors("bottom", sS, [sO.left,sO.top], _anchorLists.bottom, true, 1, [0,1]);
- placeSomeAnchors("top", sS, [sO.left,sO.top], _anchorLists.top, true, 0, [0,-1]);
- placeSomeAnchors("left", sS, [sO.left,sO.top], _anchorLists.left, false, 0, [-1,0]);
- placeSomeAnchors("right", sS, [sO.left,sO.top], _anchorLists.right, false, 1, [1,0]);
- };
-
- this.reset = function() {
- _amEndpoints = {};
- connectionsByElementId = {};
- anchorLists = {};
- };
- this.addFloatingConnection = function(key, conn) {
- floatingConnections[key] = conn;
- };
- this.removeFloatingConnection = function(key) {
- delete floatingConnections[key];
- };
- this.newConnection = function(conn) {
- var sourceId = conn.sourceId, targetId = conn.targetId,
- ep = conn.endpoints,
- doRegisterTarget = true,
- registerConnection = function(otherIndex, otherEndpoint, otherAnchor, elId, c) {
- if ((sourceId == targetId) && otherAnchor.isContinuous){
- // remove the target endpoint's canvas. we dont need it.
- jpcl.removeElement(ep[1].canvas);
- doRegisterTarget = false;
- }
- jsPlumbUtil.addToList(connectionsByElementId, elId, [c, otherEndpoint, otherAnchor.constructor == jsPlumb.DynamicAnchor]);
- };
-
- registerConnection(0, ep[0], ep[0].anchor, targetId, conn);
- if (doRegisterTarget)
- registerConnection(1, ep[1], ep[1].anchor, sourceId, conn);
- };
- var removeEndpointFromAnchorLists = function(endpoint) {
- (function(list, eId) {
- if (list) { // transient anchors dont get entries in this list.
- var f = function(e) { return e[4] == eId; };
- jsPlumbUtil.removeWithFunction(list.top, f);
- jsPlumbUtil.removeWithFunction(list.left, f);
- jsPlumbUtil.removeWithFunction(list.bottom, f);
- jsPlumbUtil.removeWithFunction(list.right, f);
- }
- })(anchorLists[endpoint.elementId], endpoint.id);
- };
- this.connectionDetached = function(connInfo) {
- var connection = connInfo.connection || connInfo,
- sourceId = connInfo.sourceId,
- targetId = connInfo.targetId,
- ep = connection.endpoints,
- removeConnection = function(otherIndex, otherEndpoint, otherAnchor, elId, c) {
- if (otherAnchor != null && otherAnchor.constructor == jsPlumb.FloatingAnchor) {
- // no-op
- }
- else {
- jsPlumbUtil.removeWithFunction(connectionsByElementId[elId], function(_c) {
- return _c[0].id == c.id;
- });
- }
- };
-
- removeConnection(1, ep[1], ep[1].anchor, sourceId, connection);
- removeConnection(0, ep[0], ep[0].anchor, targetId, connection);
-
- // remove from anchorLists
- removeEndpointFromAnchorLists(connection.endpoints[0]);
- removeEndpointFromAnchorLists(connection.endpoints[1]);
-
- self.redraw(connection.sourceId);
- self.redraw(connection.targetId);
- };
- this.add = function(endpoint, elementId) {
- jsPlumbUtil.addToList(_amEndpoints, elementId, endpoint);
- };
- this.changeId = function(oldId, newId) {
- connectionsByElementId[newId] = connectionsByElementId[oldId];
- _amEndpoints[newId] = _amEndpoints[oldId];
- delete connectionsByElementId[oldId];
- delete _amEndpoints[oldId];
- };
- this.getConnectionsFor = function(elementId) {
- return connectionsByElementId[elementId] || [];
- };
- this.getEndpointsFor = function(elementId) {
- return _amEndpoints[elementId] || [];
- };
- this.deleteEndpoint = function(endpoint) {
- jsPlumbUtil.removeWithFunction(_amEndpoints[endpoint.elementId], function(e) {
- return e.id == endpoint.id;
- });
- removeEndpointFromAnchorLists(endpoint);
- };
- this.clearFor = function(elementId) {
- delete _amEndpoints[elementId];
- _amEndpoints[elementId] = [];
- };
- // updates the given anchor list by either updating an existing anchor's info, or adding it. this function
- // also removes the anchor from its previous list, if the edge it is on has changed.
- // all connections found along the way (those that are connected to one of the faces this function
- // operates on) are added to the connsToPaint list, as are their endpoints. in this way we know to repaint
- // them wthout having to calculate anything else about them.
- var _updateAnchorList = function(lists, theta, order, conn, aBoolean, otherElId, idx, reverse, edgeId, elId, connsToPaint, endpointsToPaint) {
- // first try to find the exact match, but keep track of the first index of a matching element id along the way.s
- var exactIdx = -1,
- firstMatchingElIdx = -1,
- endpoint = conn.endpoints[idx],
- endpointId = endpoint.id,
- oIdx = [1,0][idx],
- values = [ [ theta, order ], conn, aBoolean, otherElId, endpointId ],
- listToAddTo = lists[edgeId],
- listToRemoveFrom = endpoint._continuousAnchorEdge ? lists[endpoint._continuousAnchorEdge] : null;
-
- if (listToRemoveFrom) {
- var rIdx = jsPlumbUtil.findWithFunction(listToRemoveFrom, function(e) { return e[4] == endpointId; });
- if (rIdx != -1) {
- listToRemoveFrom.splice(rIdx, 1);
- // get all connections from this list
- for (var i = 0; i < listToRemoveFrom.length; i++) {
- jsPlumbUtil.addWithFunction(connsToPaint, listToRemoveFrom[i][1], function(c) { return c.id == listToRemoveFrom[i][1].id; });
- jsPlumbUtil.addWithFunction(endpointsToPaint, listToRemoveFrom[i][1].endpoints[idx], function(e) { return e.id == listToRemoveFrom[i][1].endpoints[idx].id; });
- jsPlumbUtil.addWithFunction(endpointsToPaint, listToRemoveFrom[i][1].endpoints[oIdx], function(e) { return e.id == listToRemoveFrom[i][1].endpoints[oIdx].id; });
- }
- }
- }
-
- for (i = 0; i < listToAddTo.length; i++) {
- if (params.idx == 1 && listToAddTo[i][3] === otherElId && firstMatchingElIdx == -1)
- firstMatchingElIdx = i;
- jsPlumbUtil.addWithFunction(connsToPaint, listToAddTo[i][1], function(c) { return c.id == listToAddTo[i][1].id; });
- jsPlumbUtil.addWithFunction(endpointsToPaint, listToAddTo[i][1].endpoints[idx], function(e) { return e.id == listToAddTo[i][1].endpoints[idx].id; });
- jsPlumbUtil.addWithFunction(endpointsToPaint, listToAddTo[i][1].endpoints[oIdx], function(e) { return e.id == listToAddTo[i][1].endpoints[oIdx].id; });
- }
- if (exactIdx != -1) {
- listToAddTo[exactIdx] = values;
- }
- else {
- var insertIdx = reverse ? firstMatchingElIdx != -1 ? firstMatchingElIdx : 0 : listToAddTo.length; // of course we will get this from having looked through the array shortly.
- listToAddTo.splice(insertIdx, 0, values);
- }
-
- // store this for next time.
- endpoint._continuousAnchorEdge = edgeId;
- };
-
- //
- // find the entry in an endpoint's list for this connection and update its target endpoint
- // with the current target in the connection.
- //
- //
- this.updateOtherEndpoint = function(elId, oldTargetId, newTargetId, connection) {
- var sIndex = jsPlumbUtil.findWithFunction(connectionsByElementId[elId], function(i) {
- return i[0].id === connection.id;
- }),
- tIndex = jsPlumbUtil.findWithFunction(connectionsByElementId[oldTargetId], function(i) {
- return i[0].id === connection.id;
- });
-
- // update or add data for source
- if (sIndex != -1) {
- connectionsByElementId[elId][sIndex][0] = connection;
- connectionsByElementId[elId][sIndex][1] = connection.endpoints[1];
- connectionsByElementId[elId][sIndex][2] = connection.endpoints[1].anchor.constructor == jsPlumb.DynamicAnchor;
- }
-
- // remove entry for previous target (if there)
- if (tIndex > -1) {
-
- connectionsByElementId[oldTargetId].splice(tIndex, 1);
- // add entry for new target
- jsPlumbUtil.addToList(connectionsByElementId, newTargetId, [connection, connection.endpoints[0], connection.endpoints[0].anchor.constructor == jsPlumb.DynamicAnchor]);
- }
- };
-
- //
- // notification that the connection given has changed source from the originalId to the newId.
- // This involves:
- // 1. removing the connection from the list of connections stored for the originalId
- // 2. updating the source information for the target of the connection
- // 3. re-registering the connection in connectionsByElementId with the newId
- //
- this.sourceChanged = function(originalId, newId, connection) {
- // remove the entry that points from the old source to the target
- jsPlumbUtil.removeWithFunction(connectionsByElementId[originalId], function(info) {
- return info[0].id === connection.id;
- });
- // find entry for target and update it
- var tIdx = jsPlumbUtil.findWithFunction(connectionsByElementId[connection.targetId], function(i) {
- return i[0].id === connection.id;
- });
- if (tIdx > -1) {
- connectionsByElementId[connection.targetId][tIdx][0] = connection;
- connectionsByElementId[connection.targetId][tIdx][1] = connection.endpoints[0];
- connectionsByElementId[connection.targetId][tIdx][2] = connection.endpoints[0].anchor.constructor == jsPlumb.DynamicAnchor;
- }
- // add entry for new source
- jsPlumbUtil.addToList(connectionsByElementId, newId, [connection, connection.endpoints[1], connection.endpoints[1].anchor.constructor == jsPlumb.DynamicAnchor]);
- };
-
- //
- // moves the given endpoint from `currentId` to `element`.
- // This involves:
- //
- // 1. changing the key in _amEndpoints under which the endpoint is stored
- // 2. changing the source or target values in all of the endpoint's connections
- // 3. changing the array in connectionsByElementId in which the endpoint's connections
- // are stored (done by either sourceChanged or updateOtherEndpoint)
- //
- this.rehomeEndpoint = function(ep, currentId, element) {
- var eps = _amEndpoints[currentId] || [],
- elementId = jsPlumbInstance.getId(element);
-
- if (elementId !== currentId) {
- var idx = jsPlumbUtil.indexOf(eps, ep);
- if (idx > -1) {
- var _ep = eps.splice(idx, 1)[0];
- self.add(_ep, elementId);
- }
- }
-
- for (var i = 0; i < ep.connections.length; i++) {
- if (ep.connections[i].sourceId == currentId) {
- ep.connections[i].sourceId = ep.elementId;
- ep.connections[i].source = ep.element;
- self.sourceChanged(currentId, ep.elementId, ep.connections[i]);
- }
- else if(ep.connections[i].targetId == currentId) {
- ep.connections[i].targetId = ep.elementId;
- ep.connections[i].target = ep.element;
- self.updateOtherEndpoint(ep.connections[i].sourceId, currentId, ep.elementId, ep.connections[i]);
- }
- }
- };
-
- this.redraw = function(elementId, ui, timestamp, offsetToUI, clearEdits, doNotRecalcEndpoint) {
-
- if (!jsPlumbInstance.isSuspendDrawing()) {
- // get all the endpoints for this element
- var ep = _amEndpoints[elementId] || [],
- endpointConnections = connectionsByElementId[elementId] || [],
- connectionsToPaint = [],
- endpointsToPaint = [],
- anchorsToUpdate = [];
-
- timestamp = timestamp || jsPlumbInstance.timestamp();
- // offsetToUI are values that would have been calculated in the dragManager when registering
- // an endpoint for an element that had a parent (somewhere in the hierarchy) that had been
- // registered as draggable.
- offsetToUI = offsetToUI || {left:0, top:0};
- if (ui) {
- ui = {
- left:ui.left + offsetToUI.left,
- top:ui.top + offsetToUI.top
- };
- }
-
- // valid for one paint cycle.
- var myOffset = jsPlumbInstance.updateOffset( { elId : elementId, offset : ui, recalc : false, timestamp : timestamp }),
- orientationCache = {};
-
- // actually, first we should compute the orientation of this element to all other elements to which
- // this element is connected with a continuous anchor (whether both ends of the connection have
- // a continuous anchor or just one)
-
- for (var i = 0; i < endpointConnections.length; i++) {
- var conn = endpointConnections[i][0],
- sourceId = conn.sourceId,
- targetId = conn.targetId,
- sourceContinuous = conn.endpoints[0].anchor.isContinuous,
- targetContinuous = conn.endpoints[1].anchor.isContinuous;
-
- if (sourceContinuous || targetContinuous) {
- var oKey = sourceId + "_" + targetId,
- oKey2 = targetId + "_" + sourceId,
- o = orientationCache[oKey],
- oIdx = conn.sourceId == elementId ? 1 : 0;
-
- if (sourceContinuous && !anchorLists[sourceId]) anchorLists[sourceId] = { top:[], right:[], bottom:[], left:[] };
- if (targetContinuous && !anchorLists[targetId]) anchorLists[targetId] = { top:[], right:[], bottom:[], left:[] };
-
- if (elementId != targetId) jsPlumbInstance.updateOffset( { elId : targetId, timestamp : timestamp });
- if (elementId != sourceId) jsPlumbInstance.updateOffset( { elId : sourceId, timestamp : timestamp });
-
- var td = jsPlumbInstance.getCachedData(targetId),
- sd = jsPlumbInstance.getCachedData(sourceId);
-
- if (targetId == sourceId && (sourceContinuous || targetContinuous)) {
- // here we may want to improve this by somehow determining the face we'd like
- // to put the connector on. ideally, when drawing, the face should be calculated
- // by determining which face is closest to the point at which the mouse button
- // was released. for now, we're putting it on the top face.
- _updateAnchorList(
- anchorLists[sourceId],
- -Math.PI / 2,
- 0,
- conn,
- false,
- targetId,
- 0, false, "top", sourceId, connectionsToPaint, endpointsToPaint);
- }
- else {
- if (!o) {
- o = calculateOrientation(sourceId, targetId, sd.o, td.o, conn.endpoints[0].anchor, conn.endpoints[1].anchor);
- orientationCache[oKey] = o;
- // this would be a performance enhancement, but the computed angles need to be clamped to
- //the (-PI/2 -> PI/2) range in order for the sorting to work properly.
- /* orientationCache[oKey2] = {
- orientation:o.orientation,
- a:[o.a[1], o.a[0]],
- theta:o.theta + Math.PI,
- theta2:o.theta2 + Math.PI
- };*/
- }
- if (sourceContinuous) _updateAnchorList(anchorLists[sourceId], o.theta, 0, conn, false, targetId, 0, false, o.a[0], sourceId, connectionsToPaint, endpointsToPaint);
- if (targetContinuous) _updateAnchorList(anchorLists[targetId], o.theta2, -1, conn, true, sourceId, 1, true, o.a[1], targetId, connectionsToPaint, endpointsToPaint);
- }
-
- if (sourceContinuous) jsPlumbUtil.addWithFunction(anchorsToUpdate, sourceId, function(a) { return a === sourceId; });
- if (targetContinuous) jsPlumbUtil.addWithFunction(anchorsToUpdate, targetId, function(a) { return a === targetId; });
- jsPlumbUtil.addWithFunction(connectionsToPaint, conn, function(c) { return c.id == conn.id; });
- if ((sourceContinuous && oIdx === 0) || (targetContinuous && oIdx === 1))
- jsPlumbUtil.addWithFunction(endpointsToPaint, conn.endpoints[oIdx], function(e) { return e.id == conn.endpoints[oIdx].id; });
- }
- }
- // place Endpoints whose anchors are continuous but have no Connections
- for (i = 0; i < ep.length; i++) {
- if (ep[i].connections.length === 0 && ep[i].anchor.isContinuous) {
- if (!anchorLists[elementId]) anchorLists[elementId] = { top:[], right:[], bottom:[], left:[] };
- _updateAnchorList(anchorLists[elementId], -Math.PI / 2, 0, {endpoints:[ep[i], ep[i]], paint:function(){}}, false, elementId, 0, false, "top", elementId, connectionsToPaint, endpointsToPaint);
- jsPlumbUtil.addWithFunction(anchorsToUpdate, elementId, function(a) { return a === elementId; });
- }
- }
- // now place all the continuous anchors we need to;
- for (i = 0; i < anchorsToUpdate.length; i++) {
- placeAnchors(anchorsToUpdate[i], anchorLists[anchorsToUpdate[i]]);
- }
-
- // now that continuous anchors have been placed, paint all the endpoints for this element
- // TODO performance: add the endpoint ids to a temp array, and then when iterating in the next
- // loop, check that we didn't just paint that endpoint. we can probably shave off a few more milliseconds this way.
- for (i = 0; i < ep.length; i++) {
- ep[i].paint( { timestamp : timestamp, offset : myOffset, dimensions : myOffset.s, recalc:doNotRecalcEndpoint !== true });
- }
- // ... and any other endpoints we came across as a result of the continuous anchors.
- for (i = 0; i < endpointsToPaint.length; i++) {
- var cd = jsPlumbInstance.getCachedData(endpointsToPaint[i].elementId);
- endpointsToPaint[i].paint( { timestamp : timestamp, offset : cd, dimensions : cd.s });
- }
-
- // paint all the standard and "dynamic connections", which are connections whose other anchor is
- // static and therefore does need to be recomputed; we make sure that happens only one time.
-
- // TODO we could have compiled a list of these in the first pass through connections; might save some time.
- for (i = 0; i < endpointConnections.length; i++) {
- var otherEndpoint = endpointConnections[i][1];
- if (otherEndpoint.anchor.constructor == jsPlumb.DynamicAnchor) {
- otherEndpoint.paint({ elementWithPrecedence:elementId, timestamp:timestamp });
- jsPlumbUtil.addWithFunction(connectionsToPaint, endpointConnections[i][0], function(c) { return c.id == endpointConnections[i][0].id; });
- // all the connections for the other endpoint now need to be repainted
- for (var k = 0; k < otherEndpoint.connections.length; k++) {
- if (otherEndpoint.connections[k] !== endpointConnections[i][0])
- jsPlumbUtil.addWithFunction(connectionsToPaint, otherEndpoint.connections[k], function(c) { return c.id == otherEndpoint.connections[k].id; });
- }
- } else if (otherEndpoint.anchor.constructor == jsPlumb.Anchor) {
- jsPlumbUtil.addWithFunction(connectionsToPaint, endpointConnections[i][0], function(c) { return c.id == endpointConnections[i][0].id; });
- }
- }
- // paint current floating connection for this element, if there is one.
- var fc = floatingConnections[elementId];
- if (fc)
- fc.paint({timestamp:timestamp, recalc:false, elId:elementId});
-
- // paint all the connections
- for (i = 0; i < connectionsToPaint.length; i++) {
- connectionsToPaint[i].paint({elId:elementId, timestamp:timestamp, recalc:false, clearEdits:clearEdits});
- }
- }
- };
-
- var ContinuousAnchor = function(anchorParams) {
- jsPlumbUtil.EventGenerator.apply(this);
- this.type = "Continuous";
- this.isDynamic = true;
- this.isContinuous = true;
- var faces = anchorParams.faces || ["top", "right", "bottom", "left"],
- clockwise = !(anchorParams.clockwise === false),
- availableFaces = { },
- opposites = { "top":"bottom", "right":"left","left":"right","bottom":"top" },
- clockwiseOptions = { "top":"right", "right":"bottom","left":"top","bottom":"left" },
- antiClockwiseOptions = { "top":"left", "right":"top","left":"bottom","bottom":"right" },
- secondBest = clockwise ? clockwiseOptions : antiClockwiseOptions,
- lastChoice = clockwise ? antiClockwiseOptions : clockwiseOptions,
- cssClass = anchorParams.cssClass || "";
-
- for (var i = 0; i < faces.length; i++) { availableFaces[faces[i]] = true; }
-
- // if the given edge is supported, returns it. otherwise looks for a substitute that _is_
- // supported. if none supported we also return the request edge.
- this.verifyEdge = function(edge) {
- if (availableFaces[edge]) return edge;
- else if (availableFaces[opposites[edge]]) return opposites[edge];
- else if (availableFaces[secondBest[edge]]) return secondBest[edge];
- else if (availableFaces[lastChoice[edge]]) return lastChoice[edge];
- return edge; // we have to give them something.
- };
-
- this.compute = function(params) {
- return userDefinedContinuousAnchorLocations[params.element.id] || continuousAnchorLocations[params.element.id] || [0,0];
- };
- this.getCurrentLocation = function(params) {
- return userDefinedContinuousAnchorLocations[params.element.id] || continuousAnchorLocations[params.element.id] || [0,0];
- };
- this.getOrientation = function(endpoint) {
- return continuousAnchorOrientations[endpoint.id] || [0,0];
- };
- this.clearUserDefinedLocation = function() {
- delete userDefinedContinuousAnchorLocations[anchorParams.elementId];
- };
- this.setUserDefinedLocation = function(loc) {
- userDefinedContinuousAnchorLocations[anchorParams.elementId] = loc;
- };
- this.getCssClass = function() { return cssClass; };
- this.setCssClass = function(c) { cssClass = c; };
- };
-
- // continuous anchors
- jsPlumbInstance.continuousAnchorFactory = {
- get:function(params) {
- var existing = continuousAnchors[params.elementId];
- if (!existing) {
- existing = new ContinuousAnchor(params);
- continuousAnchors[params.elementId] = existing;
- }
- return existing;
- },
- clear:function(elementId) {
- delete continuousAnchors[elementId];
- }
- };
- };
-
- /**
- * Anchors model a position on some element at which an Endpoint may be located. They began as a first class citizen of jsPlumb, ie. a user
- * was required to create these themselves, but over time this has been replaced by the concept of referring to them either by name (eg. "TopMiddle"),
- * or by an array describing their coordinates (eg. [ 0, 0.5, 0, -1 ], which is the same as "TopMiddle"). jsPlumb now handles all of the
- * creation of Anchors without user intervention.
- */
- jsPlumb.Anchor = function(params) {
- this.x = params.x || 0;
- this.y = params.y || 0;
- this.elementId = params.elementId;
- this.cssClass = params.cssClass || "";
- this.userDefinedLocation = null;
- this.orientation = params.orientation || [ 0, 0 ];
-
- jsPlumbUtil.EventGenerator.apply(this);
-
- var jsPlumbInstance = params.jsPlumbInstance;//,
- //lastTimestamp = null;//, lastReturnValue = null;
-
- this.lastReturnValue = null;
- this.offsets = params.offsets || [ 0, 0 ];
- this.timestamp = null;
- this.compute = function(params) {
-
- var xy = params.xy, wh = params.wh, element = params.element, timestamp = params.timestamp;
-
- if(params.clearUserDefinedLocation)
- this.userDefinedLocation = null;
-
- if (timestamp && timestamp === self.timestamp)
- return this.lastReturnValue;
-
- if (this.userDefinedLocation != null) {
- this.lastReturnValue = this.userDefinedLocation;
- }
- else {
-
- this.lastReturnValue = [ xy[0] + (this.x * wh[0]) + this.offsets[0], xy[1] + (this.y * wh[1]) + this.offsets[1] ];
- // adjust loc if there is an offsetParent
- this.lastReturnValue = jsPlumbInstance.adjustForParentOffsetAndScroll(this.lastReturnValue, element.canvas);
- }
-
- this.timestamp = timestamp;
- return this.lastReturnValue;
- };
-
- this.getCurrentLocation = function(params) {
- return (this.lastReturnValue == null || (params.timestamp != null && this.timestamp != params.timestamp)) ? this.compute(params) : this.lastReturnValue;
- };
- };
- jsPlumbUtil.extend(jsPlumb.Anchor, jsPlumbUtil.EventGenerator, {
- equals : function(anchor) {
- if (!anchor) return false;
- var ao = anchor.getOrientation(),
- o = this.getOrientation();
- return this.x == anchor.x && this.y == anchor.y && this.offsets[0] == anchor.offsets[0] && this.offsets[1] == anchor.offsets[1] && o[0] == ao[0] && o[1] == ao[1];
- },
- getUserDefinedLocation : function() {
- return this.userDefinedLocation;
- },
- setUserDefinedLocation : function(l) {
- this.userDefinedLocation = l;
- },
- clearUserDefinedLocation : function() {
- this.userDefinedLocation = null;
- },
- getOrientation : function(_endpoint) { return this.orientation; },
- getCssClass : function() { return this.cssClass; }
- });
-
- /**
- * An Anchor that floats. its orientation is computed dynamically from
- * its position relative to the anchor it is floating relative to. It is used when creating
- * a connection through drag and drop.
- *
- * TODO FloatingAnchor could totally be refactored to extend Anchor just slightly.
- */
- jsPlumb.FloatingAnchor = function(params) {
-
- jsPlumb.Anchor.apply(this, arguments);
-
- // this is the anchor that this floating anchor is referenced to for
- // purposes of calculating the orientation.
- var ref = params.reference,
- jpcl = jsPlumb.CurrentLibrary,
- jsPlumbInstance = params.jsPlumbInstance,
- // the canvas this refers to.
- refCanvas = params.referenceCanvas,
- size = jpcl.getSize(jpcl.getElementObject(refCanvas)),
- // these are used to store the current relative position of our
- // anchor wrt the reference anchor. they only indicate
- // direction, so have a value of 1 or -1 (or, very rarely, 0). these
- // values are written by the compute method, and read
- // by the getOrientation method.
- xDir = 0, yDir = 0,
- // temporary member used to store an orientation when the floating
- // anchor is hovering over another anchor.
- orientation = null,
- _lastResult = null;
-
- // clear from parent. we want floating anchor orientation to always be computed.
- this.orientation = null;
-
- // set these to 0 each; they are used by certain types of connectors in the loopback case,
- // when the connector is trying to clear the element it is on. but for floating anchor it's not
- // very important.
- this.x = 0; this.y = 0;
-
- this.isFloating = true;
-
- this.compute = function(params) {
- var xy = params.xy, element = params.element,
- result = [ xy[0] + (size[0] / 2), xy[1] + (size[1] / 2) ]; // return origin of the element. we may wish to improve this so that any object can be the drag proxy.
-
- // adjust loc if there is an offsetParent
- result = jsPlumbInstance.adjustForParentOffsetAndScroll(result, element.canvas);
-
- _lastResult = result;
- return result;
- };
-
- this.getOrientation = function(_endpoint) {
- if (orientation) return orientation;
- else {
- var o = ref.getOrientation(_endpoint);
- // here we take into account the orientation of the other
- // anchor: if it declares zero for some direction, we declare zero too. this might not be the most awesome. perhaps we can come
- // up with a better way. it's just so that the line we draw looks like it makes sense. maybe this wont make sense.
- return [ Math.abs(o[0]) * xDir * -1,
- Math.abs(o[1]) * yDir * -1 ];
- }
- };
-
- /**
- * notification the endpoint associated with this anchor is hovering
- * over another anchor; we want to assume that anchor's orientation
- * for the duration of the hover.
- */
- this.over = function(anchor, endpoint) {
- orientation = anchor.getOrientation(endpoint);
- };
-
- /**
- * notification the endpoint associated with this anchor is no
- * longer hovering over another anchor; we should resume calculating
- * orientation as we normally do.
- */
- this.out = function() { orientation = null; };
-
- this.getCurrentLocation = function(params) { return _lastResult == null ? this.compute(params) : _lastResult; };
- };
- jsPlumbUtil.extend(jsPlumb.FloatingAnchor, jsPlumb.Anchor);
-
- var _convertAnchor = function(anchor, jsPlumbInstance, elementId) {
- return anchor.constructor == jsPlumb.Anchor ? anchor: jsPlumbInstance.makeAnchor(anchor, elementId, jsPlumbInstance);
- };
-
- /*
- * A DynamicAnchor is an Anchor that contains a list of other Anchors, which it cycles
- * through at compute time to find the one that is located closest to
- * the center of the target element, and returns that Anchor's compute
- * method result. this causes endpoints to follow each other with
- * respect to the orientation of their target elements, which is a useful
- * feature for some applications.
- *
- */
- jsPlumb.DynamicAnchor = function(params) {
- jsPlumb.Anchor.apply(this, arguments);
-
- this.isSelective = true;
- this.isDynamic = true;
- this.anchors = [];
- this.elementId = params.elementId;
- this.jsPlumbInstance = params.jsPlumbInstance;
-
- for (var i = 0; i < params.anchors.length; i++)
- this.anchors[i] = _convertAnchor(params.anchors[i], this.jsPlumbInstance, this.elementId);
- this.addAnchor = function(anchor) { this.anchors.push(_convertAnchor(anchor, this.jsPlumbInstance, this.elementId)); };
- this.getAnchors = function() { return this.anchors; };
- this.locked = false;
- var _curAnchor = this.anchors.length > 0 ? this.anchors[0] : null,
- _curIndex = this.anchors.length > 0 ? 0 : -1,
- _lastAnchor = _curAnchor,
- self = this,
-
- // helper method to calculate the distance between the centers of the two elements.
- _distance = function(anchor, cx, cy, xy, wh) {
- var ax = xy[0] + (anchor.x * wh[0]), ay = xy[1] + (anchor.y * wh[1]),
- acx = xy[0] + (wh[0] / 2), acy = xy[1] + (wh[1] / 2);
- return (Math.sqrt(Math.pow(cx - ax, 2) + Math.pow(cy - ay, 2)) +
- Math.sqrt(Math.pow(acx - ax, 2) + Math.pow(acy - ay, 2)));
- },
- // default method uses distance between element centers. you can provide your own method in the dynamic anchor
- // constructor (and also to jsPlumb.makeDynamicAnchor). the arguments to it are four arrays:
- // xy - xy loc of the anchor's element
- // wh - anchor's element's dimensions
- // txy - xy loc of the element of the other anchor in the connection
- // twh - dimensions of the element of the other anchor in the connection.
- // anchors - the list of selectable anchors
- _anchorSelector = params.selector || function(xy, wh, txy, twh, anchors) {
- var cx = txy[0] + (twh[0] / 2), cy = txy[1] + (twh[1] / 2);
- var minIdx = -1, minDist = Infinity;
- for ( var i = 0; i < anchors.length; i++) {
- var d = _distance(anchors[i], cx, cy, xy, wh);
- if (d < minDist) {
- minIdx = i + 0;
- minDist = d;
- }
- }
- return anchors[minIdx];
- };
-
- this.compute = function(params) {
- var xy = params.xy, wh = params.wh, timestamp = params.timestamp, txy = params.txy, twh = params.twh;
-
- if(params.clearUserDefinedLocation)
- userDefinedLocation = null;
-
- this.timestamp = timestamp;
-
- var udl = self.getUserDefinedLocation();
- if (udl != null) {
- return udl;
- }
-
- // if anchor is locked or an opposite element was not given, we
- // maintain our state. anchor will be locked
- // if it is the source of a drag and drop.
- if (this.locked || txy == null || twh == null)
- return _curAnchor.compute(params);
- else
- params.timestamp = null; // otherwise clear this, i think. we want the anchor to compute.
-
- _curAnchor = _anchorSelector(xy, wh, txy, twh, this.anchors);
- this.x = _curAnchor.x;
- this.y = _curAnchor.y;
-
- if (_curAnchor != _lastAnchor)
- this.fire("anchorChanged", _curAnchor);
-
- _lastAnchor = _curAnchor;
-
- return _curAnchor.compute(params);
- };
-
- this.getCurrentLocation = function(params) {
- return this.getUserDefinedLocation() || (_curAnchor != null ? _curAnchor.getCurrentLocation(params) : null);
- };
-
- this.getOrientation = function(_endpoint) { return _curAnchor != null ? _curAnchor.getOrientation(_endpoint) : [ 0, 0 ]; };
- this.over = function(anchor, endpoint) { if (_curAnchor != null) _curAnchor.over(anchor, endpoint); };
- this.out = function() { if (_curAnchor != null) _curAnchor.out(); };
-
- this.getCssClass = function() { return (_curAnchor && _curAnchor.getCssClass()) || ""; };
- };
- jsPlumbUtil.extend(jsPlumb.DynamicAnchor, jsPlumb.Anchor);
-
-// -------- basic anchors ------------------
- var _curryAnchor = function(x, y, ox, oy, type, fnInit) {
- jsPlumb.Anchors[type] = function(params) {
- var a = params.jsPlumbInstance.makeAnchor([ x, y, ox, oy, 0, 0 ], params.elementId, params.jsPlumbInstance);
- a.type = type;
- if (fnInit) fnInit(a, params);
- return a;
- };
- };
-
- _curryAnchor(0.5, 0, 0,-1, "TopCenter");
- _curryAnchor(0.5, 1, 0, 1, "BottomCenter");
- _curryAnchor(0, 0.5, -1, 0, "LeftMiddle");
- _curryAnchor(1, 0.5, 1, 0, "RightMiddle");
- // from 1.4.2: Top, Right, Bottom, Left
- _curryAnchor(0.5, 0, 0,-1, "Top");
- _curryAnchor(0.5, 1, 0, 1, "Bottom");
- _curryAnchor(0, 0.5, -1, 0, "Left");
- _curryAnchor(1, 0.5, 1, 0, "Right");
- _curryAnchor(0.5, 0.5, 0, 0, "Center");
- _curryAnchor(1, 0, 0,-1, "TopRight");
- _curryAnchor(1, 1, 0, 1, "BottomRight");
- _curryAnchor(0, 0, 0, -1, "TopLeft");
- _curryAnchor(0, 1, 0, 1, "BottomLeft");
-
-// ------- dynamic anchors -------------------
-
- // default dynamic anchors chooses from Top, Right, Bottom, Left
- jsPlumb.Defaults.DynamicAnchors = function(params) {
- return params.jsPlumbInstance.makeAnchors(["TopCenter", "RightMiddle", "BottomCenter", "LeftMiddle"], params.elementId, params.jsPlumbInstance);
- };
-
- // default dynamic anchors bound to name 'AutoDefault'
- jsPlumb.Anchors.AutoDefault = function(params) {
- var a = params.jsPlumbInstance.makeDynamicAnchor(jsPlumb.Defaults.DynamicAnchors(params));
- a.type = "AutoDefault";
- return a;
- };
-
-// ------- continuous anchors -------------------
-
- var _curryContinuousAnchor = function(type, faces) {
- jsPlumb.Anchors[type] = function(params) {
- var a = params.jsPlumbInstance.makeAnchor(["Continuous", { faces:faces }], params.elementId, params.jsPlumbInstance);
- a.type = type;
- return a;
- };
- };
-
- jsPlumb.Anchors.Continuous = function(params) {
- return params.jsPlumbInstance.continuousAnchorFactory.get(params);
- };
-
- _curryContinuousAnchor("ContinuousLeft", ["left"]);
- _curryContinuousAnchor("ContinuousTop", ["top"]);
- _curryContinuousAnchor("ContinuousBottom", ["bottom"]);
- _curryContinuousAnchor("ContinuousRight", ["right"]);
-
-// ------- position assign anchors -------------------
-
- // this anchor type lets you assign the position at connection time.
- jsPlumb.Anchors.Assign = _curryAnchor(0, 0, 0, 0, "Assign", function(anchor, params) {
- // find what to use as the "position finder". the user may have supplied a String which represents
- // the id of a position finder in jsPlumb.AnchorPositionFinders, or the user may have supplied the
- // position finder as a function. we find out what to use and then set it on the anchor.
- var pf = params.position || "Fixed";
- anchor.positionFinder = pf.constructor == String ? params.jsPlumbInstance.AnchorPositionFinders[pf] : pf;
- // always set the constructor params; the position finder might need them later (the Grid one does,
- // for example)
- anchor.constructorParams = params;
- });
-
- // these are the default anchor positions finders, which are used by the makeTarget function. supplying
- // a position finder argument to that function allows you to specify where the resulting anchor will
- // be located
- jsPlumb.AnchorPositionFinders = {
- "Fixed": function(dp, ep, es, params) {
- return [ (dp.left - ep.left) / es[0], (dp.top - ep.top) / es[1] ];
- },
- "Grid":function(dp, ep, es, params) {
- var dx = dp.left - ep.left, dy = dp.top - ep.top,
- gx = es[0] / (params.grid[0]), gy = es[1] / (params.grid[1]),
- mx = Math.floor(dx / gx), my = Math.floor(dy / gy);
- return [ ((mx * gx) + (gx / 2)) / es[0], ((my * gy) + (gy / 2)) / es[1] ];
- }
- };
-
-// ------- perimeter anchors -------------------
-
- jsPlumb.Anchors.Perimeter = function(params) {
- params = params || {};
- var anchorCount = params.anchorCount || 60,
- shape = params.shape;
-
- if (!shape) throw new Error("no shape supplied to Perimeter Anchor type");
-
- var _circle = function() {
- var r = 0.5, step = Math.PI * 2 / anchorCount, current = 0, a = [];
- for (var i = 0; i < anchorCount; i++) {
- var x = r + (r * Math.sin(current)),
- y = r + (r * Math.cos(current));
- a.push( [ x, y, 0, 0 ] );
- current += step;
- }
- return a;
- },
- _path = function(segments) {
- var anchorsPerFace = anchorCount / segments.length, a = [],
- _computeFace = function(x1, y1, x2, y2, fractionalLength) {
- anchorsPerFace = anchorCount * fractionalLength;
- var dx = (x2 - x1) / anchorsPerFace, dy = (y2 - y1) / anchorsPerFace;
- for (var i = 0; i < anchorsPerFace; i++) {
- a.push( [
- x1 + (dx * i),
- y1 + (dy * i),
- 0,
- 0
- ]);
- }
- };
-
- for (var i = 0; i < segments.length; i++)
- _computeFace.apply(null, segments[i]);
-
- return a;
- },
- _shape = function(faces) {
- var s = [];
- for (var i = 0; i < faces.length; i++) {
- s.push([faces[i][0], faces[i][1], faces[i][2], faces[i][3], 1 / faces.length]);
- }
- return _path(s);
- },
- _rectangle = function() {
- return _shape([
- [ 0, 0, 1, 0 ], [ 1, 0, 1, 1 ], [ 1, 1, 0, 1 ], [ 0, 1, 0, 0 ]
- ]);
- };
-
- var _shapes = {
- "Circle":_circle,
- "Ellipse":_circle,
- "Diamond":function() {
- return _shape([
- [ 0.5, 0, 1, 0.5 ], [ 1, 0.5, 0.5, 1 ], [ 0.5, 1, 0, 0.5 ], [ 0, 0.5, 0.5, 0 ]
- ]);
- },
- "Rectangle":_rectangle,
- "Square":_rectangle,
- "Triangle":function() {
- return _shape([
- [ 0.5, 0, 1, 1 ], [ 1, 1, 0, 1 ], [ 0, 1, 0.5, 0]
- ]);
- },
- "Path":function(params) {
- var points = params.points, p = [], tl = 0;
- for (var i = 0; i < points.length - 1; i++) {
- var l = Math.sqrt(Math.pow(points[i][2] - points[i][0]) + Math.pow(points[i][3] - points[i][1]));
- tl += l;
- p.push([points[i][0], points[i][1], points[i+1][0], points[i+1][1], l]);
- }
- for (var j = 0; j < p.length; j++) {
- p[j][4] = p[j][4] / tl;
- }
- return _path(p);
- }
- },
- _rotate = function(points, amountInDegrees) {
- var o = [], theta = amountInDegrees / 180 * Math.PI ;
- for (var i = 0; i < points.length; i++) {
- var _x = points[i][0] - 0.5,
- _y = points[i][1] - 0.5;
-
- o.push([
- 0.5 + ((_x * Math.cos(theta)) - (_y * Math.sin(theta))),
- 0.5 + ((_x * Math.sin(theta)) + (_y * Math.cos(theta))),
- points[i][2],
- points[i][3]
- ]);
- }
- return o;
- };
-
- if (!_shapes[shape]) throw new Error("Shape [" + shape + "] is unknown by Perimeter Anchor type");
-
- var da = _shapes[shape](params);
- if (params.rotation) da = _rotate(da, params.rotation);
- var a = params.jsPlumbInstance.makeDynamicAnchor(da);
- a.type = "Perimeter";
- return a;
- };
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the default Connectors, Endpoint and Overlay definitions.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-;(function() {
-
- /**
- *
- * Helper class to consume unused mouse events by components that are DOM elements and
- * are used by all of the different rendering modes.
- *
- */
- jsPlumb.DOMElementComponent = jsPlumbUtil.extend(jsPlumb.jsPlumbUIComponent, function(params) {
- // when render mode is canvas, these functions may be called by the canvas mouse handler.
- // this component is safe to pipe this stuff to /dev/null.
- this.mousemove =
- this.dblclick =
- this.click =
- this.mousedown =
- this.mouseup = function(e) { };
- });
-
- jsPlumb.Segments = {
-
- /*
- * Class: AbstractSegment
- * A Connector is made up of 1..N Segments, each of which has a Type, such as 'Straight', 'Arc',
- * 'Bezier'. This is new from 1.4.2, and gives us a lot more flexibility when drawing connections: things such
- * as rounded corners for flowchart connectors, for example, or a straight line stub for Bezier connections, are
- * much easier to do now.
- *
- * A Segment is responsible for providing coordinates for painting it, and also must be able to report its length.
- *
- */
- AbstractSegment : function(params) {
- this.params = params;
-
- /**
- * Function: findClosestPointOnPath
- * Finds the closest point on this segment to the given [x, y],
- * returning both the x and y of the point plus its distance from
- * the supplied point, and its location along the length of the
- * path inscribed by the segment. This implementation returns
- * Infinity for distance and null values for everything else;
- * subclasses are expected to override.
- */
- this.findClosestPointOnPath = function(x, y) {
- return {
- d:Infinity,
- x:null,
- y:null,
- l:null
- };
- };
-
- this.getBounds = function() {
- return {
- minX:Math.min(params.x1, params.x2),
- minY:Math.min(params.y1, params.y2),
- maxX:Math.max(params.x1, params.x2),
- maxY:Math.max(params.y1, params.y2)
- };
- };
- },
- Straight : function(params) {
- var _super = jsPlumb.Segments.AbstractSegment.apply(this, arguments),
- length, m, m2, x1, x2, y1, y2,
- _recalc = function() {
- length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
- m = jsPlumbUtil.gradient({x:x1, y:y1}, {x:x2, y:y2});
- m2 = -1 / m;
- };
-
- this.type = "Straight";
-
- this.getLength = function() { return length; };
- this.getGradient = function() { return m; };
-
- this.getCoordinates = function() {
- return { x1:x1,y1:y1,x2:x2,y2:y2 };
- };
- this.setCoordinates = function(coords) {
- x1 = coords.x1; y1 = coords.y1; x2 = coords.x2; y2 = coords.y2;
- _recalc();
- };
- this.setCoordinates({x1:params.x1, y1:params.y1, x2:params.x2, y2:params.y2});
-
- this.getBounds = function() {
- return {
- minX:Math.min(x1, x2),
- minY:Math.min(y1, y2),
- maxX:Math.max(x1, x2),
- maxY:Math.max(y1, y2)
- };
- };
-
- /**
- * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from
- * 0 to 1 inclusive. for the straight line segment this is simple maths.
- */
- this.pointOnPath = function(location, absolute) {
- if (location === 0 && !absolute)
- return { x:x1, y:y1 };
- else if (location == 1 && !absolute)
- return { x:x2, y:y2 };
- else {
- var l = absolute ? location > 0 ? location : length + location : location * length;
- return jsPlumbUtil.pointOnLine({x:x1, y:y1}, {x:x2, y:y2}, l);
- }
- };
-
- /**
- * returns the gradient of the segment at the given point - which for us is constant.
- */
- this.gradientAtPoint = function(_) {
- return m;
- };
-
- /**
- * returns the point on the segment's path that is 'distance' along the length of the path from 'location', where
- * 'location' is a decimal from 0 to 1 inclusive, and 'distance' is a number of pixels.
- * this hands off to jsPlumbUtil to do the maths, supplying two points and the distance.
- */
- this.pointAlongPathFrom = function(location, distance, absolute) {
- var p = this.pointOnPath(location, absolute),
- farAwayPoint = distance <= 0 ? {x:x1, y:y1} : {x:x2, y:y2 };
-
- /*
- location == 1 ? {
- x:x1 + ((x2 - x1) * 10),
- y:y1 + ((y1 - y2) * 10)
- } :
- */
-
- if (distance <= 0 && Math.abs(distance) > 1) distance *= -1;
-
- return jsPlumbUtil.pointOnLine(p, farAwayPoint, distance);
- };
-
- /**
- Function: findClosestPointOnPath
- Finds the closest point on this segment to [x,y]. See
- notes on this method in AbstractSegment.
- */
- this.findClosestPointOnPath = function(x, y) {
- if (m === 0) {
- return {
- x:x,
- y:y1,
- d:Math.abs(y - y1)
- };
- }
- else if (m == Infinity || m == -Infinity) {
- return {
- x:x1,
- y:y,
- d:Math.abs(x - 1)
- };
- }
- else {
- // closest point lies on normal from given point to this line.
- var b = y1 - (m * x1),
- b2 = y - (m2 * x),
- // y1 = m.x1 + b and y1 = m2.x1 + b2
- // so m.x1 + b = m2.x1 + b2
- // x1(m - m2) = b2 - b
- // x1 = (b2 - b) / (m - m2)
- _x1 = (b2 -b) / (m - m2),
- _y1 = (m * _x1) + b,
- d = jsPlumbUtil.lineLength([ x, y ], [ _x1, _y1 ]),
- fractionInSegment = jsPlumbUtil.lineLength([ _x1, _y1 ], [ x1, y1 ]);
-
- return { d:d, x:_x1, y:_y1, l:fractionInSegment / length};
- }
- };
- },
-
- /*
- Arc Segment. You need to supply:
-
- r - radius
- cx - center x for the arc
- cy - center y for the arc
- ac - whether the arc is anticlockwise or not. default is clockwise.
-
- and then either:
-
- startAngle - startAngle for the arc.
- endAngle - endAngle for the arc.
-
- or:
-
- x1 - x for start point
- y1 - y for start point
- x2 - x for end point
- y2 - y for end point
-
- */
- Arc : function(params) {
- var _super = jsPlumb.Segments.AbstractSegment.apply(this, arguments),
- _calcAngle = function(_x, _y) {
- return jsPlumbUtil.theta([params.cx, params.cy], [_x, _y]);
- },
- _calcAngleForLocation = function(segment, location) {
- if (segment.anticlockwise) {
- var sa = segment.startAngle < segment.endAngle ? segment.startAngle + TWO_PI : segment.startAngle,
- s = Math.abs(sa - segment.endAngle);
- return sa - (s * location);
- }
- else {
- var ea = segment.endAngle < segment.startAngle ? segment.endAngle + TWO_PI : segment.endAngle,
- ss = Math.abs (ea - segment.startAngle);
-
- return segment.startAngle + (ss * location);
- }
- },
- TWO_PI = 2 * Math.PI;
-
- this.radius = params.r;
- this.anticlockwise = params.ac;
- this.type = "Arc";
-
- if (params.startAngle && params.endAngle) {
- this.startAngle = params.startAngle;
- this.endAngle = params.endAngle;
- this.x1 = params.cx + (this.radius * Math.cos(params.startAngle));
- this.y1 = params.cy + (this.radius * Math.sin(params.startAngle));
- this.x2 = params.cx + (this.radius * Math.cos(params.endAngle));
- this.y2 = params.cy + (this.radius * Math.sin(params.endAngle));
- }
- else {
- this.startAngle = _calcAngle(params.x1, params.y1);
- this.endAngle = _calcAngle(params.x2, params.y2);
- this.x1 = params.x1;
- this.y1 = params.y1;
- this.x2 = params.x2;
- this.y2 = params.y2;
- }
-
- if (this.endAngle < 0) this.endAngle += TWO_PI;
- if (this.startAngle < 0) this.startAngle += TWO_PI;
-
- // segment is used by vml
- this.segment = jsPlumbUtil.segment([this.x1, this.y1], [this.x2, this.y2]);
-
- // we now have startAngle and endAngle as positive numbers, meaning the
- // absolute difference (|d|) between them is the sweep (s) of this arc, unless the
- // arc is 'anticlockwise' in which case 's' is given by 2PI - |d|.
-
- var ea = this.endAngle < this.startAngle ? this.endAngle + TWO_PI : this.endAngle;
- this.sweep = Math.abs (ea - this.startAngle);
- if (this.anticlockwise) this.sweep = TWO_PI - this.sweep;
- var circumference = 2 * Math.PI * this.radius,
- frac = this.sweep / TWO_PI,
- length = circumference * frac;
-
- this.getLength = function() {
- return length;
- };
-
- this.getBounds = function() {
- return {
- minX:params.cx - params.r,
- maxX:params.cx + params.r,
- minY:params.cy - params.r,
- maxY:params.cy + params.r
- };
- };
-
- var VERY_SMALL_VALUE = 0.0000000001,
- gentleRound = function(n) {
- var f = Math.floor(n), r = Math.ceil(n);
- if (n - f < VERY_SMALL_VALUE)
- return f;
- else if (r - n < VERY_SMALL_VALUE)
- return r;
- return n;
- };
-
- /**
- * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from
- * 0 to 1 inclusive.
- */
- this.pointOnPath = function(location, absolute) {
-
- if (location === 0) {
- return { x:this.x1, y:this.y1, theta:this.startAngle };
- }
- else if (location == 1) {
- return { x:this.x2, y:this.y2, theta:this.endAngle };
- }
-
- if (absolute) {
- location = location / length;
- }
-
- var angle = _calcAngleForLocation(this, location),
- _x = params.cx + (params.r * Math.cos(angle)),
- _y = params.cy + (params.r * Math.sin(angle));
-
- return { x:gentleRound(_x), y:gentleRound(_y), theta:angle };
- };
-
- /**
- * returns the gradient of the segment at the given point.
- */
- this.gradientAtPoint = function(location, absolute) {
- var p = this.pointOnPath(location, absolute);
- var m = jsPlumbUtil.normal( [ params.cx, params.cy ], [p.x, p.y ] );
- if (!this.anticlockwise && (m == Infinity || m == -Infinity)) m *= -1;
- return m;
- };
-
- this.pointAlongPathFrom = function(location, distance, absolute) {
- var p = this.pointOnPath(location, absolute),
- arcSpan = distance / circumference * 2 * Math.PI,
- dir = this.anticlockwise ? -1 : 1,
- startAngle = p.theta + (dir * arcSpan),
- startX = params.cx + (this.radius * Math.cos(startAngle)),
- startY = params.cy + (this.radius * Math.sin(startAngle));
-
- return {x:startX, y:startY};
- };
- },
-
- Bezier : function(params) {
- var _super = jsPlumb.Segments.AbstractSegment.apply(this, arguments),
- curve = [
- { x:params.x1, y:params.y1},
- { x:params.cp1x, y:params.cp1y },
- { x:params.cp2x, y:params.cp2y },
- { x:params.x2, y:params.y2 }
- ],
- // although this is not a strictly rigorous determination of bounds
- // of a bezier curve, it works for the types of curves that this segment
- // type produces.
- bounds = {
- minX:Math.min(params.x1, params.x2, params.cp1x, params.cp2x),
- minY:Math.min(params.y1, params.y2, params.cp1y, params.cp2y),
- maxX:Math.max(params.x1, params.x2, params.cp1x, params.cp2x),
- maxY:Math.max(params.y1, params.y2, params.cp1y, params.cp2y)
- };
-
- this.type = "Bezier";
-
- var _translateLocation = function(_curve, location, absolute) {
- if (absolute)
- location = jsBezier.locationAlongCurveFrom(_curve, location > 0 ? 0 : 1, location);
-
- return location;
- };
-
- /**
- * returns the point on the segment's path that is 'location' along the length of the path, where 'location' is a decimal from
- * 0 to 1 inclusive.
- */
- this.pointOnPath = function(location, absolute) {
- location = _translateLocation(curve, location, absolute);
- return jsBezier.pointOnCurve(curve, location);
- };
-
- /**
- * returns the gradient of the segment at the given point.
- */
- this.gradientAtPoint = function(location, absolute) {
- location = _translateLocation(curve, location, absolute);
- return jsBezier.gradientAtPoint(curve, location);
- };
-
- this.pointAlongPathFrom = function(location, distance, absolute) {
- location = _translateLocation(curve, location, absolute);
- return jsBezier.pointAlongCurveFrom(curve, location, distance);
- };
-
- this.getLength = function() {
- return jsBezier.getLength(curve);
- };
-
- this.getBounds = function() {
- return bounds;
- };
- }
- };
-
- /*
- Class: AbstractComponent
- Superclass for AbstractConnector and AbstractEndpoint.
- */
- var AbstractComponent = function() {
- this.resetBounds = function() {
- this.bounds = { minX:Infinity, minY:Infinity, maxX:-Infinity, maxY:-Infinity };
- };
- this.resetBounds();
- };
-
- /*
- * Class: AbstractConnector
- * Superclass for all Connectors; here is where Segments are managed. This is exposed on jsPlumb just so it
- * can be accessed from other files. You should not try to instantiate one of these directly.
- *
- * When this class is asked for a pointOnPath, or gradient etc, it must first figure out which segment to dispatch
- * that request to. This is done by keeping track of the total connector length as segments are added, and also
- * their cumulative ratios to the total length. Then when the right segment is found it is a simple case of dispatching
- * the request to it (and adjusting 'location' so that it is relative to the beginning of that segment.)
- */
- jsPlumb.Connectors.AbstractConnector = function(params) {
-
- AbstractComponent.apply(this, arguments);
-
- var //self = this,
- segments = [],
- editing = false,
- totalLength = 0,
- segmentProportions = [],
- segmentProportionalLengths = [],
- stub = params.stub || 0,
- sourceStub = jsPlumbUtil.isArray(stub) ? stub[0] : stub,
- targetStub = jsPlumbUtil.isArray(stub) ? stub[1] : stub,
- gap = params.gap || 0,
- sourceGap = jsPlumbUtil.isArray(gap) ? gap[0] : gap,
- targetGap = jsPlumbUtil.isArray(gap) ? gap[1] : gap,
- userProvidedSegments = null,
- edited = false,
- paintInfo = null;
-
- // subclasses should override.
- this.isEditable = function() { return false; };
- this.setEdited = function(ed) { edited = ed; };
-
- // to be overridden by subclasses.
- this.getPath = function() { };
- this.setPath = function(path) { };
-
- /**
- * Function: findSegmentForPoint
- * Returns the segment that is closest to the given [x,y],
- * null if nothing found. This function returns a JS
- * object with:
- *
- * d - distance from segment
- * l - proportional location in segment
- * x - x point on the segment
- * y - y point on the segment
- * s - the segment itself.
- */
- this.findSegmentForPoint = function(x, y) {
- var out = { d:Infinity, s:null, x:null, y:null, l:null };
- for (var i = 0; i < segments.length; i++) {
- var _s = segments[i].findClosestPointOnPath(x, y);
- if (_s.d < out.d) {
- out.d = _s.d;
- out.l = _s.l;
- out.x = _s.x;
- out.y = _s.y;
- out.s = segments[i];
- }
- }
-
- return out;
- };
-
- var _updateSegmentProportions = function() {
- var curLoc = 0;
- for (var i = 0; i < segments.length; i++) {
- var sl = segments[i].getLength();
- segmentProportionalLengths[i] = sl / totalLength;
- segmentProportions[i] = [curLoc, (curLoc += (sl / totalLength)) ];
- }
- },
-
- /**
- * returns [segment, proportion of travel in segment, segment index] for the segment
- * that contains the point which is 'location' distance along the entire path, where
- * 'location' is a decimal between 0 and 1 inclusive. in this connector type, paths
- * are made up of a list of segments, each of which contributes some fraction to
- * the total length.
- * From 1.3.10 this also supports the 'absolute' property, which lets us specify a location
- * as the absolute distance in pixels, rather than a proportion of the total path.
- */
- _findSegmentForLocation = function(location, absolute) {
- if (absolute) {
- location = location > 0 ? location / totalLength : (totalLength + location) / totalLength;
- }
-
- var idx = segmentProportions.length - 1, inSegmentProportion = 1;
- //if (location < 1) {
- for (var i = 0; i < segmentProportions.length; i++) {
- if (segmentProportions[i][1] >= location) {
- idx = i;
- // todo is this correct for all connector path types?
- inSegmentProportion = location == 1 ? 1 : location === 0 ? 0 : (location - segmentProportions[i][0]) / segmentProportionalLengths[i];
- break;
- }
- }
- //}
- return { segment:segments[idx], proportion:inSegmentProportion, index:idx };
- },
- _addSegment = function(conn, type, params) {
- if (params.x1 == params.x2 && params.y1 == params.y2) return;
- var s = new jsPlumb.Segments[type](params);
- segments.push(s);
- totalLength += s.getLength();
- conn.updateBounds(s);
- },
- _clearSegments = function() {
- totalLength = 0;
- segments.splice(0, segments.length);
- segmentProportions.splice(0, segmentProportions.length);
- segmentProportionalLengths.splice(0, segmentProportionalLengths.length);
- };
-
- this.setSegments = function(_segs) {
- userProvidedSegments = [];
- totalLength = 0;
- for (var i = 0; i < _segs.length; i++) {
- userProvidedSegments.push(_segs[i]);
- totalLength += _segs[i].getLength();
- }
- };
-
- var _prepareCompute = function(params) {
- this.lineWidth = params.lineWidth;
- var segment = jsPlumbUtil.segment(params.sourcePos, params.targetPos),
- swapX = params.targetPos[0] < params.sourcePos[0],
- swapY = params.targetPos[1] < params.sourcePos[1],
- lw = params.lineWidth || 1,
- so = params.sourceEndpoint.anchor.orientation || params.sourceEndpoint.anchor.getOrientation(params.sourceEndpoint),
- to = params.targetEndpoint.anchor.orientation || params.targetEndpoint.anchor.getOrientation(params.targetEndpoint),
- x = swapX ? params.targetPos[0] : params.sourcePos[0],
- y = swapY ? params.targetPos[1] : params.sourcePos[1],
- w = Math.abs(params.targetPos[0] - params.sourcePos[0]),
- h = Math.abs(params.targetPos[1] - params.sourcePos[1]);
-
- // if either anchor does not have an orientation set, we derive one from their relative
- // positions. we fix the axis to be the one in which the two elements are further apart, and
- // point each anchor at the other element. this is also used when dragging a new connection.
- if (so[0] === 0 && so[1] === 0 || to[0] === 0 && to[1] === 0) {
- var index = w > h ? 0 : 1, oIndex = [1,0][index];
- so = []; to = [];
- so[index] = params.sourcePos[index] > params.targetPos[index] ? -1 : 1;
- to[index] = params.sourcePos[index] > params.targetPos[index] ? 1 : -1;
- so[oIndex] = 0; to[oIndex] = 0;
- }
-
- var sx = swapX ? w + (sourceGap * so[0]) : sourceGap * so[0],
- sy = swapY ? h + (sourceGap * so[1]) : sourceGap * so[1],
- tx = swapX ? targetGap * to[0] : w + (targetGap * to[0]),
- ty = swapY ? targetGap * to[1] : h + (targetGap * to[1]),
- oProduct = ((so[0] * to[0]) + (so[1] * to[1]));
-
- var result = {
- sx:sx, sy:sy, tx:tx, ty:ty, lw:lw,
- xSpan:Math.abs(tx - sx),
- ySpan:Math.abs(ty - sy),
- mx:(sx + tx) / 2,
- my:(sy + ty) / 2,
- so:so, to:to, x:x, y:y, w:w, h:h,
- segment : segment,
- startStubX : sx + (so[0] * sourceStub),
- startStubY : sy + (so[1] * sourceStub),
- endStubX : tx + (to[0] * targetStub),
- endStubY : ty + (to[1] * targetStub),
- isXGreaterThanStubTimes2 : Math.abs(sx - tx) > (sourceStub + targetStub),
- isYGreaterThanStubTimes2 : Math.abs(sy - ty) > (sourceStub + targetStub),
- opposite:oProduct == -1,
- perpendicular:oProduct === 0,
- orthogonal:oProduct == 1,
- sourceAxis : so[0] === 0 ? "y" : "x",
- points:[x, y, w, h, sx, sy, tx, ty ]
- };
- result.anchorOrientation = result.opposite ? "opposite" : result.orthogonal ? "orthogonal" : "perpendicular";
- return result;
- };
-
- this.getSegments = function() { return segments; };
-
- this.updateBounds = function(segment) {
- var segBounds = segment.getBounds();
- this.bounds.minX = Math.min(this.bounds.minX, segBounds.minX);
- this.bounds.maxX = Math.max(this.bounds.maxX, segBounds.maxX);
- this.bounds.minY = Math.min(this.bounds.minY, segBounds.minY);
- this.bounds.maxY = Math.max(this.bounds.maxY, segBounds.maxY);
- };
-
- var dumpSegmentsToConsole = function() {
- console.log("SEGMENTS:");
- for (var i = 0; i < segments.length; i++) {
- console.log(segments[i].type, segments[i].getLength(), segmentProportions[i]);
- }
- };
-
- this.pointOnPath = function(location, absolute) {
- var seg = _findSegmentForLocation(location, absolute);
- return seg.segment && seg.segment.pointOnPath(seg.proportion, absolute) || [0,0];
- };
-
- this.gradientAtPoint = function(location) {
- var seg = _findSegmentForLocation(location, absolute);
- return seg.segment && seg.segment.gradientAtPoint(seg.proportion, absolute) || 0;
- };
-
- this.pointAlongPathFrom = function(location, distance, absolute) {
- var seg = _findSegmentForLocation(location, absolute);
- // TODO what happens if this crosses to the next segment?
- return seg.segment && seg.segment.pointAlongPathFrom(seg.proportion, distance, false) || [0,0];
- };
-
- this.compute = function(params) {
- if (!edited)
- paintInfo = _prepareCompute.call(this, params);
-
- _clearSegments();
- this._compute(paintInfo, params);
- this.x = paintInfo.points[0];
- this.y = paintInfo.points[1];
- this.w = paintInfo.points[2];
- this.h = paintInfo.points[3];
- this.segment = paintInfo.segment;
- _updateSegmentProportions();
- };
-
- return {
- addSegment:_addSegment,
- prepareCompute:_prepareCompute,
- sourceStub:sourceStub,
- targetStub:targetStub,
- maxStub:Math.max(sourceStub, targetStub),
- sourceGap:sourceGap,
- targetGap:targetGap,
- maxGap:Math.max(sourceGap, targetGap)
- };
- };
- jsPlumbUtil.extend(jsPlumb.Connectors.AbstractConnector, AbstractComponent);
-
- /**
- * Class: Connectors.Straight
- * The Straight connector draws a simple straight line between the two anchor points. It does not have any constructor parameters.
- */
- var Straight = function() {
- this.type = "Straight";
- var _super = jsPlumb.Connectors.AbstractConnector.apply(this, arguments);
-
- this._compute = function(paintInfo, _) {
- _super.addSegment(this, "Straight", {x1:paintInfo.sx, y1:paintInfo.sy, x2:paintInfo.startStubX, y2:paintInfo.startStubY});
- _super.addSegment(this, "Straight", {x1:paintInfo.startStubX, y1:paintInfo.startStubY, x2:paintInfo.endStubX, y2:paintInfo.endStubY});
- _super.addSegment(this, "Straight", {x1:paintInfo.endStubX, y1:paintInfo.endStubY, x2:paintInfo.tx, y2:paintInfo.ty});
- };
- };
- jsPlumbUtil.extend(jsPlumb.Connectors.Straight, jsPlumb.Connectors.AbstractConnector);
- jsPlumb.registerConnectorType(Straight, "Straight");
-
- /**
- * Class:Connectors.Bezier
- * This Connector draws a Bezier curve with two control points. You can provide a 'curviness' value which gets applied to jsPlumb's
- * internal voodoo machine and ends up generating locations for the two control points. See the constructor documentation below.
- */
- /**
- * Function:Constructor
- *
- * Parameters:
- * curviness - How 'curvy' you want the curve to be! This is a directive for the placement of control points, not endpoints of the curve, so your curve does not
- * actually touch the given point, but it has the tendency to lean towards it. The larger this value, the greater the curve is pulled from a straight line.
- * Optional; defaults to 150.
- * stub - optional value for a distance to travel from the connector's endpoint before beginning the Bezier curve. defaults to 0.
- *
- */
- var Bezier = function(params) {
- params = params || {};
-
- var _super = jsPlumb.Connectors.AbstractConnector.apply(this, arguments),
- stub = params.stub || 50,
- majorAnchor = params.curviness || 150,
- minorAnchor = 10;
-
- this.type = "Bezier";
- this.getCurviness = function() { return majorAnchor; };
-
- this._findControlPoint = function(point, sourceAnchorPosition, targetAnchorPosition, sourceEndpoint, targetEndpoint) {
- // determine if the two anchors are perpendicular to each other in their orientation. we swap the control
- // points around if so (code could be tightened up)
- var soo = sourceEndpoint.anchor.getOrientation(sourceEndpoint),
- too = targetEndpoint.anchor.getOrientation(targetEndpoint),
- perpendicular = soo[0] != too[0] || soo[1] == too[1],
- p = [];
-
- if (!perpendicular) {
- if (soo[0] === 0) // X
- p.push(sourceAnchorPosition[0] < targetAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
- else p.push(point[0] - (majorAnchor * soo[0]));
-
- if (soo[1] === 0) // Y
- p.push(sourceAnchorPosition[1] < targetAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
- else p.push(point[1] + (majorAnchor * too[1]));
- }
- else {
- if (too[0] === 0) // X
- p.push(targetAnchorPosition[0] < sourceAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
- else p.push(point[0] + (majorAnchor * too[0]));
-
- if (too[1] === 0) // Y
- p.push(targetAnchorPosition[1] < sourceAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
- else p.push(point[1] + (majorAnchor * soo[1]));
- }
-
- return p;
- };
-
- this._compute = function(paintInfo, p) {
- var sp = p.sourcePos,
- tp = p.targetPos,
- _w = Math.abs(sp[0] - tp[0]),
- _h = Math.abs(sp[1] - tp[1]),
- _sx = sp[0] < tp[0] ? _w : 0,
- _sy = sp[1] < tp[1] ? _h : 0,
- _tx = sp[0] < tp[0] ? 0 : _w,
- _ty = sp[1] < tp[1] ? 0 : _h,
- _CP = this._findControlPoint([_sx, _sy], sp, tp, p.sourceEndpoint, p.targetEndpoint),
- _CP2 = this._findControlPoint([_tx, _ty], tp, sp, p.targetEndpoint, p.sourceEndpoint);
-
- _super.addSegment(this, "Bezier", {
- x1:_sx, y1:_sy, x2:_tx, y2:_ty,
- cp1x:_CP[0], cp1y:_CP[1], cp2x:_CP2[0], cp2y:_CP2[1]
- });
- };
- };
- jsPlumbUtil.extend(Bezier, jsPlumb.Connectors.AbstractConnector);
- jsPlumb.registerConnectorType(Bezier, "Bezier");
-
- // ********************************* END OF CONNECTOR TYPES *******************************************************************
-
- // ********************************* ENDPOINT TYPES *******************************************************************
-
- jsPlumb.Endpoints.AbstractEndpoint = function(params) {
- AbstractComponent.apply(this, arguments);
- var compute = this.compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- var out = this._compute.apply(this, arguments);
- this.x = out[0];
- this.y = out[1];
- this.w = out[2];
- this.h = out[3];
- this.bounds.minX = this.x;
- this.bounds.minY = this.y;
- this.bounds.maxX = this.x + this.w;
- this.bounds.maxY = this.y + this.h;
- return out;
- };
- return {
- compute:compute,
- cssClass:params.cssClass
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.AbstractEndpoint, AbstractComponent);
-
- /**
- * Class: Endpoints.Dot
- * A round endpoint, with default radius 10 pixels.
- */
-
- /**
- * Function: Constructor
- *
- * Parameters:
- *
- * radius - radius of the endpoint. defaults to 10 pixels.
- */
- jsPlumb.Endpoints.Dot = function(params) {
- this.type = "Dot";
- var _super = jsPlumb.Endpoints.AbstractEndpoint.apply(this, arguments);
- params = params || {};
- this.radius = params.radius || 10;
- this.defaultOffset = 0.5 * this.radius;
- this.defaultInnerRadius = this.radius / 3;
-
- this._compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- this.radius = endpointStyle.radius || this.radius;
- var x = anchorPoint[0] - this.radius,
- y = anchorPoint[1] - this.radius,
- w = this.radius * 2,
- h = this.radius * 2;
-
- if (endpointStyle.strokeStyle) {
- var lw = endpointStyle.lineWidth || 1;
- x -= lw;
- y -= lw;
- w += (lw * 2);
- h += (lw * 2);
- }
- return [ x, y, w, h, this.radius ];
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.Dot, jsPlumb.Endpoints.AbstractEndpoint);
-
- /**
- * Class: Endpoints.Rectangle
- * A Rectangular Endpoint, with default size 20x20.
- */
- /**
- * Function: Constructor
- *
- * Parameters:
- *
- * width - width of the endpoint. defaults to 20 pixels.
- * height - height of the endpoint. defaults to 20 pixels.
- */
- jsPlumb.Endpoints.Rectangle = function(params) {
- this.type = "Rectangle";
- var _super = jsPlumb.Endpoints.AbstractEndpoint.apply(this, arguments);
- params = params || {};
- this.width = params.width || 20;
- this.height = params.height || 20;
-
- this._compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- var width = endpointStyle.width || this.width,
- height = endpointStyle.height || this.height,
- x = anchorPoint[0] - (width/2),
- y = anchorPoint[1] - (height/2);
-
- return [ x, y, width, height];
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.Rectangle, jsPlumb.Endpoints.AbstractEndpoint);
-
-
- var DOMElementEndpoint = function(params) {
- jsPlumb.DOMElementComponent.apply(this, arguments);
- this._jsPlumb.displayElements = [ ];
- };
- jsPlumbUtil.extend(DOMElementEndpoint, jsPlumb.DOMElementComponent, {
- // jsPlumb.Endpoints.AbstractEndpoint
- getDisplayElements : function() {
- return this._jsPlumb.displayElements;
- },
- appendDisplayElement : function(el) {
- this._jsPlumb.displayElements.push(el);
- }
- });
-
- /**
- * Class: Endpoints.Image
- * Draws an image as the Endpoint.
- */
- /**
- * Function: Constructor
- *
- * Parameters:
- *
- * src - location of the image to use.
-
- TODO: multiple references to self. not sure quite how to get rid of them entirely. perhaps self = null in the cleanup
- function will suffice
-
- TODO this class still leaks memory.
-
- */
- jsPlumb.Endpoints.Image = function(params) {
-
- this.type = "Image";
- DOMElementEndpoint.apply(this, arguments);
- jsPlumb.Endpoints.AbstractEndpoint.apply(this, arguments);
-
- var _onload = params.onload,
- src = params.src || params.url,
- parent = params.parent,
- clazz = params.cssClass ? " " + params.cssClass : "";
-
- this._jsPlumb.img = new Image();
- this._jsPlumb.ready = false;
- this._jsPlumb.initialized = false;
- this._jsPlumb.deleted = false;
- this._jsPlumb.widthToUse = params.width;
- this._jsPlumb.heightToUse = params.height;
- this._jsPlumb.endpoint = params.endpoint;
-
- this._jsPlumb.img.onload = function() {
- // check we weren't actually discarded before use (in fact mostly happens in tests)
- if (this._jsPlumb != null) {
- this._jsPlumb.ready = true;
- this._jsPlumb.widthToUse = this._jsPlumb.widthToUse || this._jsPlumb.img.width;
- this._jsPlumb.heightToUse = this._jsPlumb.heightToUse || this._jsPlumb.img.height;
- if (_onload) {
- _onload(this);
- }
- }
- }.bind(this);
-
- /*
- Function: setImage
- Sets the Image to use in this Endpoint.
-
- Parameters:
- img - may be a URL or an Image object
- onload - optional; a callback to execute once the image has loaded.
- */
- this._jsPlumb.endpoint.setImage = function(_img, onload) {
- var s = _img.constructor == String ? _img : _img.src;
- _onload = onload;
- this._jsPlumb.img.src = s;
-
- if (this.canvas != null)
- this.canvas.setAttribute("src", this._jsPlumb.img.src);
- }.bind(this);
-
- this._jsPlumb.endpoint.setImage(src, _onload);
- /*
- var s = src.constructor == String ? src : src.src;
- //_onload = onload;
- this._jsPlumb.img.src = src;
-
- if (this.canvas != null)
- this.canvas.setAttribute("src", this._jsPlumb.img.src);
- // }.bind(this);
-
- //this._jsPlumb.endpoint.setImage(src, _onload);*/
-
- this._compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- this.anchorPoint = anchorPoint;
- if (this._jsPlumb.ready) return [anchorPoint[0] - this._jsPlumb.widthToUse / 2, anchorPoint[1] - this._jsPlumb.heightToUse / 2,
- this._jsPlumb.widthToUse, this._jsPlumb.heightToUse];
- else return [0,0,0,0];
- };
-
- this.canvas = document.createElement("img");
- this.canvas.style.margin = 0;
- this.canvas.style.padding = 0;
- this.canvas.style.outline = 0;
- this.canvas.style.position = "absolute";
- this.canvas.className = this._jsPlumb.instance.endpointClass + clazz;
- if (this._jsPlumb.widthToUse) this.canvas.setAttribute("width", this._jsPlumb.widthToUse);
- if (this._jsPlumb.heightToUse) this.canvas.setAttribute("height", this._jsPlumb.heightToUse);
- this._jsPlumb.instance.appendElement(this.canvas, parent);
- this.attachListeners(this.canvas, this);
-
- this.actuallyPaint = function(d, style, anchor) {
- if (!this._jsPlumb.deleted) {
- if (!this._jsPlumb.initialized) {
- this.canvas.setAttribute("src", this._jsPlumb.img.src);
- this.appendDisplayElement(this.canvas);
- this._jsPlumb.initialized = true;
- }
- var x = this.anchorPoint[0] - (this._jsPlumb.widthToUse / 2),
- y = this.anchorPoint[1] - (this._jsPlumb.heightToUse / 2);
- jsPlumbUtil.sizeElement(this.canvas, x, y, this._jsPlumb.widthToUse, this._jsPlumb.heightToUse);
- }
- };
-
- this.paint = function(style, anchor) {
- if (this._jsPlumb != null) { // may have been deleted
- if (this._jsPlumb.ready) {
- this.actuallyPaint(style, anchor);
- }
- else {
- window.setTimeout(function() {
- this.paint(style, anchor);
- }.bind(this), 200);
- }
- }
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.Image, [ DOMElementEndpoint, jsPlumb.Endpoints.AbstractEndpoint ], {
- cleanup : function() {
- this._jsPlumb.deleted = true;
- jsPlumbUtil.removeElement(this.canvas);
- this.canvas = null;
- }
- });
-
- /*
- * Class: Endpoints.Blank
- * An Endpoint that paints nothing (visible) on the screen. Supports cssClass and hoverClass parameters like all Endpoints.
- */
- jsPlumb.Endpoints.Blank = function(params) {
- var _super = jsPlumb.Endpoints.AbstractEndpoint.apply(this, arguments);
- this.type = "Blank";
- DOMElementEndpoint.apply(this, arguments);
- this._compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- return [anchorPoint[0], anchorPoint[1],10,0];
- };
-
- this.canvas = document.createElement("div");
- this.canvas.style.display = "block";
- this.canvas.style.width = "1px";
- this.canvas.style.height = "1px";
- this.canvas.style.background = "transparent";
- this.canvas.style.position = "absolute";
- this.canvas.className = this._jsPlumb.endpointClass;
- jsPlumb.appendElement(this.canvas, params.parent);
-
- this.paint = function(style, anchor) {
- jsPlumbUtil.sizeElement(this.canvas, this.x, this.y, this.w, this.h);
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.Blank, [jsPlumb.Endpoints.AbstractEndpoint, DOMElementEndpoint], {
- cleanup:function() {
- if (this.canvas) {
- this.canvas.parentNode.removeChild(this.canvas);
- }
- }
- });
-
- /*
- * Class: Endpoints.Triangle
- * A triangular Endpoint.
- */
- /*
- * Function: Constructor
- *
- * Parameters:
- *
- * width - width of the triangle's base. defaults to 55 pixels.
- * height - height of the triangle from base to apex. defaults to 55 pixels.
- */
- jsPlumb.Endpoints.Triangle = function(params) {
- this.type = "Triangle";
- var _super = jsPlumb.Endpoints.AbstractEndpoint.apply(this, arguments);
- params = params || { };
- params.width = params.width || 55;
- params.height = params.height || 55;
- this.width = params.width;
- this.height = params.height;
- this._compute = function(anchorPoint, orientation, endpointStyle, connectorPaintStyle) {
- var width = endpointStyle.width || self.width,
- height = endpointStyle.height || self.height,
- x = anchorPoint[0] - (width/2),
- y = anchorPoint[1] - (height/2);
- return [ x, y, width, height ];
- };
- };
-// ********************************* END OF ENDPOINT TYPES *******************************************************************
-
-
-// ********************************* OVERLAY DEFINITIONS ***********************************************************************
-
- var AbstractOverlay = jsPlumb.Overlays.AbstractOverlay = function(params) {
- this.visible = true;
- this.isAppendedAtTopLevel = true;
- this.component = params.component;
- this.loc = params.location == null ? 0.5 : params.location;
- this.endpointLoc = params.endpointLocation == null ? [ 0.5, 0.5] : params.endpointLocation;
- //this.;
- };
- AbstractOverlay.prototype = {
- cleanup:function() {
- this.component = null;
- this.canvas = null;
- this.endpointLoc = null;
- },
- setVisible : function(val) {
- this.visible = val;
- this.component.repaint();
- },
- isVisible : function() { return this.visible; },
- hide : function() { this.setVisible(false); },
- show : function() { this.setVisible(true); },
-
- incrementLocation : function(amount) {
- this.loc += amount;
- this.component.repaint();
- },
- setLocation : function(l) {
- this.loc = l;
- this.component.repaint();
- },
- getLocation : function() {
- return this.loc;
- }
- };
-
-
- /*
- * Class: Overlays.Arrow
- *
- * An arrow overlay, defined by four points: the head, the two sides of the tail, and a 'foldback' point at some distance along the length
- * of the arrow that lines from each tail point converge into. The foldback point is defined using a decimal that indicates some fraction
- * of the length of the arrow and has a default value of 0.623. A foldback point value of 1 would mean that the arrow had a straight line
- * across the tail.
- */
- /*
- * Function: Constructor
- *
- * Parameters:
- *
- * length - distance in pixels from head to tail baseline. default 20.
- * width - width in pixels of the tail baseline. default 20.
- * fillStyle - style to use when filling the arrow. defaults to "black".
- * strokeStyle - style to use when stroking the arrow. defaults to null, which means the arrow is not stroked.
- * lineWidth - line width to use when stroking the arrow. defaults to 1, but only used if strokeStyle is not null.
- * foldback - distance (as a decimal from 0 to 1 inclusive) along the length of the arrow marking the point the tail points should fold back to. defaults to 0.623.
- * location - distance (as a decimal from 0 to 1 inclusive) marking where the arrow should sit on the connector. defaults to 0.5.
- * direction - indicates the direction the arrow points in. valid values are -1 and 1; 1 is default.
- */
- jsPlumb.Overlays.Arrow = function(params) {
- this.type = "Arrow";
- AbstractOverlay.apply(this, arguments);
- this.isAppendedAtTopLevel = false;
- params = params || {};
- var _ju = jsPlumbUtil;
-
- this.length = params.length || 20;
- this.width = params.width || 20;
- this.id = params.id;
- var direction = (params.direction || 1) < 0 ? -1 : 1,
- paintStyle = params.paintStyle || { lineWidth:1 },
- // how far along the arrow the lines folding back in come to. default is 62.3%.
- foldback = params.foldback || 0.623;
-
- this.computeMaxSize = function() { return self.width * 1.5; };
- //this.cleanup = function() { }; // nothing to clean up for Arrows
- this.draw = function(component, currentConnectionPaintStyle) {
-
- var hxy, mid, txy, tail, cxy;
- if (component.pointAlongPathFrom) {
-
- if (_ju.isString(this.loc) || this.loc > 1 || this.loc < 0) {
- var l = parseInt(this.loc, 10);
- hxy = component.pointAlongPathFrom(l, direction * this.length / 2, true);
- mid = component.pointOnPath(l, true);
- txy = _ju.pointOnLine(hxy, mid, this.length);
- }
- else if (this.loc == 1) {
- hxy = component.pointOnPath(this.loc);
- mid = component.pointAlongPathFrom(this.loc, -(this.length));
- txy = _ju.pointOnLine(hxy, mid, this.length);
-
- if (direction == -1) {
- var _ = txy;
- txy = hxy;
- hxy = _;
- }
- }
- else if (this.loc === 0) {
- txy = component.pointOnPath(this.loc);
- mid = component.pointAlongPathFrom(this.loc, this.length);
- hxy = _ju.pointOnLine(txy, mid, this.length);
- if (direction == -1) {
- var __ = txy;
- txy = hxy;
- hxy = __;
- }
- }
- else {
- hxy = component.pointAlongPathFrom(this.loc, direction * this.length / 2);
- mid = component.pointOnPath(this.loc);
- txy = _ju.pointOnLine(hxy, mid, this.length);
- }
-
- tail = _ju.perpendicularLineTo(hxy, txy, this.width);
- cxy = _ju.pointOnLine(hxy, txy, foldback * this.length);
-
- var d = { hxy:hxy, tail:tail, cxy:cxy },
- strokeStyle = paintStyle.strokeStyle || currentConnectionPaintStyle.strokeStyle,
- fillStyle = paintStyle.fillStyle || currentConnectionPaintStyle.strokeStyle,
- lineWidth = paintStyle.lineWidth || currentConnectionPaintStyle.lineWidth,
- info = {
- component:component,
- d:d,
- lineWidth:lineWidth,
- strokeStyle:strokeStyle,
- fillStyle:fillStyle,
- minX:Math.min(hxy.x, tail[0].x, tail[1].x),
- maxX:Math.max(hxy.x, tail[0].x, tail[1].x),
- minY:Math.min(hxy.y, tail[0].y, tail[1].y),
- maxY:Math.max(hxy.y, tail[0].y, tail[1].y)
- };
-
- return info;
- }
- else return {component:component, minX:0,maxX:0,minY:0,maxY:0};
- };
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.Arrow, AbstractOverlay);
-
- /*
- * Class: Overlays.PlainArrow
- *
- * A basic arrow. This is in fact just one instance of the more generic case in which the tail folds back on itself to some
- * point along the length of the arrow: in this case, that foldback point is the full length of the arrow. so it just does
- * a 'call' to Arrow with foldback set appropriately.
- */
- /*
- * Function: Constructor
- * See <Overlays.Arrow> for allowed parameters for this overlay.
- */
- jsPlumb.Overlays.PlainArrow = function(params) {
- params = params || {};
- var p = jsPlumb.extend(params, {foldback:1});
- jsPlumb.Overlays.Arrow.call(this, p);
- this.type = "PlainArrow";
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.PlainArrow, jsPlumb.Overlays.Arrow);
-
- /*
- * Class: Overlays.Diamond
- *
- * A diamond. Like PlainArrow, this is a concrete case of the more generic case of the tail points converging on some point...it just
- * happens that in this case, that point is greater than the length of the the arrow.
- *
- * this could probably do with some help with positioning...due to the way it reuses the Arrow paint code, what Arrow thinks is the
- * center is actually 1/4 of the way along for this guy. but we don't have any knowledge of pixels at this point, so we're kind of
- * stuck when it comes to helping out the Arrow class. possibly we could pass in a 'transpose' parameter or something. the value
- * would be -l/4 in this case - move along one quarter of the total length.
- */
- /*
- * Function: Constructor
- * See <Overlays.Arrow> for allowed parameters for this overlay.
- */
- jsPlumb.Overlays.Diamond = function(params) {
- params = params || {};
- var l = params.length || 40,
- p = jsPlumb.extend(params, {length:l/2, foldback:2});
- jsPlumb.Overlays.Arrow.call(this, p);
- this.type = "Diamond";
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.Diamond, jsPlumb.Overlays.Arrow);
-
- var _getDimensions = function(component) {
- if (component._jsPlumb.cachedDimensions == null)
- component._jsPlumb.cachedDimensions = component.getDimensions();
- return component._jsPlumb.cachedDimensions;
- };
-
- // abstract superclass for overlays that add an element to the DOM.
- var AbstractDOMOverlay = function(params) {
- jsPlumb.DOMElementComponent.apply(this, arguments);
- AbstractOverlay.apply(this, arguments);
-
- var jpcl = jsPlumb.CurrentLibrary;
- this.id = params.id;
- this._jsPlumb.div = null;
- this._jsPlumb.initialised = false;
- this._jsPlumb.component = params.component;
- this._jsPlumb.cachedDimensions = null;
- this._jsPlumb.create = params.create;
-
- this.getElement = function() {
- if (this._jsPlumb.div == null) {
- var div = this._jsPlumb.div = jpcl.getDOMElement(this._jsPlumb.create(this._jsPlumb.component));
- div.style.position = "absolute";
- var clazz = params._jsPlumb.overlayClass + " " +
- (this.cssClass ? this.cssClass :
- params.cssClass ? params.cssClass : "");
- div.className = clazz;
- this._jsPlumb.instance.appendElement(div, this._jsPlumb.component.parent);
- this._jsPlumb.instance.getId(div);
- this.attachListeners(div, this);
- this.canvas = div;
- }
- return this._jsPlumb.div;
- };
-
- /*
- this.paint = function(p, containerExtents) {
- if (!this._jsPlumb.initialised) {
- this.getElement();
- p.component.appendDisplayElement(this._jsPlumb.div);
- this.attachListeners(this._jsPlumb.div, p.component);
- this._jsPlumb.initialised = true;
- }
- this._jsPlumb.div.style.left = (p.component.x + p.d.minx) + "px";
- this._jsPlumb.div.style.top = (p.component.y + p.d.miny) + "px";
- };*/
-
- this.draw = function(component, currentConnectionPaintStyle) {
- var td = _getDimensions(this);
- if (td != null && td.length == 2) {
- var cxy = {x:0,y:0};
- if (component.pointOnPath) {
- var loc = this.loc, absolute = false;
- if (jsPlumbUtil.isString(this.loc) || this.loc < 0 || this.loc > 1) {
- loc = parseInt(this.loc, 10);
- absolute = true;
- }
- cxy = component.pointOnPath(loc, absolute); // a connection
- }
- else {
- var locToUse = this.loc.constructor == Array ? this.loc : this.endpointLoc;
- cxy = { x:locToUse[0] * component.w,
- y:locToUse[1] * component.h };
- }
-
- var minx = cxy.x - (td[0] / 2),
- miny = cxy.y - (td[1] / 2);
-
- return {
- component:component,
- d:{ minx:minx, miny:miny, td:td, cxy:cxy },
- minX:minx,
- maxX:minx + td[0],
- minY:miny,
- maxY:miny + td[1]
- };
- }
- else return {minX:0,maxX:0,minY:0,maxY:0};
- };
- };
- jsPlumbUtil.extend(AbstractDOMOverlay, [jsPlumb.DOMElementComponent, AbstractOverlay], {
- getDimensions : function() {
- return jsPlumb.CurrentLibrary.getSize(jsPlumb.CurrentLibrary.getElementObject(this.getElement()));
- },
- setVisible : function(state) {
- this._jsPlumb.div.style.display = state ? "block" : "none";
- },
- /*
- * Function: clearCachedDimensions
- * Clears the cached dimensions for the label. As a performance enhancement, label dimensions are
- * cached from 1.3.12 onwards. The cache is cleared when you change the label text, of course, but
- * there are other reasons why the text dimensions might change - if you make a change through CSS, for
- * example, you might change the font size. in that case you should explicitly call this method.
- */
- clearCachedDimensions : function() {
- this._jsPlumb.cachedDimensions = null;
- },
- cleanup : function() {
- if (this._jsPlumb.div != null)
- jsPlumb.CurrentLibrary.removeElement(this._jsPlumb.div);
- },
- computeMaxSize : function() {
- var td = _getDimensions(this);
- return Math.max(td[0], td[1]);
- },
- reattachListeners : function(connector) {
- if (this._jsPlumb.div) {
- this.reattachListenersForElement(this._jsPlumb.div, this, connector);
- }
- },
- paint : function(p, containerExtents) {
- if (!this._jsPlumb.initialised) {
- this.getElement();
- p.component.appendDisplayElement(this._jsPlumb.div);
- this.attachListeners(this._jsPlumb.div, p.component);
- this._jsPlumb.initialised = true;
- }
- this._jsPlumb.div.style.left = (p.component.x + p.d.minx) + "px";
- this._jsPlumb.div.style.top = (p.component.y + p.d.miny) + "px";
- }
- });
-
- /*
- * Class: Overlays.Custom
- * A Custom overlay. You supply a 'create' function which returns some DOM element, and jsPlumb positions it.
- * The 'create' function is passed a Connection or Endpoint.
- */
- /*
- * Function: Constructor
- *
- * Parameters:
- * create - function for jsPlumb to call that returns a DOM element.
- * location - distance (as a decimal from 0 to 1 inclusive) marking where the label should sit on the connector. defaults to 0.5.
- * id - optional id to use for later retrieval of this overlay.
- *
- */
- jsPlumb.Overlays.Custom = function(params) {
- this.type = "Custom";
- AbstractDOMOverlay.apply(this, arguments);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.Custom, AbstractDOMOverlay);
-
- jsPlumb.Overlays.GuideLines = function() {
- var self = this;
- self.length = 50;
- self.lineWidth = 5;
- this.type = "GuideLines";
- AbstractOverlay.apply(this, arguments);
- jsPlumb.jsPlumbUIComponent.apply(this, arguments);
- this.draw = function(connector, currentConnectionPaintStyle) {
-
- var head = connector.pointAlongPathFrom(self.loc, self.length / 2),
- mid = connector.pointOnPath(self.loc),
- tail = jsPlumbUtil.pointOnLine(head, mid, self.length),
- tailLine = jsPlumbUtil.perpendicularLineTo(head, tail, 40),
- headLine = jsPlumbUtil.perpendicularLineTo(tail, head, 20);
-
- return {
- connector:connector,
- head:head,
- tail:tail,
- headLine:headLine,
- tailLine:tailLine,
- minX:Math.min(head.x, tail.x, headLine[0].x, headLine[1].x),
- minY:Math.min(head.y, tail.y, headLine[0].y, headLine[1].y),
- maxX:Math.max(head.x, tail.x, headLine[0].x, headLine[1].x),
- maxY:Math.max(head.y, tail.y, headLine[0].y, headLine[1].y)
- };
- };
-
- // this.cleanup = function() { }; // nothing to clean up for GuideLines
- };
-
- /*
- * Class: Overlays.Label
- * A Label overlay. For all different renderer types (SVG/Canvas/VML), jsPlumb draws a Label overlay as a styled DIV. Version 1.3.0 of jsPlumb
- * introduced the ability to set css classes on the label; this is now the preferred way for you to style a label. The 'labelStyle' parameter
- * is still supported in 1.3.0 but its usage is deprecated. Under the hood, jsPlumb just turns that object into a bunch of CSS directive that it
- * puts on the Label's 'style' attribute, so the end result is the same.
- */
- /*
- * Function: Constructor
- *
- * Parameters:
- * cssClass - optional css class string to append to css class. This string is appended "as-is", so you can of course have multiple classes
- * defined. This parameter is preferred to using labelStyle, borderWidth and borderStyle.
- * label - the label to paint. May be a string or a function that returns a string. Nothing will be painted if your label is null or your
- * label function returns null. empty strings _will_ be painted.
- * location - distance (as a decimal from 0 to 1 inclusive) marking where the label should sit on the connector. defaults to 0.5.
- * id - optional id to use for later retrieval of this overlay.
- *
- */
- jsPlumb.Overlays.Label = function(params) {
- this.labelStyle = params.labelStyle || jsPlumb.Defaults.LabelStyle;
- this.cssClass = this.labelStyle != null ? this.labelStyle.cssClass : null;
- var p = jsPlumb.extend({
- create : function() {
- return document.createElement("div");
- }}, params);
- jsPlumb.Overlays.Custom.call(this, p);
- this.type = "Label";
- this.label = params.label || "";
- this.labelText = null;
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.Label, jsPlumb.Overlays.Custom, {
- cleanup:function() {
- this.div = null;
- this.label = null;
- this.labelText = null;
- this.cssClass = null;
- this.labelStyle = null;
- },
- getLabel : function() {
- return this.label;
- },
- /*
- * Function: setLabel
- * sets the label's, um, label. you would think i'd call this function
- * 'setText', but you can pass either a Function or a String to this, so
- * it makes more sense as 'setLabel'. This uses innerHTML on the label div, so keep
- * that in mind if you need escaped HTML.
- */
- setLabel : function(l) {
- this.label = l;
- this.labelText = null;
- this.clearCachedDimensions();
- this.update();
- this.component.repaint();
- },
- getDimensions : function() {
- this.update();
- return AbstractDOMOverlay.prototype.getDimensions.apply(this, arguments);
- },
- update : function() {
- if (typeof this.label == "function") {
- var lt = this.label(this);
- this.getElement().innerHTML = lt.replace(/\r\n/g, "<br/>");
- }
- else {
- if (this.labelText == null) {
- this.labelText = this.label;
- this.getElement().innerHTML = this.labelText.replace(/\r\n/g, "<br/>");
- }
- }
- }
- });
-
- // ********************************* END OF OVERLAY DEFINITIONS ***********************************************************************
-
-})();
-
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the 'flowchart' connectors, consisting of vertical and horizontal line segments.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt ([email protected])
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-;(function() {
-
- /**
- * Function: Constructor
- *
- * Parameters:
- * stub - minimum length for the stub at each end of the connector. This can be an integer, giving a value for both ends of the connections,
- * or an array of two integers, giving separate values for each end. The default is an integer with value 30 (pixels).
- * gap - gap to leave between the end of the connector and the element on which the endpoint resides. if you make this larger than stub then you will see some odd looking behaviour.
- Like stub, this can be an array or a single value. defaults to 0 pixels for each end.
- * cornerRadius - optional, defines the radius of corners between segments. defaults to 0 (hard edged corners).
- * alwaysRespectStubs - defaults to false. whether or not the connectors should always draw the stub, or, if the two elements
- are in close proximity to each other (closer than the sum of the two stubs), to adjust the stubs.
- */
- var Flowchart = function(params) {
- this.type = "Flowchart";
- params = params || {};
- params.stub = params.stub == null ? 30 : params.stub;
- var self = this,
- _super = jsPlumb.Connectors.AbstractConnector.apply(this, arguments),
- midpoint = params.midpoint == null ? 0.5 : params.midpoint,
- points = [], segments = [],
- grid = params.grid,
- alwaysRespectStubs = params.alwaysRespectStubs,
- userSuppliedSegments = null,
- lastx = null, lasty = null, lastOrientation,
- cornerRadius = params.cornerRadius != null ? params.cornerRadius : 0,
- sgn = function(n) { return n < 0 ? -1 : n === 0 ? 0 : 1; },
- /**
- * helper method to add a segment.
- */
- addSegment = function(segments, x, y, paintInfo) {
- if (lastx == x && lasty == y) return;
- var lx = lastx == null ? paintInfo.sx : lastx,
- ly = lasty == null ? paintInfo.sy : lasty,
- o = lx == x ? "v" : "h",
- sgnx = sgn(x - lx),
- sgny = sgn(y - ly);
-
- lastx = x;
- lasty = y;
- segments.push([lx, ly, x, y, o, sgnx, sgny]);
- },
- segLength = function(s) {
- return Math.sqrt(Math.pow(s[0] - s[2], 2) + Math.pow(s[1] - s[3], 2));
- },
- _cloneArray = function(a) { var _a = []; _a.push.apply(_a, a); return _a;},
- updateMinMax = function(a1) {
- self.bounds.minX = Math.min(self.bounds.minX, a1[2]);
- self.bounds.maxX = Math.max(self.bounds.maxX, a1[2]);
- self.bounds.minY = Math.min(self.bounds.minY, a1[3]);
- self.bounds.maxY = Math.max(self.bounds.maxY, a1[3]);
- },
- writeSegments = function(conn, segments, paintInfo) {
- var current, next;
- for (var i = 0; i < segments.length - 1; i++) {
-
- current = current || _cloneArray(segments[i]);
- next = _cloneArray(segments[i + 1]);
- if (cornerRadius > 0 && current[4] != next[4]) {
- var radiusToUse = Math.min(cornerRadius, segLength(current), segLength(next));
- // right angle. adjust current segment's end point, and next segment's start point.
- current[2] -= current[5] * radiusToUse;
- current[3] -= current[6] * radiusToUse;
- next[0] += next[5] * radiusToUse;
- next[1] += next[6] * radiusToUse;
- var ac = (current[6] == next[5] && next[5] == 1) ||
- ((current[6] == next[5] && next[5] === 0) && current[5] != next[6]) ||
- (current[6] == next[5] && next[5] == -1),
- sgny = next[1] > current[3] ? 1 : -1,
- sgnx = next[0] > current[2] ? 1 : -1,
- sgnEqual = sgny == sgnx,
- cx = (sgnEqual && ac || (!sgnEqual && !ac)) ? next[0] : current[2],
- cy = (sgnEqual && ac || (!sgnEqual && !ac)) ? current[3] : next[1];
-
- _super.addSegment(conn, "Straight", {
- x1:current[0], y1:current[1], x2:current[2], y2:current[3]
- });
-
- _super.addSegment(conn, "Arc", {
- r:radiusToUse,
- x1:current[2],
- y1:current[3],
- x2:next[0],
- y2:next[1],
- cx:cx,
- cy:cy,
- ac:ac
- });
- }
- else {
- // dx + dy are used to adjust for line width.
- var dx = (current[2] == current[0]) ? 0 : (current[2] > current[0]) ? (paintInfo.lw / 2) : -(paintInfo.lw / 2),
- dy = (current[3] == current[1]) ? 0 : (current[3] > current[1]) ? (paintInfo.lw / 2) : -(paintInfo.lw / 2);
- _super.addSegment(conn, "Straight", {
- x1:current[0]- dx, y1:current[1]-dy, x2:current[2] + dx, y2:current[3] + dy
- });
- }
- current = next;
- }
- // last segment
- _super.addSegment(conn, "Straight", {
- x1:next[0], y1:next[1], x2:next[2], y2:next[3]
- });
- };
-
- this.setSegments = function(s) {
- userSuppliedSegments = s;
- };
-
- this.isEditable = function() { return true; };
-
- /*
- Function: getOriginalSegments
- Gets the segments before the addition of rounded corners. This is used by the flowchart
- connector editor, since it only wants to concern itself with the original segments.
- */
- this.getOriginalSegments = function() {
- return userSuppliedSegments || segments;
- };
-
- this._compute = function(paintInfo, params) {
-
- if (params.clearEdits)
- userSuppliedSegments = null;
-
- if (userSuppliedSegments != null) {
- writeSegments(this, userSuppliedSegments, paintInfo);
- return;
- }
-
- segments = [];
- lastx = null; lasty = null;
- lastOrientation = null;
-
- var midx = paintInfo.startStubX + ((paintInfo.endStubX - paintInfo.startStubX) * midpoint),
- midy = paintInfo.startStubY + ((paintInfo.endStubY - paintInfo.startStubY) * midpoint);
-
- var findClearedLine = function(start, mult, anchorPos, dimension) {
- return start + (mult * (( 1 - anchorPos) * dimension) + _super.maxStub);
- },
- orientations = { x:[ 0, 1 ], y:[ 1, 0 ] },
- commonStubCalculator = function(axis) {
- return [ paintInfo.startStubX, paintInfo.startStubY, paintInfo.endStubX, paintInfo.endStubY ];
- },
- stubCalculators = {
- perpendicular:commonStubCalculator,
- orthogonal:commonStubCalculator,
- opposite:function(axis) {
- var pi = paintInfo,
- idx = axis == "x" ? 0 : 1,
- areInProximity = {
- "x":function() {
- return ( (pi.so[idx] == 1 && (
- ( (pi.startStubX > pi.endStubX) && (pi.tx > pi.startStubX) ) ||
- ( (pi.sx > pi.endStubX) && (pi.tx > pi.sx))))) ||
-
- ( (pi.so[idx] == -1 && (
- ( (pi.startStubX < pi.endStubX) && (pi.tx < pi.startStubX) ) ||
- ( (pi.sx < pi.endStubX) && (pi.tx < pi.sx)))));
- },
- "y":function() {
- return ( (pi.so[idx] == 1 && (
- ( (pi.startStubY > pi.endStubY) && (pi.ty > pi.startStubY) ) ||
- ( (pi.sy > pi.endStubY) && (pi.ty > pi.sy))))) ||
-
- ( (pi.so[idx] == -1 && (
- ( (pi.startStubY < pi.endStubY) && (pi.ty < pi.startStubY) ) ||
- ( (pi.sy < pi.endStubY) && (pi.ty < pi.sy)))));
- }
- };
-
- if (!alwaysRespectStubs && areInProximity[axis]()) {
- return {
- "x":[(paintInfo.sx + paintInfo.tx) / 2, paintInfo.startStubY, (paintInfo.sx + paintInfo.tx) / 2, paintInfo.endStubY],
- "y":[paintInfo.startStubX, (paintInfo.sy + paintInfo.ty) / 2, paintInfo.endStubX, (paintInfo.sy + paintInfo.ty) / 2]
- }[axis];
- }
- else {
- return [ paintInfo.startStubX, paintInfo.startStubY, paintInfo.endStubX, paintInfo.endStubY ];
- }
- }
- },
- lineCalculators = {
- perpendicular : function(axis, ss, oss, es, oes) {
- var pi = paintInfo,
- sis = {
- x:[ [ [ 1,2,3,4 ], null, [ 2,1,4,3 ] ], null, [ [ 4,3,2,1 ], null, [ 3,4,1,2 ] ] ],
- y:[ [ [ 3,2,1,4 ], null, [ 2,3,4,1 ] ], null, [ [ 4,1,2,3 ], null, [ 1,4,3,2 ] ] ]
- },
- stubs = {
- x:[ [ pi.startStubX, pi.endStubX ] , null, [ pi.endStubX, pi.startStubX ] ],
- y:[ [ pi.startStubY, pi.endStubY ] , null, [ pi.endStubY, pi.startStubY ] ]
- },
- midLines = {
- x:[ [ midx, pi.startStubY ], [ midx, pi.endStubY ] ],
- y:[ [ pi.startStubX, midy ], [ pi.endStubX, midy ] ]
- },
- linesToEnd = {
- x:[ [ pi.endStubX, pi.startStubY ] ],
- y:[ [ pi.startStubX, pi.endStubY ] ]
- },
- startToEnd = {
- x:[ [ pi.startStubX, pi.endStubY ], [ pi.endStubX, pi.endStubY ] ],
- y:[ [ pi.endStubX, pi.startStubY ], [ pi.endStubX, pi.endStubY ] ]
- },
- startToMidToEnd = {
- x:[ [ pi.startStubX, midy ], [ pi.endStubX, midy ], [ pi.endStubX, pi.endStubY ] ],
- y:[ [ midx, pi.startStubY ], [ midx, pi.endStubY ], [ pi.endStubX, pi.endStubY ] ]
- },
- otherStubs = {
- x:[ pi.startStubY, pi.endStubY ],
- y:[ pi.startStubX, pi.endStubX ]
- },
- soIdx = orientations[axis][0], toIdx = orientations[axis][1],
- _so = pi.so[soIdx] + 1,
- _to = pi.to[toIdx] + 1,
- otherFlipped = (pi.to[toIdx] == -1 && (otherStubs[axis][1] < otherStubs[axis][0])) || (pi.to[toIdx] == 1 && (otherStubs[axis][1] > otherStubs[axis][0])),
- stub1 = stubs[axis][_so][0],
- stub2 = stubs[axis][_so][1],
- segmentIndexes = sis[axis][_so][_to];
-
- if (pi.segment == segmentIndexes[3] || (pi.segment == segmentIndexes[2] && otherFlipped)) {
- return midLines[axis];
- }
- else if (pi.segment == segmentIndexes[2] && stub2 < stub1) {
- return linesToEnd[axis];
- }
- else if ((pi.segment == segmentIndexes[2] && stub2 >= stub1) || (pi.segment == segmentIndexes[1] && !otherFlipped)) {
- return startToMidToEnd[axis];
- }
- else if (pi.segment == segmentIndexes[0] || (pi.segment == segmentIndexes[1] && otherFlipped)) {
- return startToEnd[axis];
- }
- },
- orthogonal : function(axis, startStub, otherStartStub, endStub, otherEndStub) {
- var pi = paintInfo,
- extent = {
- "x":pi.so[0] == -1 ? Math.min(startStub, endStub) : Math.max(startStub, endStub),
- "y":pi.so[1] == -1 ? Math.min(startStub, endStub) : Math.max(startStub, endStub)
- }[axis];
-
- return {
- "x":[ [ extent, otherStartStub ],[ extent, otherEndStub ], [ endStub, otherEndStub ] ],
- "y":[ [ otherStartStub, extent ], [ otherEndStub, extent ], [ otherEndStub, endStub ] ]
- }[axis];
- },
- opposite : function(axis, ss, oss, es, oes) {
- var pi = paintInfo,
- otherAxis = {"x":"y","y":"x"}[axis],
- dim = {"x":"height","y":"width"}[axis],
- comparator = pi["is" + axis.toUpperCase() + "GreaterThanStubTimes2"];
-
- if (params.sourceEndpoint.elementId == params.targetEndpoint.elementId) {
- var _val = oss + ((1 - params.sourceEndpoint.anchor[otherAxis]) * params.sourceInfo[dim]) + _super.maxStub;
- return {
- "x":[ [ ss, _val ], [ es, _val ] ],
- "y":[ [ _val, ss ], [ _val, es ] ]
- }[axis];
-
- }
- else if (!comparator || (pi.so[idx] == 1 && ss > es) || (pi.so[idx] == -1 && ss < es)) {
- return {
- "x":[[ ss, midy ], [ es, midy ]],
- "y":[[ midx, ss ], [ midx, es ]]
- }[axis];
- }
- else if ((pi.so[idx] == 1 && ss < es) || (pi.so[idx] == -1 && ss > es)) {
- return {
- "x":[[ midx, pi.sy ], [ midx, pi.ty ]],
- "y":[[ pi.sx, midy ], [ pi.tx, midy ]]
- }[axis];
- }
- }
- };
-
- var stubs = stubCalculators[paintInfo.anchorOrientation](paintInfo.sourceAxis),
- idx = paintInfo.sourceAxis == "x" ? 0 : 1,
- oidx = paintInfo.sourceAxis == "x" ? 1 : 0,
- ss = stubs[idx],
- oss = stubs[oidx],
- es = stubs[idx + 2],
- oes = stubs[oidx + 2];
-
- // add the start stub segment.
- addSegment(segments, stubs[0], stubs[1], paintInfo);
-
- // compute the rest of the line
- var p = lineCalculators[paintInfo.anchorOrientation](paintInfo.sourceAxis, ss, oss, es, oes);
- if (p) {
- for (var i = 0; i < p.length; i++) {
- addSegment(segments, p[i][0], p[i][1], paintInfo);
- }
- }
-
- // line to end stub
- addSegment(segments, stubs[2], stubs[3], paintInfo);
-
- // end stub to end
- addSegment(segments, paintInfo.tx, paintInfo.ty, paintInfo);
-
- writeSegments(this, segments, paintInfo);
- };
-
- this.getPath = function() {
- var _last = null, _lastAxis = null, s = [], segs = userSuppliedSegments || segments;
- for (var i = 0; i < segs.length; i++) {
- var seg = segs[i], axis = seg[4], axisIndex = (axis == "v" ? 3 : 2);
- if (_last != null && _lastAxis === axis) {
- _last[axisIndex] = seg[axisIndex];
- }
- else {
- if (seg[0] != seg[2] || seg[1] != seg[3]) {
- s.push({
- start:[ seg[0], seg[1] ],
- end:[ seg[2], seg[3] ]
- });
- _last = seg;
- _lastAxis = seg[4];
- }
- }
- }
- return s;
- };
-
- this.setPath = function(path) {
- userSuppliedSegments = [];
- for (var i = 0; i < path.length; i++) {
- var lx = path[i].start[0],
- ly = path[i].start[1],
- x = path[i].end[0],
- y = path[i].end[1],
- o = lx == x ? "v" : "h",
- sgnx = sgn(x - lx),
- sgny = sgn(y - ly);
-
- userSuppliedSegments.push([lx, ly, x, y, o, sgnx, sgny]);
- }
- };
- };
-
- jsPlumbUtil.extend(Flowchart, jsPlumb.Connectors.AbstractConnector);
- jsPlumb.registerConnectorType(Flowchart, "Flowchart");
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the state machine connectors.
- *
- * Thanks to Brainstorm Mobile Solutions for supporting the development of these.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt ([email protected])
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-;(function() {
-
- var Line = function(x1, y1, x2, y2) {
-
- this.m = (y2 - y1) / (x2 - x1);
- this.b = -1 * ((this.m * x1) - y1);
-
- this.rectIntersect = function(x,y,w,h) {
- var results = [], xInt, yInt;
-
- // try top face
- // the equation of the top face is y = (0 * x) + b; y = b.
- xInt = (y - this.b) / this.m;
- // test that the X value is in the line's range.
- if (xInt >= x && xInt <= (x + w)) results.push([ xInt, (this.m * xInt) + this.b ]);
-
- // try right face
- yInt = (this.m * (x + w)) + this.b;
- if (yInt >= y && yInt <= (y + h)) results.push([ (yInt - this.b) / this.m, yInt ]);
-
- // bottom face
- xInt = ((y + h) - this.b) / this.m;
- // test that the X value is in the line's range.
- if (xInt >= x && xInt <= (x + w)) results.push([ xInt, (this.m * xInt) + this.b ]);
-
- // try left face
- yInt = (this.m * x) + this.b;
- if (yInt >= y && yInt <= (y + h)) results.push([ (yInt - this.b) / this.m, yInt ]);
-
- if (results.length == 2) {
- var midx = (results[0][0] + results[1][0]) / 2, midy = (results[0][1] + results[1][1]) / 2;
- results.push([ midx,midy ]);
- // now calculate the segment inside the rectangle where the midpoint lies.
- var xseg = midx <= x + (w / 2) ? -1 : 1,
- yseg = midy <= y + (h / 2) ? -1 : 1;
- results.push([xseg, yseg]);
- return results;
- }
-
- return null;
-
- };
- },
- _segment = function(x1, y1, x2, y2) {
- if (x1 <= x2 && y2 <= y1) return 1;
- else if (x1 <= x2 && y1 <= y2) return 2;
- else if (x2 <= x1 && y2 >= y1) return 3;
- return 4;
- },
-
- // the control point we will use depends on the faces to which each end of the connection is assigned, specifically whether or not the
- // two faces are parallel or perpendicular. if they are parallel then the control point lies on the midpoint of the axis in which they
- // are parellel and varies only in the other axis; this variation is proportional to the distance that the anchor points lie from the
- // center of that face. if the two faces are perpendicular then the control point is at some distance from both the midpoints; the amount and
- // direction are dependent on the orientation of the two elements. 'seg', passed in to this method, tells you which segment the target element
- // lies in with respect to the source: 1 is top right, 2 is bottom right, 3 is bottom left, 4 is top left.
- //
- // sourcePos and targetPos are arrays of info about where on the source and target each anchor is located. their contents are:
- //
- // 0 - absolute x
- // 1 - absolute y
- // 2 - proportional x in element (0 is left edge, 1 is right edge)
- // 3 - proportional y in element (0 is top edge, 1 is bottom edge)
- //
- _findControlPoint = function(midx, midy, segment, sourceEdge, targetEdge, dx, dy, distance, proximityLimit) {
- // TODO (maybe)
- // - if anchor pos is 0.5, make the control point take into account the relative position of the elements.
- if (distance <= proximityLimit) return [midx, midy];
-
- if (segment === 1) {
- if (sourceEdge[3] <= 0 && targetEdge[3] >= 1) return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ];
- else if (sourceEdge[2] >= 1 && targetEdge[2] <= 0) return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ];
- else return [ midx + (-1 * dx) , midy + (-1 * dy) ];
- }
- else if (segment === 2) {
- if (sourceEdge[3] >= 1 && targetEdge[3] <= 0) return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ];
- else if (sourceEdge[2] >= 1 && targetEdge[2] <= 0) return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ];
- else return [ midx + (1 * dx) , midy + (-1 * dy) ];
- }
- else if (segment === 3) {
- if (sourceEdge[3] >= 1 && targetEdge[3] <= 0) return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ];
- else if (sourceEdge[2] <= 0 && targetEdge[2] >= 1) return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ];
- else return [ midx + (-1 * dx) , midy + (-1 * dy) ];
- }
- else if (segment === 4) {
- if (sourceEdge[3] <= 0 && targetEdge[3] >= 1) return [ midx + (sourceEdge[2] < 0.5 ? -1 * dx : dx), midy ];
- else if (sourceEdge[2] <= 0 && targetEdge[2] >= 1) return [ midx, midy + (sourceEdge[3] < 0.5 ? -1 * dy : dy) ];
- else return [ midx + (1 * dx) , midy + (-1 * dy) ];
- }
-
- };
-
- /**
- * Class: Connectors.StateMachine
- * Provides 'state machine' connectors.
- */
- /*
- * Function: Constructor
- *
- * Parameters:
- * curviness - measure of how "curvy" the connectors will be. this is translated as the distance that the
- * Bezier curve's control point is from the midpoint of the straight line connecting the two
- * endpoints, and does not mean that the connector is this wide. The Bezier curve never reaches
- * its control points; they act as gravitational masses. defaults to 10.
- * margin - distance from element to start and end connectors, in pixels. defaults to 5.
- * proximityLimit - sets the distance beneath which the elements are consider too close together to bother
- * with fancy curves. by default this is 80 pixels.
- * loopbackRadius - the radius of a loopback connector. optional; defaults to 25.
- * showLoopback - If set to false this tells the connector that it is ok to paint connections whose source and target is the same element with a connector running through the element. The default value for this is true; the connector always makes a loopback connection loop around the element rather than passing through it.
- */
- var StateMachine = function(params) {
- params = params || {};
- this.type = "StateMachine";
-
- var self = this,
- _super = jsPlumb.Connectors.AbstractConnector.apply(this, arguments),
- curviness = params.curviness || 10,
- margin = params.margin || 5,
- proximityLimit = params.proximityLimit || 80,
- clockwise = params.orientation && params.orientation === "clockwise",
- loopbackRadius = params.loopbackRadius || 25,
- showLoopback = params.showLoopback !== false;
-
- this._compute = function(paintInfo, params) {
- var w = Math.abs(params.sourcePos[0] - params.targetPos[0]),
- h = Math.abs(params.sourcePos[1] - params.targetPos[1]),
- x = Math.min(params.sourcePos[0], params.targetPos[0]),
- y = Math.min(params.sourcePos[1], params.targetPos[1]);
-
- if (!showLoopback || (params.sourceEndpoint.elementId !== params.targetEndpoint.elementId)) {
- var _sx = params.sourcePos[0] < params.targetPos[0] ? 0 : w,
- _sy = params.sourcePos[1] < params.targetPos[1] ? 0:h,
- _tx = params.sourcePos[0] < params.targetPos[0] ? w : 0,
- _ty = params.sourcePos[1] < params.targetPos[1] ? h : 0;
-
- // now adjust for the margin
- if (params.sourcePos[2] === 0) _sx -= margin;
- if (params.sourcePos[2] === 1) _sx += margin;
- if (params.sourcePos[3] === 0) _sy -= margin;
- if (params.sourcePos[3] === 1) _sy += margin;
- if (params.targetPos[2] === 0) _tx -= margin;
- if (params.targetPos[2] === 1) _tx += margin;
- if (params.targetPos[3] === 0) _ty -= margin;
- if (params.targetPos[3] === 1) _ty += margin;
-
- //
- // these connectors are quadratic bezier curves, having a single control point. if both anchors
- // are located at 0.5 on their respective faces, the control point is set to the midpoint and you
- // get a straight line. this is also the case if the two anchors are within 'proximityLimit', since
- // it seems to make good aesthetic sense to do that. outside of that, the control point is positioned
- // at 'curviness' pixels away along the normal to the straight line connecting the two anchors.
- //
- // there may be two improvements to this. firstly, we might actually support the notion of avoiding nodes
- // in the UI, or at least making a good effort at doing so. if a connection would pass underneath some node,
- // for example, we might increase the distance the control point is away from the midpoint in a bid to
- // steer it around that node. this will work within limits, but i think those limits would also be the likely
- // limits for, once again, aesthetic good sense in the layout of a chart using these connectors.
- //
- // the second possible change is actually two possible changes: firstly, it is possible we should gradually
- // decrease the 'curviness' as the distance between the anchors decreases; start tailing it off to 0 at some
- // point (which should be configurable). secondly, we might slightly increase the 'curviness' for connectors
- // with respect to how far their anchor is from the center of its respective face. this could either look cool,
- // or stupid, and may indeed work only in a way that is so subtle as to have been a waste of time.
- //
-
- var _midx = (_sx + _tx) / 2, _midy = (_sy + _ty) / 2,
- m2 = (-1 * _midx) / _midy, theta2 = Math.atan(m2),
- dy = (m2 == Infinity || m2 == -Infinity) ? 0 : Math.abs(curviness / 2 * Math.sin(theta2)),
- dx = (m2 == Infinity || m2 == -Infinity) ? 0 : Math.abs(curviness / 2 * Math.cos(theta2)),
- segment = _segment(_sx, _sy, _tx, _ty),
- distance = Math.sqrt(Math.pow(_tx - _sx, 2) + Math.pow(_ty - _sy, 2)),
- // calculate the control point. this code will be where we'll put in a rudimentary element avoidance scheme; it
- // will work by extending the control point to force the curve to be, um, curvier.
- _controlPoint = _findControlPoint(_midx,
- _midy,
- segment,
- params.sourcePos,
- params.targetPos,
- curviness, curviness,
- distance,
- proximityLimit);
-
- _super.addSegment(this, "Bezier", {
- x1:_tx, y1:_ty, x2:_sx, y2:_sy,
- cp1x:_controlPoint[0], cp1y:_controlPoint[1],
- cp2x:_controlPoint[0], cp2y:_controlPoint[1]
- });
- }
- else {
- // a loopback connector. draw an arc from one anchor to the other.
- var x1 = params.sourcePos[0], x2 = params.sourcePos[0], y1 = params.sourcePos[1] - margin, y2 = params.sourcePos[1] - margin,
- cx = x1, cy = y1 - loopbackRadius,
- // canvas sizing stuff, to ensure the whole painted area is visible.
- _w = 2 * loopbackRadius,
- _h = 2 * loopbackRadius,
- _x = cx - loopbackRadius,
- _y = cy - loopbackRadius;
-
- paintInfo.points[0] = _x;
- paintInfo.points[1] = _y;
- paintInfo.points[2] = _w;
- paintInfo.points[3] = _h;
-
- // ADD AN ARC SEGMENT.
- _super.addSegment(this, "Arc", {
- x1:(x1 - _x) + 4,
- y1:y1 - _y,
- startAngle:0,
- endAngle: 2 * Math.PI,
- r:loopbackRadius,
- ac:!clockwise,
- x2:(x1 - _x) - 4,
- y2:y1 - _y,
- cx:cx - _x,
- cy:cy - _y
- });
- }
- };
- };
- jsPlumb.registerConnectorType(StateMachine, "StateMachine");
-})();
-
-/*
- // a possible rudimentary avoidance scheme, old now, perhaps not useful.
- // if (avoidSelector) {
- // var testLine = new Line(sourcePos[0] + _sx,sourcePos[1] + _sy,sourcePos[0] + _tx,sourcePos[1] + _ty);
- // var sel = jsPlumb.getSelector(avoidSelector);
- // for (var i = 0; i < sel.length; i++) {
- // var id = jsPlumb.getId(sel[i]);
- // if (id != sourceEndpoint.elementId && id != targetEndpoint.elementId) {
- // o = jsPlumb.getOffset(id), s = jsPlumb.getSize(id);
-//
-// if (o && s) {
-// var collision = testLine.rectIntersect(o.left,o.top,s[0],s[1]);
-// if (collision) {
- // set the control point to be a certain distance from the midpoint of the two points that
- // the line crosses on the rectangle.
- // TODO where will this 75 number come from?
- // _controlX = collision[2][0] + (75 * collision[3][0]);
- // / _controlY = collision[2][1] + (75 * collision[3][1]);
-// }
-// }
- // }
- // }
- //}
- */
-
-;(function() {
-
- var Bezier = function(params) {
- params = params || {};
-
- var self = this,
- _super = jsPlumb.Connectors.AbstractConnector.apply(this, arguments),
- stub = params.stub || 50,
- majorAnchor = params.curviness || 150,
- minorAnchor = 10;
-
- this.type = "Bezier";
- this.getCurviness = function() { return majorAnchor; };
-
- this._findControlPoint = function(point, sourceAnchorPosition, targetAnchorPosition, sourceEndpoint, targetEndpoint) {
- // determine if the two anchors are perpendicular to each other in their orientation. we swap the control
- // points around if so (code could be tightened up)
- var soo = sourceEndpoint.anchor.getOrientation(sourceEndpoint),
- too = targetEndpoint.anchor.getOrientation(targetEndpoint),
- perpendicular = soo[0] != too[0] || soo[1] == too[1],
- p = [];
-
- if (!perpendicular) {
- if (soo[0] === 0) // X
- p.push(sourceAnchorPosition[0] < targetAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
- else p.push(point[0] - (majorAnchor * soo[0]));
-
- if (soo[1] === 0) // Y
- p.push(sourceAnchorPosition[1] < targetAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
- else p.push(point[1] + (majorAnchor * too[1]));
- }
- else {
- if (too[0] === 0) // X
- p.push(targetAnchorPosition[0] < sourceAnchorPosition[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
- else p.push(point[0] + (majorAnchor * too[0]));
-
- if (too[1] === 0) // Y
- p.push(targetAnchorPosition[1] < sourceAnchorPosition[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
- else p.push(point[1] + (majorAnchor * soo[1]));
- }
-
- return p;
- };
-
- this._compute = function(paintInfo, p) {
- var sp = p.sourcePos,
- tp = p.targetPos,
- _w = Math.abs(sp[0] - tp[0]),
- _h = Math.abs(sp[1] - tp[1]),
- _sx = sp[0] < tp[0] ? _w : 0,
- _sy = sp[1] < tp[1] ? _h : 0,
- _tx = sp[0] < tp[0] ? 0 : _w,
- _ty = sp[1] < tp[1] ? 0 : _h,
- _CP = self._findControlPoint([_sx, _sy], sp, tp, p.sourceEndpoint, p.targetEndpoint),
- _CP2 = self._findControlPoint([_tx, _ty], tp, sp, p.targetEndpoint, p.sourceEndpoint);
-
- _super.addSegment(this, "Bezier", {
- x1:_sx, y1:_sy, x2:_tx, y2:_ty,
- cp1x:_CP[0], cp1y:_CP[1], cp2x:_CP2[0], cp2y:_CP2[1]
- });
- };
- };
-
- jsPlumb.registerConnectorType(Bezier, "Bezier");
-
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the HTML5 canvas renderers. Support for canvas was dropped in 1.4.2.
- * This is being kept around because canvas might make a comeback as a single-page solution
- * that also supports node rendering.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-;(function() {
-
-
-// ********************************* CANVAS RENDERERS FOR CONNECTORS AND ENDPOINTS *******************************************************************
-
- // TODO refactor to renderer common script. put a ref to jsPlumb.sizeCanvas in there too.
- var _connectionBeingDragged = null,
- _hasClass = function(el, clazz) { return jsPlumb.CurrentLibrary.hasClass(_getElementObject(el), clazz); },
- _getElementObject = function(el) { return jsPlumb.CurrentLibrary.getElementObject(el); },
- _getOffset = function(el) { return jsPlumb.CurrentLibrary.getOffset(_getElementObject(el)); },
- _pageXY = function(el) { return jsPlumb.CurrentLibrary.getPageXY(el); },
- _clientXY = function(el) { return jsPlumb.CurrentLibrary.getClientXY(el); };
-
- /*
- * Class:CanvasMouseAdapter
- * Provides support for mouse events on canvases.
- */
- var CanvasMouseAdapter = window.CanvasMouseAdapter = function() {
- var self = this;
- self.overlayPlacements = [];
- jsPlumb.jsPlumbUIComponent.apply(this, arguments);
- jsPlumbUtil.EventGenerator.apply(this, arguments);
- /**
- * returns whether or not the given event is ojver a painted area of the canvas.
- */
- this._over = function(e) {
- var o = _getOffset(_getElementObject(self.canvas)),
- pageXY = _pageXY(e),
- x = pageXY[0] - o.left, y = pageXY[1] - o.top;
- if (x > 0 && y > 0 && x < self.canvas.width && y < self.canvas.height) {
- // first check overlays
- for ( var i = 0; i < self.overlayPlacements.length; i++) {
- var p = self.overlayPlacements[i];
- if (p && (p[0] <= x && p[1] >= x && p[2] <= y && p[3] >= y))
- return true;
- }
- // then the canvas
- var d = self.canvas.getContext("2d").getImageData(parseInt(x, 10), parseInt(y, 10), 1, 1);
- return d.data[0] !== 0 || d.data[1] !== 0 || d.data[2] !== 0 || d.data[3] !== 0;
- }
- return false;
- };
-
- var _mouseover = false, _mouseDown = false, _posWhenMouseDown = null, _mouseWasDown = false,
- _nullSafeHasClass = function(el, clazz) {
- return el !== null && _hasClass(el, clazz);
- };
- this.mousemove = function(e) {
- var pageXY = _pageXY(e), clientXY = _clientXY(e),
- ee = document.elementFromPoint(clientXY[0], clientXY[1]),
- eventSourceWasOverlay = _nullSafeHasClass(ee, "_jsPlumb_overlay");
- var _continue = _connectionBeingDragged === null && (_nullSafeHasClass(ee, "_jsPlumb_endpoint") || _nullSafeHasClass(ee, "_jsPlumb_connector"));
- if (!_mouseover && _continue && self._over(e)) {
- _mouseover = true;
- self.fire("mouseenter", self, e);
- return true;
- }
- // TODO here there is a remote chance that the overlay the mouse moved onto
- // is actually not an overlay for the current component. a more thorough check would
- // be to ensure the overlay belonged to the current component.
- else if (_mouseover && (!self._over(e) || !_continue) && !eventSourceWasOverlay) {
- _mouseover = false;
- self.fire("mouseexit", self, e);
- }
- self.fire("mousemove", self, e);
- };
-
- this.click = function(e) {
- if (_mouseover && self._over(e) && !_mouseWasDown)
- self.fire("click", self, e);
- _mouseWasDown = false;
- };
-
- this.dblclick = function(e) {
- if (_mouseover && self._over(e) && !_mouseWasDown)
- self.fire("dblclick", self, e);
- _mouseWasDown = false;
- };
-
- this.mousedown = function(e) {
- if(self._over(e) && !_mouseDown) {
- _mouseDown = true;
- _posWhenMouseDown = _getOffset(_getElementObject(self.canvas));
- self.fire("mousedown", self, e);
- }
- };
-
- this.mouseup = function(e) {
- _mouseDown = false;
- self.fire("mouseup", self, e);
- };
-
- this.contextmenu = function(e) {
- if (_mouseover && self._over(e) && !_mouseWasDown)
- self.fire("contextmenu", self, e);
- _mouseWasDown = false;
- };
- };
- jsPlumbUtil.extend(CanvasMouseAdapter, [ jsPlumb.jsPlumbUIComponent, jsPlumbUtil.EventGenerator ]);
-
- var _newCanvas = function(params) {
- var canvas = document.createElement("canvas");
- params._jsPlumb.instance.appendElement(canvas, params.parent);
- canvas.style.position = "absolute";
- if (params["class"]) canvas.className = params["class"];
- // set an id. if no id on the element and if uuid was supplied it
- // will be used, otherwise we'll create one.
- params._jsPlumb.instance.getId(canvas, params.uuid);
- if (params.tooltip) canvas.setAttribute("title", params.tooltip);
-
- return canvas;
- };
-
- var CanvasComponent = window.CanvasComponent = function(params) {
- CanvasMouseAdapter.apply(this, arguments);
-
- var displayElements = [ ];
- this.getDisplayElements = function() { return displayElements; };
- this.appendDisplayElement = function(el) { displayElements.push(el); };
- };
- jsPlumbUtil.extend(CanvasComponent, CanvasMouseAdapter);
-
- var segmentMultipliers = [null, [1, -1], [1, 1], [-1, 1], [-1, -1] ];
- var maybeMakeGradient = function(ctx, style, gradientFunction) {
- if (style.gradient) {
- var g = gradientFunction();
- for ( var i = 0; i < style.gradient.stops.length; i++)
- g.addColorStop(style.gradient.stops[i][0], style.gradient.stops[i][1]);
- ctx.strokeStyle = g;
- }
- };
- var segmentRenderer = function(segment, ctx, style, dx, dy) {
- ({
- "Straight":function(segment, ctx, style, dx, dy) {
- var d = segment.params;
- ctx.save();
- maybeMakeGradient(ctx, style, function() { return ctx.createLinearGradient(d.x1, d.y1, d.x2, d.y2); });
- ctx.beginPath();
- ctx.translate(dx, dy);
- if (style.dashstyle && style.dashstyle.split(" ").length === 2) {
- // only a very simple dashed style is supported - having two values, which define the stroke length
- // (as a multiple of the stroke width) and then the space length (also as a multiple of stroke width).
- var ds = style.dashstyle.split(" ");
- if (ds.length !== 2) ds = [2, 2];
- var dss = [ ds[0] * style.lineWidth, ds[1] * style.lineWidth ],
- m = (d.x2- d.x1) / (d.y2 - d.y1),
- s = jsPlumbUtil.segment([d.x1, d.y1], [ d.x2, d.y2 ]),
- sm = segmentMultipliers[s],
- theta = Math.atan(m),
- l = Math.sqrt(Math.pow(d.x2 - d.x1, 2) + Math.pow(d.y2 - d.y1, 2)),
- repeats = Math.floor(l / (dss[0] + dss[1])),
- curPos = [d.x1, d.y1];
-
-
- // TODO: the question here is why could we not support this in all connector types? it's really
- // just a case of going along and asking jsPlumb for the next point on the path a few times, until it
- // reaches the end. every type of connector supports that method, after all. but right now its only the
- // bezier connector that gives you back the new location on the path along with the x,y coordinates, which
- // we would need. we'd start out at loc=0 and ask for the point along the path that is dss[0] pixels away.
- // we then ask for the point that is (dss[0] + dss[1]) pixels away; and from that one we need not just the
- // x,y but the location, cos we're gonna plug that location back in in order to find where that dash ends.
- //
- // it also strikes me that it should be trivial to support arbitrary dash styles (having more or less than two
- // entries). you'd just iterate that array using a step size of 2, and generify the (rss[0] + rss[1])
- // computation to be sum(rss[0]..rss[n]).
-
- for (var i = 0; i < repeats; i++) {
- ctx.moveTo(curPos[0], curPos[1]);
-
- var nextEndX = curPos[0] + (Math.abs(Math.sin(theta) * dss[0]) * sm[0]),
- nextEndY = curPos[1] + (Math.abs(Math.cos(theta) * dss[0]) * sm[1]),
- nextStartX = curPos[0] + (Math.abs(Math.sin(theta) * (dss[0] + dss[1])) * sm[0]),
- nextStartY = curPos[1] + (Math.abs(Math.cos(theta) * (dss[0] + dss[1])) * sm[1]);
-
- ctx.lineTo(nextEndX, nextEndY);
- curPos = [nextStartX, nextStartY];
- }
-
- // now draw the last bit
- ctx.moveTo(curPos[0], curPos[1]);
- ctx.lineTo(d.x2, d.y2);
-
- }
- else {
- ctx.moveTo(d.x1, d.y1);
- ctx.lineTo(d.x2, d.y2);
- }
-
- ctx.stroke();
-
- ctx.restore();
- },
- "Bezier":function(segment, ctx, style, dx, dy) {
- var d = segment.params;
- ctx.save();
- maybeMakeGradient(ctx, style, function() { return ctx.createLinearGradient(d.x2 + dx, d.y2 + dy, d.x1 + dx, d.y1 + dy); });
- ctx.beginPath();
- ctx.translate(dx, dy);
- ctx.moveTo(d.x1, d.y1);
- ctx.bezierCurveTo(d.cp1x, d.cp1y, d.cp2x, d.cp2y, d.x2, d.y2);
- ctx.stroke();
- ctx.restore();
- },
- "Arc":function(segment, ctx, style, dx, dy) {
- var d = segment.params;
- ctx.save();
- ctx.beginPath();
- ctx.translate(dx, dy);
- ctx.arc(d.cx, d.cy, d.r, segment.startAngle, segment.endAngle, d.ac);
- ctx.stroke();
- ctx.restore();
- }
- })[segment.type](segment, ctx, style, dx, dy);
- };
-
- /**
- * Class:CanvasConnector
- * Superclass for Canvas Connector renderers.
- */
- var CanvasConnector = jsPlumb.ConnectorRenderers.canvas = function(params) {
- CanvasComponent.apply(this, arguments);
-
- var _paintOneStyle = function(aStyle, dx, dy) {
- this.ctx.save();
- jsPlumb.extend(this.ctx, aStyle);
-
- var segments = this.getSegments();
- for (var i = 0; i < segments.length; i++) {
- segmentRenderer(segments[i], this.ctx, aStyle, dx, dy);
- }
- this.ctx.restore();
- }.bind(this);
-
- var clazz = this._jsPlumb.instance.connectorClass + " " + (params.cssClass || "");
- this.canvas = _newCanvas({
- "class":clazz,
- _jsPlumb:this._jsPlumb,
- parent:params.parent
- });
- this.ctx = this.canvas.getContext("2d");
-
- this.appendDisplayElement(this.canvas);
-
- this.paint = function(style, anchor, extents) {
- if (style != null) {
-
- var xy = [ this.x, this.y ], wh = [ this.w, this.h ], p,
- dx = 0, dy = 0;
-
- if (extents != null) {
- if (extents.xmin < 0) {
- xy[0] += extents.xmin;
- dx = -extents.xmin;
- }
- if (extents.ymin < 0) {
- xy[1] += extents.ymin;
- dy = -extents.ymin;
- }
- wh[0] = extents.xmax + ((extents.xmin < 0) ? -extents.xmin : 0);
- wh[1] = extents.ymax + ((extents.ymin < 0) ? -extents.ymin : 0);
- }
-
- this.translateX = dx;
- this.translateY = dy;
-
- jsPlumbUtil.sizeElement(this.canvas, xy[0], xy[1], wh[0], wh[1]);
-
- if (style.outlineColor != null) {
- var outlineWidth = style.outlineWidth || 1,
- outlineStrokeWidth = style.lineWidth + (2 * outlineWidth),
- outlineStyle = {
- strokeStyle:style.outlineColor,
- lineWidth:outlineStrokeWidth
- };
- _paintOneStyle(outlineStyle, dx, dy);
- }
- _paintOneStyle(style, dx, dy);
- }
- };
- };
- jsPlumbUtil.extend(CanvasConnector, CanvasComponent);
-
-
- /**
- * Class:CanvasEndpoint
- * Superclass for Canvas Endpoint renderers.
- */
- var CanvasEndpoint = function(params) {
- CanvasComponent.apply(this, arguments);
- var clazz = this._jsPlumb.instance.endpointClass + " " + (params.cssClass || ""),
- canvasParams = {
- "class":clazz,
- _jsPlumb:this._jsPlumb,
- parent:params.parent,
- tooltip:self.tooltip
- };
- this.canvas = _newCanvas(canvasParams);
- this.ctx = this.canvas.getContext("2d");
-
- this.appendDisplayElement(this.canvas);
-
- this.paint = function(style, anchor, extents) {
- jsPlumbUtil.sizeElement(this.canvas, this.x, this.y, this.w, this.h);
- if (style.outlineColor != null) {
- var outlineWidth = style.outlineWidth || 1,
- outlineStrokeWidth = style.lineWidth + (2 * outlineWidth);
- var outlineStyle = {
- strokeStyle:style.outlineColor,
- lineWidth:outlineStrokeWidth
- };
- }
-
- this._paint.apply(this, arguments);
- };
- };
- jsPlumbUtil.extend(CanvasEndpoint, CanvasComponent);
-
- jsPlumb.Endpoints.canvas.Dot = function(params) {
- jsPlumb.Endpoints.Dot.apply(this, arguments);
- CanvasEndpoint.apply(this, arguments);
- var self = this,
- parseValue = function(value) {
- try { return parseInt(value, 10); }
- catch(e) {
- if (value.substring(value.length - 1) == '%')
- return parseInt(value.substring(0, value - 1), 10);
- }
- },
- calculateAdjustments = function(gradient) {
- var offsetAdjustment = self.defaultOffset, innerRadius = self.defaultInnerRadius;
- if (gradient.offset) offsetAdjustment = parseValue(gradient.offset);
- if (gradient.innerRadius) innerRadius = parseValue(gradient.innerRadius);
- return [offsetAdjustment, innerRadius];
- };
- this._paint = function(style) {
- if (style != null) {
- var ctx = self.canvas.getContext('2d'),
- orientation = params.endpoint.anchor.getOrientation(params.endpoint);
-
- jsPlumb.extend(ctx, style);
- if (style.gradient) {
- var adjustments = calculateAdjustments(style.gradient),
- yAdjust = orientation[1] == 1 ? adjustments[0] * -1 : adjustments[0],
- xAdjust = orientation[0] == 1 ? adjustments[0] * -1: adjustments[0],
- g = ctx.createRadialGradient(self.radius, self.radius, self.radius, self.radius + xAdjust, self.radius + yAdjust, adjustments[1]);
- for (var i = 0; i < style.gradient.stops.length; i++)
- g.addColorStop(style.gradient.stops[i][0], style.gradient.stops[i][1]);
- ctx.fillStyle = g;
- }
- ctx.beginPath();
- //ctx.translate(dx, dy);
- ctx.arc(self.radius, self.radius, self.radius, 0, Math.PI*2, true);
- ctx.closePath();
- if (style.fillStyle || style.gradient) ctx.fill();
- if (style.strokeStyle) ctx.stroke();
- }
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.canvas.Dot, [ jsPlumb.Endpoints.Dot, CanvasEndpoint ]);
-
- jsPlumb.Endpoints.canvas.Rectangle = function(params) {
-
- var self = this;
- jsPlumb.Endpoints.Rectangle.apply(this, arguments);
- CanvasEndpoint.apply(this, arguments);
-
- this._paint = function(style) {
-
- var ctx = self.canvas.getContext("2d"),
- orientation = params.endpoint.anchor.getOrientation(params.endpoint);
-
- jsPlumb.extend(ctx, style);
-
- /* canvas gradient */
- if (style.gradient) {
- // first figure out which direction to run the gradient in (it depends on the orientation of the anchors)
- var y1 = orientation[1] == 1 ? self.h : orientation[1] === 0 ? self.h / 2 : 0;
- var y2 = orientation[1] == -1 ? self.h : orientation[1] === 0 ? self.h / 2 : 0;
- var x1 = orientation[0] == 1 ? self.w : orientation[0] === 0 ? self.w / 2 : 0;
- var x2 = orientation[0] == -1 ? self.w : orientation[0] === 0 ? self.w / 2 : 0;
- var g = ctx.createLinearGradient(x1,y1,x2,y2);
- for (var i = 0; i < style.gradient.stops.length; i++)
- g.addColorStop(style.gradient.stops[i][0], style.gradient.stops[i][1]);
- ctx.fillStyle = g;
- }
-
- ctx.beginPath();
- ctx.rect(0, 0, self.w, self.h);
- ctx.closePath();
- if (style.fillStyle || style.gradient) ctx.fill();
- if (style.strokeStyle) ctx.stroke();
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.canvas.Rectangle, [ jsPlumb.Endpoints.Rectangle, CanvasEndpoint ]);
-
- jsPlumb.Endpoints.canvas.Triangle = function(params) {
-
- var self = this;
- jsPlumb.Endpoints.Triangle.apply(this, arguments);
- CanvasEndpoint.apply(this, arguments);
-
- this._paint = function(style) {
- var ctx = self.canvas.getContext('2d'),
- offsetX = 0, offsetY = 0, angle = 0,
- orientation = params.endpoint.anchor.getOrientation(params.endpoint);
-
- if( orientation[0] == 1 ) {
- offsetX = self.width;
- offsetY = self.height;
- angle = 180;
- }
- if( orientation[1] == -1 ) {
- offsetX = self.width;
- angle = 90;
- }
- if( orientation[1] == 1 ) {
- offsetY = self.height;
- angle = -90;
- }
-
- ctx.fillStyle = style.fillStyle;
-
- ctx.translate(offsetX, offsetY);
- ctx.rotate(angle * Math.PI/180);
-
- ctx.beginPath();
- ctx.moveTo(0, 0);
- ctx.lineTo(self.width/2, self.height/2);
- ctx.lineTo(0, self.height);
- ctx.closePath();
- if (style.fillStyle || style.gradient) ctx.fill();
- if (style.strokeStyle) ctx.stroke();
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.canvas.Triangle, [ jsPlumb.Endpoints.Triangle, CanvasEndpoint ]);
-
- /*
- * Canvas Image Endpoint: uses the default version, which creates an <img> tag.
- */
- jsPlumb.Endpoints.canvas.Image = jsPlumb.Endpoints.Image;
-
- /*
- * Blank endpoint in all renderers is just the default Blank endpoint.
- */
- jsPlumb.Endpoints.canvas.Blank = jsPlumb.Endpoints.Blank;
-
- /*
- * Canvas Bezier Connector. Draws a Bezier curve onto a Canvas element.
- *
- jsPlumb.Connectors.canvas.Bezier = function() {
- jsPlumb.Connectors.Bezier.apply(this, arguments);
- CanvasConnector.apply(this, arguments);
- };
- jsPlumbUtil.extend(jsPlumb.Connectors.canvas.Bezier, [ jsPlumb.Connectors.Bezier, CanvasConnector ]);
-
- /*
- * Canvas straight line Connector. Draws a straight line onto a Canvas element.
- *
- jsPlumb.Connectors.canvas.Straight = function() {
- jsPlumb.Connectors.Straight.apply(this, arguments);
- CanvasConnector.apply(this, arguments);
- };
- jsPlumbUtil.extend(jsPlumb.Connectors.canvas.Straight, [ jsPlumb.Connectors.Straight, CanvasConnector ]);
-
- jsPlumb.Connectors.canvas.Flowchart = function() {
- jsPlumb.Connectors.Flowchart.apply(this, arguments);
- CanvasConnector.apply(this, arguments);
- };
- jsPlumbUtil.extend(jsPlumb.Connectors.canvas.Flowchart, [ jsPlumb.Connectors.Flowchart, CanvasConnector ]);
-
- */
-// ********************************* END OF CANVAS RENDERERS *******************************************************************
-
- jsPlumb.Overlays.canvas.Label = jsPlumb.Overlays.Label;
- jsPlumb.Overlays.canvas.Custom = jsPlumb.Overlays.Custom;
-
- /**
- * a placeholder right now, really just exists to mirror the fact that there are SVG and VML versions of this.
- */
- var CanvasOverlay = function() {
- jsPlumb.jsPlumbUIComponent.apply(this, arguments);
- };
- jsPlumbUtil.extend(CanvasOverlay, jsPlumb.jsPlumbUIComponent);
-
- var AbstractCanvasArrowOverlay = function(superclass, originalArgs) {
- superclass.apply(this, originalArgs);
- CanvasOverlay.apply(this, originalArgs);
- this.paint = function(params, containerExtents) {
- var ctx = params.component.ctx, d = params.d;
-
- if (d) {
- ctx.save();
- ctx.lineWidth = params.lineWidth;
- ctx.beginPath();
- ctx.translate(params.component.translateX, params.component.translateY);
- ctx.moveTo(d.hxy.x, d.hxy.y);
- ctx.lineTo(d.tail[0].x, d.tail[0].y);
- ctx.lineTo(d.cxy.x, d.cxy.y);
- ctx.lineTo(d.tail[1].x, d.tail[1].y);
- ctx.lineTo(d.hxy.x, d.hxy.y);
- ctx.closePath();
-
- if (params.strokeStyle) {
- ctx.strokeStyle = params.strokeStyle;
- ctx.stroke();
- }
- if (params.fillStyle) {
- ctx.fillStyle = params.fillStyle;
- ctx.fill();
- }
- ctx.restore();
- }
- };
- };
-
- jsPlumb.Overlays.canvas.Arrow = function() {
- AbstractCanvasArrowOverlay.apply(this, [jsPlumb.Overlays.Arrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.canvas.Arrow, [ jsPlumb.Overlays.Arrow, CanvasOverlay ] );
-
- jsPlumb.Overlays.canvas.PlainArrow = function() {
- AbstractCanvasArrowOverlay.apply(this, [jsPlumb.Overlays.PlainArrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.canvas.PlainArrow, [ jsPlumb.Overlays.PlainArrow, CanvasOverlay ] );
-
- jsPlumb.Overlays.canvas.Diamond = function() {
- AbstractCanvasArrowOverlay.apply(this, [jsPlumb.Overlays.Diamond, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.canvas.Diamond, [ jsPlumb.Overlays.Diamond, CanvasOverlay ] );
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the SVG renderers.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-/**
- * SVG support for jsPlumb.
- *
- * things to investigate:
- *
- * gradients: https://developer.mozilla.org/en/svg_in_html_introduction
- * css:http://tutorials.jenkov.com/svg/svg-and-css.html
- * text on a path: http://www.w3.org/TR/SVG/text.html#TextOnAPath
- * pointer events: https://developer.mozilla.org/en/css/pointer-events
- *
- * IE9 hover jquery: http://forum.jquery.com/topic/1-6-2-broke-svg-hover-events
- *
- */
-;(function() {
-
-// ************************** SVG utility methods ********************************************
-
- var svgAttributeMap = {
- "joinstyle":"stroke-linejoin",
- "stroke-linejoin":"stroke-linejoin",
- "stroke-dashoffset":"stroke-dashoffset",
- "stroke-linecap":"stroke-linecap"
- },
- STROKE_DASHARRAY = "stroke-dasharray",
- DASHSTYLE = "dashstyle",
- LINEAR_GRADIENT = "linearGradient",
- RADIAL_GRADIENT = "radialGradient",
- FILL = "fill",
- STOP = "stop",
- STROKE = "stroke",
- STROKE_WIDTH = "stroke-width",
- STYLE = "style",
- NONE = "none",
- JSPLUMB_GRADIENT = "jsplumb_gradient_",
- LINE_WIDTH = "lineWidth",
- ns = {
- svg:"http://www.w3.org/2000/svg",
- xhtml:"http://www.w3.org/1999/xhtml"
- },
- _attr = function(node, attributes) {
- for (var i in attributes)
- node.setAttribute(i, "" + attributes[i]);
- },
- _node = function(name, attributes) {
- var n = document.createElementNS(ns.svg, name);
- attributes = attributes || {};
- attributes.version = "1.1";
- attributes.xmlns = ns.xhtml;
- _attr(n, attributes);
- return n;
- },
- _pos = function(d) { return "position:absolute;left:" + d[0] + "px;top:" + d[1] + "px"; },
- _clearGradient = function(parent) {
- for (var i = 0; i < parent.childNodes.length; i++) {
- if (parent.childNodes[i].tagName == LINEAR_GRADIENT || parent.childNodes[i].tagName == RADIAL_GRADIENT)
- parent.removeChild(parent.childNodes[i]);
- }
- },
- _updateGradient = function(parent, node, style, dimensions, uiComponent) {
- var id = JSPLUMB_GRADIENT + uiComponent._jsPlumb.instance.idstamp();
- // first clear out any existing gradient
- _clearGradient(parent);
- // this checks for an 'offset' property in the gradient, and in the absence of it, assumes
- // we want a linear gradient. if it's there, we create a radial gradient.
- // it is possible that a more explicit means of defining the gradient type would be
- // better. relying on 'offset' means that we can never have a radial gradient that uses
- // some default offset, for instance.
- // issue 244 suggested the 'gradientUnits' attribute; without this, straight/flowchart connectors with gradients would
- // not show gradients when the line was perfectly horizontal or vertical.
- var g;
- if (!style.gradient.offset) {
- g = _node(LINEAR_GRADIENT, {id:id, gradientUnits:"userSpaceOnUse"});
- }
- else {
- g = _node(RADIAL_GRADIENT, {
- id:id
- });
- }
-
- parent.appendChild(g);
-
- // the svg radial gradient seems to treat stops in the reverse
- // order to how canvas does it. so we want to keep all the maths the same, but
- // iterate the actual style declarations in reverse order, if the x indexes are not in order.
- for (var i = 0; i < style.gradient.stops.length; i++) {
- var styleToUse = uiComponent.segment == 1 || uiComponent.segment == 2 ? i: style.gradient.stops.length - 1 - i,
- stopColor = jsPlumbUtil.convertStyle(style.gradient.stops[styleToUse][1], true),
- s = _node(STOP, {"offset":Math.floor(style.gradient.stops[i][0] * 100) + "%", "stop-color":stopColor});
-
- g.appendChild(s);
- }
- var applyGradientTo = style.strokeStyle ? STROKE : FILL;
- node.setAttribute(STYLE, applyGradientTo + ":url(#" + id + ")");
- },
- _applyStyles = function(parent, node, style, dimensions, uiComponent) {
-
- if (style.gradient) {
- _updateGradient(parent, node, style, dimensions, uiComponent);
- }
- else {
- // make sure we clear any existing gradient
- _clearGradient(parent);
- node.setAttribute(STYLE, "");
- }
-
- node.setAttribute(FILL, style.fillStyle ? jsPlumbUtil.convertStyle(style.fillStyle, true) : NONE);
- node.setAttribute(STROKE, style.strokeStyle ? jsPlumbUtil.convertStyle(style.strokeStyle, true) : NONE);
- if (style.lineWidth) {
- node.setAttribute(STROKE_WIDTH, style.lineWidth);
- }
-
- // in SVG there is a stroke-dasharray attribute we can set, and its syntax looks like
- // the syntax in VML but is actually kind of nasty: values are given in the pixel
- // coordinate space, whereas in VML they are multiples of the width of the stroked
- // line, which makes a lot more sense. for that reason, jsPlumb is supporting both
- // the native svg 'stroke-dasharray' attribute, and also the 'dashstyle' concept from
- // VML, which will be the preferred method. the code below this converts a dashstyle
- // attribute given in terms of stroke width into a pixel representation, by using the
- // stroke's lineWidth.
- if (style[DASHSTYLE] && style[LINE_WIDTH] && !style[STROKE_DASHARRAY]) {
- var sep = style[DASHSTYLE].indexOf(",") == -1 ? " " : ",",
- parts = style[DASHSTYLE].split(sep),
- styleToUse = "";
- parts.forEach(function(p) {
- styleToUse += (Math.floor(p * style.lineWidth) + sep);
- });
- node.setAttribute(STROKE_DASHARRAY, styleToUse);
- }
- else if(style[STROKE_DASHARRAY]) {
- node.setAttribute(STROKE_DASHARRAY, style[STROKE_DASHARRAY]);
- }
-
- // extra attributes such as join type, dash offset.
- for (var i in svgAttributeMap) {
- if (style[i]) {
- node.setAttribute(svgAttributeMap[i], style[i]);
- }
- }
- },
- _decodeFont = function(f) {
- var r = /([0-9].)(p[xt])\s(.*)/,
- bits = f.match(r);
-
- return {size:bits[1] + bits[2], font:bits[3]};
- },
- _classManip = function(el, add, clazz) {
- var classesToAddOrRemove = clazz.split(" "),
- className = el.className,
- curClasses = className.baseVal.split(" ");
-
- for (var i = 0; i < classesToAddOrRemove.length; i++) {
- if (add) {
- if (curClasses.indexOf(classesToAddOrRemove[i]) == -1)
- curClasses.push(classesToAddOrRemove[i]);
- }
- else {
- var idx = curClasses.indexOf(classesToAddOrRemove[i]);
- if (idx != -1)
- curClasses.splice(idx, 1);
- }
- }
-
- el.className.baseVal = curClasses.join(" ");
- },
- _addClass = function(el, clazz) { _classManip(el, true, clazz); },
- _removeClass = function(el, clazz) { _classManip(el, false, clazz); },
- _appendAtIndex = function(svg, path, idx) {
- if (svg.childNodes.length > idx) {
- svg.insertBefore(path, svg.childNodes[idx]);
- }
- else svg.appendChild(path);
- };
-
- /**
- utility methods for other objects to use.
- */
- jsPlumbUtil.svg = {
- addClass:_addClass,
- removeClass:_removeClass,
- node:_node,
- attr:_attr,
- pos:_pos
- };
-
- // ************************** / SVG utility methods ********************************************
-
- /*
- * Base class for SVG components.
- */
- var SvgComponent = function(params) {
- var pointerEventsSpec = params.pointerEventsSpec || "all", renderer = {};
-
- jsPlumb.jsPlumbUIComponent.apply(this, params.originalArgs);
- this.canvas = null;this.path = null;this.svg = null;
-
- var clazz = params.cssClass + " " + (params.originalArgs[0].cssClass || ""),
- svgParams = {
- "style":"",
- "width":0,
- "height":0,
- "pointer-events":pointerEventsSpec,
- "position":"absolute"
- };
- this.svg = _node("svg", svgParams);
- if (params.useDivWrapper) {
- this.canvas = document.createElement("div");
- this.canvas.style.position = "absolute";
- jsPlumbUtil.sizeElement(this.canvas,0,0,1,1);
- this.canvas.className = clazz;
- }
- else {
- _attr(this.svg, { "class":clazz });
- this.canvas = this.svg;
- }
-
- params._jsPlumb.appendElement(this.canvas, params.originalArgs[0].parent);
- if (params.useDivWrapper) this.canvas.appendChild(this.svg);
-
- // TODO this displayElement stuff is common between all components, across all
- // renderers. would be best moved to jsPlumbUIComponent.
- var displayElements = [ this.canvas ];
- this.getDisplayElements = function() {
- return displayElements;
- };
-
- this.appendDisplayElement = function(el) {
- displayElements.push(el);
- };
-
- this.paint = function(style, anchor, extents) {
- if (style != null) {
-
- var xy = [ this.x, this.y ], wh = [ this.w, this.h ], p;
- if (extents != null) {
- if (extents.xmin < 0) xy[0] += extents.xmin;
- if (extents.ymin < 0) xy[1] += extents.ymin;
- wh[0] = extents.xmax + ((extents.xmin < 0) ? -extents.xmin : 0);
- wh[1] = extents.ymax + ((extents.ymin < 0) ? -extents.ymin : 0);
- }
-
- if (params.useDivWrapper) {
- jsPlumbUtil.sizeElement(this.canvas, xy[0], xy[1], wh[0], wh[1]);
- xy[0] = 0; xy[1] = 0;
- p = _pos([ 0, 0 ]);
- }
- else
- p = _pos([ xy[0], xy[1] ]);
-
- renderer.paint.apply(this, arguments);
-
- _attr(this.svg, {
- "style":p,
- "width": wh[0],
- "height": wh[1]
- });
- }
- };
-
- return {
- renderer:renderer
- };
- };
- jsPlumbUtil.extend(SvgComponent, jsPlumb.jsPlumbUIComponent, {
- cleanup:function() {
- jsPlumbUtil.removeElement(this.canvas);
- this.svg = null;
- this.canvas = null;
- this.path = null;
- }
- });
-
- /*
- * Base class for SVG connectors.
- */
- var SvgConnector = jsPlumb.ConnectorRenderers.svg = function(params) {
- var self = this,
- _super = SvgComponent.apply(this, [ {
- cssClass:params._jsPlumb.connectorClass,
- originalArgs:arguments,
- pointerEventsSpec:"none",
- _jsPlumb:params._jsPlumb
- } ]);
-
- /*this.pointOnPath = function(location, absolute) {
- if (!self.path) return [0,0];
- var p = absolute ? location : location * self.path.getTotalLength();
- return self.path.getPointAtLength(p);
- };*/
-
- _super.renderer.paint = function(style, anchor, extents) {
-
- var segments = self.getSegments(), p = "", offset = [0,0];
- if (extents.xmin < 0) offset[0] = -extents.xmin;
- if (extents.ymin < 0) offset[1] = -extents.ymin;
-
- // create path from segments.
- for (var i = 0; i < segments.length; i++) {
- p += jsPlumb.Segments.svg.SegmentRenderer.getPath(segments[i]);
- p += " ";
- }
-
- var a = {
- d:p,
- transform:"translate(" + offset[0] + "," + offset[1] + ")",
- "pointer-events":params["pointer-events"] || "visibleStroke"
- },
- outlineStyle = null,
- d = [self.x,self.y,self.w,self.h];
-
- // outline style. actually means drawing an svg object underneath the main one.
- if (style.outlineColor) {
- var outlineWidth = style.outlineWidth || 1,
- outlineStrokeWidth = style.lineWidth + (2 * outlineWidth);
- outlineStyle = jsPlumb.CurrentLibrary.extend({}, style);
- outlineStyle.strokeStyle = jsPlumbUtil.convertStyle(style.outlineColor);
- outlineStyle.lineWidth = outlineStrokeWidth;
-
- if (self.bgPath == null) {
- self.bgPath = _node("path", a);
- _appendAtIndex(self.svg, self.bgPath, 0);
- self.attachListeners(self.bgPath, self);
- }
- else {
- _attr(self.bgPath, a);
- }
-
- _applyStyles(self.svg, self.bgPath, outlineStyle, d, self);
- }
-
- if (self.path == null) {
- self.path = _node("path", a);
- _appendAtIndex(self.svg, self.path, style.outlineColor ? 1 : 0);
- self.attachListeners(self.path, self);
- }
- else {
- _attr(self.path, a);
- }
-
- _applyStyles(self.svg, self.path, style, d, self);
- };
-
- this.reattachListeners = function() {
- if (this.bgPath) this.reattachListenersForElement(this.bgPath, this);
- if (this.path) this.reattachListenersForElement(this.path, this);
- };
- };
- jsPlumbUtil.extend(jsPlumb.ConnectorRenderers.svg, SvgComponent);
-
-// ******************************* svg segment renderer *****************************************************
-
- jsPlumb.Segments.svg = {
- SegmentRenderer : {
- getPath : function(segment) {
- return ({
- "Straight":function() {
- var d = segment.getCoordinates();
- return "M " + d.x1 + " " + d.y1 + " L " + d.x2 + " " + d.y2;
- },
- "Bezier":function() {
- var d = segment.params;
- return "M " + d.x1 + " " + d.y1 +
- " C " + d.cp1x + " " + d.cp1y + " " + d.cp2x + " " + d.cp2y + " " + d.x2 + " " + d.y2;
- },
- "Arc":function() {
- var d = segment.params,
- laf = segment.sweep > Math.PI ? 1 : 0,
- sf = segment.anticlockwise ? 0 : 1;
-
- return "M" + segment.x1 + " " + segment.y1 + " A " + segment.radius + " " + d.r + " 0 " + laf + "," + sf + " " + segment.x2 + " " + segment.y2;
- }
- })[segment.type]();
- }
- }
- };
-
-// ******************************* /svg segments *****************************************************
-
- /*
- * Base class for SVG endpoints.
- */
- var SvgEndpoint = window.SvgEndpoint = function(params) {
- var _super = SvgComponent.apply(this, [ {
- cssClass:params._jsPlumb.endpointClass,
- originalArgs:arguments,
- pointerEventsSpec:"all",
- useDivWrapper:true,
- _jsPlumb:params._jsPlumb
- } ]);
-
- _super.renderer.paint = function(style) {
- var s = jsPlumb.extend({}, style);
- if (s.outlineColor) {
- s.strokeWidth = s.outlineWidth;
- s.strokeStyle = jsPlumbUtil.convertStyle(s.outlineColor, true);
- }
-
- if (this.node == null) {
- this.node = this.makeNode(s);
- this.svg.appendChild(this.node);
- this.attachListeners(this.node, this);
- }
- else if (this.updateNode != null) {
- this.updateNode(this.node);
- }
- _applyStyles(this.svg, this.node, s, [ this.x, this.y, this.w, this.h ], this);
- _pos(this.node, [ this.x, this.y ]);
- }.bind(this);
-
- };
- jsPlumbUtil.extend(SvgEndpoint, SvgComponent, {
- reattachListeners : function() {
- if (this.node) this.reattachListenersForElement(this.node, this);
- }
- });
-
- /*
- * SVG Dot Endpoint
- */
- jsPlumb.Endpoints.svg.Dot = function() {
- jsPlumb.Endpoints.Dot.apply(this, arguments);
- SvgEndpoint.apply(this, arguments);
- this.makeNode = function(style) {
- return _node("circle", {
- "cx" : this.w / 2,
- "cy" : this.h / 2,
- "r" : this.radius
- });
- };
- this.updateNode = function(node) {
- _attr(node, {
- "cx":this.w / 2,
- "cy":this.h / 2,
- "r":this.radius
- });
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.svg.Dot, [jsPlumb.Endpoints.Dot, SvgEndpoint]);
-
- /*
- * SVG Rectangle Endpoint
- */
- jsPlumb.Endpoints.svg.Rectangle = function() {
- jsPlumb.Endpoints.Rectangle.apply(this, arguments);
- SvgEndpoint.apply(this, arguments);
- this.makeNode = function(style) {
- return _node("rect", {
- "width" : this.w,
- "height" : this.h
- });
- };
- this.updateNode = function(node) {
- _attr(node, {
- "width":this.w,
- "height":this.h
- });
- };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.svg.Rectangle, [jsPlumb.Endpoints.Rectangle, SvgEndpoint]);
-
- /*
- * SVG Image Endpoint is the default image endpoint.
- */
- jsPlumb.Endpoints.svg.Image = jsPlumb.Endpoints.Image;
- /*
- * Blank endpoint in svg renderer is the default Blank endpoint.
- */
- jsPlumb.Endpoints.svg.Blank = jsPlumb.Endpoints.Blank;
- /*
- * Label overlay in svg renderer is the default Label overlay.
- */
- jsPlumb.Overlays.svg.Label = jsPlumb.Overlays.Label;
- /*
- * Custom overlay in svg renderer is the default Custom overlay.
- */
- jsPlumb.Overlays.svg.Custom = jsPlumb.Overlays.Custom;
-
- var AbstractSvgArrowOverlay = function(superclass, originalArgs) {
- superclass.apply(this, originalArgs);
- jsPlumb.jsPlumbUIComponent.apply(this, originalArgs);
- this.isAppendedAtTopLevel = false;
- var self = this;
- this.path = null;
- this.paint = function(params, containerExtents) {
- // only draws on connections, not endpoints.
- if (params.component.svg && containerExtents) {
- if (this.path == null) {
- this.path = _node("path", {
- "pointer-events":"all"
- });
- params.component.svg.appendChild(this.path);
-
- this.attachListeners(this.path, params.component);
- this.attachListeners(this.path, this);
- }
- var clazz = originalArgs && (originalArgs.length == 1) ? (originalArgs[0].cssClass || "") : "",
- offset = [0,0];
-
- if (containerExtents.xmin < 0) offset[0] = -containerExtents.xmin;
- if (containerExtents.ymin < 0) offset[1] = -containerExtents.ymin;
-
- _attr(this.path, {
- "d" : makePath(params.d),
- "class" : clazz,
- stroke : params.strokeStyle ? params.strokeStyle : null,
- fill : params.fillStyle ? params.fillStyle : null,
- transform : "translate(" + offset[0] + "," + offset[1] + ")"
- });
- }
- };
- var makePath = function(d) {
- return "M" + d.hxy.x + "," + d.hxy.y +
- " L" + d.tail[0].x + "," + d.tail[0].y +
- " L" + d.cxy.x + "," + d.cxy.y +
- " L" + d.tail[1].x + "," + d.tail[1].y +
- " L" + d.hxy.x + "," + d.hxy.y;
- };
- this.reattachListeners = function() {
- if (this.path) this.reattachListenersForElement(this.path, this);
- };
- };
- jsPlumbUtil.extend(AbstractSvgArrowOverlay, jsPlumb.jsPlumbUIComponent, {
- cleanup : function() {
- if (this.path != null) jsPlumb.CurrentLibrary.removeElement(this.path);
- }
- });
-
- jsPlumb.Overlays.svg.Arrow = function() {
- AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.Arrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.svg.Arrow, [ jsPlumb.Overlays.Arrow, AbstractSvgArrowOverlay ]);
-
- jsPlumb.Overlays.svg.PlainArrow = function() {
- AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.PlainArrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.svg.PlainArrow, [ jsPlumb.Overlays.PlainArrow, AbstractSvgArrowOverlay ]);
-
- jsPlumb.Overlays.svg.Diamond = function() {
- AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.Diamond, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.svg.Diamond, [ jsPlumb.Overlays.Diamond, AbstractSvgArrowOverlay ]);
-
- // a test
- jsPlumb.Overlays.svg.GuideLines = function() {
- var path = null, self = this, p1_1, p1_2;
- jsPlumb.Overlays.GuideLines.apply(this, arguments);
- this.paint = function(params, containerExtents) {
- if (path == null) {
- path = _node("path");
- params.connector.svg.appendChild(path);
- self.attachListeners(path, params.connector);
- self.attachListeners(path, self);
-
- p1_1 = _node("path");
- params.connector.svg.appendChild(p1_1);
- self.attachListeners(p1_1, params.connector);
- self.attachListeners(p1_1, self);
-
- p1_2 = _node("path");
- params.connector.svg.appendChild(p1_2);
- self.attachListeners(p1_2, params.connector);
- self.attachListeners(p1_2, self);
- }
-
- var offset =[0,0];
- if (containerExtents.xmin < 0) offset[0] = -containerExtents.xmin;
- if (containerExtents.ymin < 0) offset[1] = -containerExtents.ymin;
-
- _attr(path, {
- "d" : makePath(params.head, params.tail),
- stroke : "red",
- fill : null,
- transform:"translate(" + offset[0] + "," + offset[1] + ")"
- });
-
- _attr(p1_1, {
- "d" : makePath(params.tailLine[0], params.tailLine[1]),
- stroke : "blue",
- fill : null,
- transform:"translate(" + offset[0] + "," + offset[1] + ")"
- });
-
- _attr(p1_2, {
- "d" : makePath(params.headLine[0], params.headLine[1]),
- stroke : "green",
- fill : null,
- transform:"translate(" + offset[0] + "," + offset[1] + ")"
- });
- };
-
- var makePath = function(d1, d2) {
- return "M " + d1.x + "," + d1.y +
- " L" + d2.x + "," + d2.y;
- };
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.svg.GuideLines, jsPlumb.Overlays.GuideLines);
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the VML renderers.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-
-;(function() {
-
- // http://ajaxian.com/archives/the-vml-changes-in-ie-8
- // http://www.nczonline.net/blog/2010/01/19/internet-explorer-8-document-and-browser-modes/
- // http://www.louisremi.com/2009/03/30/changes-in-vml-for-ie8-or-what-feature-can-the-ie-dev-team-break-for-you-today/
-
- var vmlAttributeMap = {
- "stroke-linejoin":"joinstyle",
- "joinstyle":"joinstyle",
- "endcap":"endcap",
- "miterlimit":"miterlimit"
- },
- jsPlumbStylesheet = null;
-
- if (document.createStyleSheet && document.namespaces) {
-
- var ruleClasses = [
- ".jsplumb_vml", "jsplumb\\:textbox", "jsplumb\\:oval", "jsplumb\\:rect",
- "jsplumb\\:stroke", "jsplumb\\:shape", "jsplumb\\:group"
- ],
- rule = "behavior:url(#default#VML);position:absolute;";
-
- jsPlumbStylesheet = document.createStyleSheet();
-
- for (var i = 0; i < ruleClasses.length; i++)
- jsPlumbStylesheet.addRule(ruleClasses[i], rule);
-
- // in this page it is also mentioned that IE requires the extra arg to the namespace
- // http://www.louisremi.com/2009/03/30/changes-in-vml-for-ie8-or-what-feature-can-the-ie-dev-team-break-for-you-today/
- // but someone commented saying they didn't need it, and it seems jsPlumb doesnt need it either.
- // var iev = document.documentMode;
- //if (!iev || iev < 8)
- document.namespaces.add("jsplumb", "urn:schemas-microsoft-com:vml");
- //else
- // document.namespaces.add("jsplumb", "urn:schemas-microsoft-com:vml", "#default#VML");
- }
-
- jsPlumb.vml = {};
-
- var scale = 1000,
-
- _groupMap = {},
- _getGroup = function(container, connectorClass) {
- var id = jsPlumb.getId(container),
- g = _groupMap[id];
- if(!g) {
- g = _node("group", [0,0,scale, scale], {"class":connectorClass});
- //g.style.position=absolute;
- //g["coordsize"] = "1000,1000";
- g.style.backgroundColor="red";
- _groupMap[id] = g;
- //jsPlumb.appendElement(g, container); // todo if this gets reinstated, remember to use the current jsplumb instance.
- //jsPlumb.CurrentLibrary.getDOMElement(container).appendChild(g);
- //document.body.appendChild(g);
- }
- return g;
- },
- _atts = function(o, atts) {
- for (var i in atts) {
- // IE8 fix: setattribute does not work after an element has been added to the dom!
- // http://www.louisremi.com/2009/03/30/changes-in-vml-for-ie8-or-what-feature-can-the-ie-dev-team-break-for-you-today/
- //o.setAttribute(i, atts[i]);
-
- /*There is an additional problem when accessing VML elements by using get/setAttribute. The simple solution is following:
-
- if (document.documentMode==8) {
- ele.opacity=1;
- } else {
- ele.setAttribute(‘opacity’,1);
- }
- */
-
- o[i] = atts[i];
- }
- },
- _node = function(name, d, atts, parent, _jsPlumb, deferToJsPlumbContainer) {
- atts = atts || {};
- var o = document.createElement("jsplumb:" + name);
- if (deferToJsPlumbContainer)
- _jsPlumb.appendElement(o, parent);
- else
- jsPlumb.CurrentLibrary.appendElement(o, parent);
- o.className = (atts["class"] ? atts["class"] + " " : "") + "jsplumb_vml";
- _pos(o, d);
- _atts(o, atts);
- return o;
- },
- _pos = function(o,d, zIndex) {
- o.style.left = d[0] + "px";
- o.style.top = d[1] + "px";
- o.style.width= d[2] + "px";
- o.style.height= d[3] + "px";
- o.style.position = "absolute";
- if (zIndex)
- o.style.zIndex = zIndex;
- },
- _conv = jsPlumb.vml.convertValue = function(v) {
- return Math.floor(v * scale);
- },
- // tests if the given style is "transparent" and then sets the appropriate opacity node to 0 if so,
- // or 1 if not. TODO in the future, support variable opacity.
- _maybeSetOpacity = function(styleToWrite, styleToCheck, type, component) {
- if ("transparent" === styleToCheck)
- component.setOpacity(type, "0.0");
- else
- component.setOpacity(type, "1.0");
- },
- _applyStyles = function(node, style, component, _jsPlumb) {
- var styleToWrite = {};
- if (style.strokeStyle) {
- styleToWrite.stroked = "true";
- var strokeColor = jsPlumbUtil.convertStyle(style.strokeStyle, true);
- styleToWrite.strokecolor = strokeColor;
- _maybeSetOpacity(styleToWrite, strokeColor, "stroke", component);
- styleToWrite.strokeweight = style.lineWidth + "px";
- }
- else styleToWrite.stroked = "false";
-
- if (style.fillStyle) {
- styleToWrite.filled = "true";
- var fillColor = jsPlumbUtil.convertStyle(style.fillStyle, true);
- styleToWrite.fillcolor = fillColor;
- _maybeSetOpacity(styleToWrite, fillColor, "fill", component);
- }
- else styleToWrite.filled = "false";
-
- if(style.dashstyle) {
- if (component.strokeNode == null) {
- component.strokeNode = _node("stroke", [0,0,0,0], { dashstyle:style.dashstyle }, node, _jsPlumb);
- }
- else
- component.strokeNode.dashstyle = style.dashstyle;
- }
- else if (style["stroke-dasharray"] && style.lineWidth) {
- var sep = style["stroke-dasharray"].indexOf(",") == -1 ? " " : ",",
- parts = style["stroke-dasharray"].split(sep),
- styleToUse = "";
- for(var i = 0; i < parts.length; i++) {
- styleToUse += (Math.floor(parts[i] / style.lineWidth) + sep);
- }
- if (component.strokeNode == null) {
- component.strokeNode = _node("stroke", [0,0,0,0], { dashstyle:styleToUse }, node, _jsPlumb);
- }
- else
- component.strokeNode.dashstyle = styleToUse;
- }
-
- _atts(node, styleToWrite);
- },
- /*
- * Base class for Vml endpoints and connectors. Extends jsPlumbUIComponent.
- */
- VmlComponent = function() {
- var self = this, renderer = {};
- jsPlumb.jsPlumbUIComponent.apply(this, arguments);
-
- this.opacityNodes = {
- "stroke":null,
- "fill":null
- };
- this.initOpacityNodes = function(vml) {
- self.opacityNodes.stroke = _node("stroke", [0,0,1,1], {opacity:"0.0"}, vml, self._jsPlumb.instance);
- self.opacityNodes.fill = _node("fill", [0,0,1,1], {opacity:"0.0"}, vml, self._jsPlumb.instance);
- };
- this.setOpacity = function(type, value) {
- var node = self.opacityNodes[type];
- if (node) node.opacity = "" + value;
- };
- var displayElements = [ ];
- this.getDisplayElements = function() {
- return displayElements;
- };
-
- this.appendDisplayElement = function(el, doNotAppendToCanvas) {
- if (!doNotAppendToCanvas) self.canvas.parentNode.appendChild(el);
- displayElements.push(el);
- };
- };
- jsPlumbUtil.extend(VmlComponent, jsPlumb.jsPlumbUIComponent, {
- cleanup:function() {
- if (this.bgCanvas) jsPlumbUtil.removeElement(this.bgCanvas);
- jsPlumbUtil.removeElement(this.canvas);
- }
- });
-
- /*
- * Base class for Vml connectors. extends VmlComponent.
- */
- var VmlConnector = jsPlumb.ConnectorRenderers.vml = function(params) {
- this.strokeNode = null;
- this.canvas = null;
- VmlComponent.apply(this, arguments);
- var clazz = this._jsPlumb.instance.connectorClass + (params.cssClass ? (" " + params.cssClass) : "");
- this.paint = function(style) {
- if (style !== null) {
-
- // we need to be at least 1 pixel in each direction, because otherwise coordsize gets set to
- // 0 and overlays cannot paint.
- this.w = Math.max(this.w, 1);
- this.h = Math.max(this.h, 1);
-
- var segments = this.getSegments(), p = { "path":"" },
- d = [this.x, this.y, this.w, this.h];
-
- // create path from segments.
- for (var i = 0; i < segments.length; i++) {
- p.path += jsPlumb.Segments.vml.SegmentRenderer.getPath(segments[i]);
- p.path += " ";
- }
-
- //*
- if (style.outlineColor) {
- var outlineWidth = style.outlineWidth || 1,
- outlineStrokeWidth = style.lineWidth + (2 * outlineWidth),
- outlineStyle = {
- strokeStyle : jsPlumbUtil.convertStyle(style.outlineColor),
- lineWidth : outlineStrokeWidth
- };
- for (var aa in vmlAttributeMap) outlineStyle[aa] = style[aa];
-
- if (this.bgCanvas == null) {
- p["class"] = clazz;
- p.coordsize = (d[2] * scale) + "," + (d[3] * scale);
- this.bgCanvas = _node("shape", d, p, params.parent, this._jsPlumb.instance, true);
- _pos(this.bgCanvas, d);
- this.appendDisplayElement(this.bgCanvas, true);
- this.attachListeners(this.bgCanvas, this);
- this.initOpacityNodes(this.bgCanvas, ["stroke"]);
- }
- else {
- p.coordsize = (d[2] * scale) + "," + (d[3] * scale);
- _pos(this.bgCanvas, d);
- _atts(this.bgCanvas, p);
- }
-
- _applyStyles(this.bgCanvas, outlineStyle, this);
- }
- //*/
-
- if (this.canvas == null) {
- p["class"] = clazz;
- p.coordsize = (d[2] * scale) + "," + (d[3] * scale);
- this.canvas = _node("shape", d, p, params.parent, this._jsPlumb.instance, true);
- //var group = _getGroup(params.parent); // test of append everything to a group
- //group.appendChild(self.canvas); // sort of works but not exactly;
- //params["_jsPlumb"].appendElement(self.canvas, params.parent); //before introduction of groups
-
- this.appendDisplayElement(this.canvas, true);
- this.attachListeners(this.canvas, this);
- this.initOpacityNodes(this.canvas, ["stroke"]);
- }
- else {
- p.coordsize = (d[2] * scale) + "," + (d[3] * scale);
- _pos(this.canvas, d);
- _atts(this.canvas, p);
- }
-
- _applyStyles(this.canvas, style, this, this._jsPlumb.instance);
- }
- };
-
- };
- jsPlumbUtil.extend(VmlConnector, VmlComponent, {
- reattachListeners : function() {
- if (this.canvas) this.reattachListenersForElement(this.canvas, this);
- }
- });
-
- /*
- *
- * Base class for Vml Endpoints. extends VmlComponent.
- *
- */
- var VmlEndpoint = window.VmlEndpoint = function(params) {
- VmlComponent.apply(this, arguments);
- this._jsPlumb.vml = null;//, opacityStrokeNode = null, opacityFillNode = null;
- this.canvas = document.createElement("div");
- this.canvas.style.position = "absolute";
- this._jsPlumb.clazz = this._jsPlumb.instance.endpointClass + (params.cssClass ? (" " + params.cssClass) : "");
-
- // TODO vml endpoint adds class to VML at constructor time. but the addClass method adds VML
- // to the enclosing DIV. what to do? seems like it would be better to just target the div.
- // HOWEVER...vml connection has no containing div. why not? it feels like it should.
-
- //var group = _getGroup(params.parent);
- //group.appendChild(self.canvas);
- params._jsPlumb.appendElement(this.canvas, params.parent);
-
- this.paint = function(style, anchor) {
- var p = { }, vml = this._jsPlumb.vml;
-
- jsPlumbUtil.sizeElement(this.canvas, this.x, this.y, this.w, this.h);
- if (this._jsPlumb.vml == null) {
- p["class"] = this._jsPlumb.clazz;
- vml = this._jsPlumb.vml = this.getVml([0,0, this.w, this.h], p, anchor, this.canvas, this._jsPlumb.instance);
- this.attachListeners(vml, this);
-
- this.appendDisplayElement(vml, true);
- this.appendDisplayElement(this.canvas, true);
-
- this.initOpacityNodes(vml, ["fill"]);
- }
- else {
- _pos(vml, [0,0, this.w, this.h]);
- _atts(vml, p);
- }
-
- _applyStyles(vml, style, this);
- };
- };
- jsPlumbUtil.extend(VmlEndpoint, VmlComponent, {
- reattachListeners : function() {
- if (this._jsPlumb.vml) this.reattachListenersForElement(this._jsPlumb.vml, this);
- }
- });
-
-// ******************************* vml segments *****************************************************
-
- jsPlumb.Segments.vml = {
- SegmentRenderer : {
- getPath : function(segment) {
- return ({
- "Straight":function(segment) {
- var d = segment.params;
- return "m" + _conv(d.x1) + "," + _conv(d.y1) + " l" + _conv(d.x2) + "," + _conv(d.y2) + " e";
- },
- "Bezier":function(segment) {
- var d = segment.params;
- return "m" + _conv(d.x1) + "," + _conv(d.y1) +
- " c" + _conv(d.cp1x) + "," + _conv(d.cp1y) + "," + _conv(d.cp2x) + "," + _conv(d.cp2y) + "," + _conv(d.x2) + "," + _conv(d.y2) + " e";
- },
- "Arc":function(segment) {
- var d = segment.params,
- xmin = Math.min(d.x1, d.x2),
- xmax = Math.max(d.x1, d.x2),
- ymin = Math.min(d.y1, d.y2),
- ymax = Math.max(d.y1, d.y2),
- sf = segment.anticlockwise ? 1 : 0,
- pathType = (segment.anticlockwise ? "at " : "wa "),
- makePosString = function() {
- var xy = [
- null,
- [ function() { return [xmin, ymin ];}, function() { return [xmin - d.r, ymin - d.r ];}],
- [ function() { return [xmin - d.r, ymin ];}, function() { return [xmin, ymin - d.r ];}],
- [ function() { return [xmin - d.r, ymin - d.r ];}, function() { return [xmin, ymin ];}],
- [ function() { return [xmin, ymin - d.r ];}, function() { return [xmin - d.r, ymin ];}]
- ][segment.segment][sf]();
-
- return _conv(xy[0]) + "," + _conv(xy[1]) + "," + _conv(xy[0] + (2*d.r)) + "," + _conv(xy[1] + (2*d.r));
- };
-
-
- return pathType + makePosString() + "," + _conv(d.x1) + "," + _conv(d.y1) + "," + _conv(d.x2) + "," + _conv(d.y2) + " e";
- }
-
- })[segment.type](segment);
- }
- }
- };
-
-// ******************************* /vml segments *****************************************************
-
-// ******************************* vml endpoints *****************************************************
-
- jsPlumb.Endpoints.vml.Dot = function() {
- jsPlumb.Endpoints.Dot.apply(this, arguments);
- VmlEndpoint.apply(this, arguments);
- this.getVml = function(d, atts, anchor, parent, _jsPlumb) { return _node("oval", d, atts, parent, _jsPlumb); };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.vml.Dot, VmlEndpoint);
-
- jsPlumb.Endpoints.vml.Rectangle = function() {
- jsPlumb.Endpoints.Rectangle.apply(this, arguments);
- VmlEndpoint.apply(this, arguments);
- this.getVml = function(d, atts, anchor, parent, _jsPlumb) { return _node("rect", d, atts, parent, _jsPlumb); };
- };
- jsPlumbUtil.extend(jsPlumb.Endpoints.vml.Rectangle, VmlEndpoint);
-
- /*
- * VML Image Endpoint is the same as the default image endpoint.
- */
- jsPlumb.Endpoints.vml.Image = jsPlumb.Endpoints.Image;
-
- /**
- * placeholder for Blank endpoint in vml renderer.
- */
- jsPlumb.Endpoints.vml.Blank = jsPlumb.Endpoints.Blank;
-
-// ******************************* /vml endpoints *****************************************************
-
-// ******************************* vml overlays *****************************************************
-
- /**
- * VML Label renderer. uses the default label renderer (which adds an element to the DOM)
- */
- jsPlumb.Overlays.vml.Label = jsPlumb.Overlays.Label;
-
- /**
- * VML Custom renderer. uses the default Custom renderer (which adds an element to the DOM)
- */
- jsPlumb.Overlays.vml.Custom = jsPlumb.Overlays.Custom;
-
- /**
- * Abstract VML arrow superclass
- */
- var AbstractVmlArrowOverlay = function(superclass, originalArgs) {
- superclass.apply(this, originalArgs);
- VmlComponent.apply(this, originalArgs);
- var self = this, path = null;
- self.canvas = null;
- self.isAppendedAtTopLevel = true;
- var getPath = function(d) {
- return "m " + _conv(d.hxy.x) + "," + _conv(d.hxy.y) +
- " l " + _conv(d.tail[0].x) + "," + _conv(d.tail[0].y) +
- " " + _conv(d.cxy.x) + "," + _conv(d.cxy.y) +
- " " + _conv(d.tail[1].x) + "," + _conv(d.tail[1].y) +
- " x e";
- };
- this.paint = function(params, containerExtents) {
- var p = {}, d = params.d, connector = params.component;
- if (params.strokeStyle) {
- p.stroked = "true";
- p.strokecolor = jsPlumbUtil.convertStyle(params.strokeStyle, true);
- }
- if (params.lineWidth) p.strokeweight = params.lineWidth + "px";
- if (params.fillStyle) {
- p.filled = "true";
- p.fillcolor = params.fillStyle;
- }
-
- var xmin = Math.min(d.hxy.x, d.tail[0].x, d.tail[1].x, d.cxy.x),
- ymin = Math.min(d.hxy.y, d.tail[0].y, d.tail[1].y, d.cxy.y),
- xmax = Math.max(d.hxy.x, d.tail[0].x, d.tail[1].x, d.cxy.x),
- ymax = Math.max(d.hxy.y, d.tail[0].y, d.tail[1].y, d.cxy.y),
- w = Math.abs(xmax - xmin),
- h = Math.abs(ymax - ymin),
- dim = [xmin, ymin, w, h];
-
- // for VML, we create overlays using shapes that have the same dimensions and
- // coordsize as their connector - overlays calculate themselves relative to the
- // connector (it's how it's been done since the original canvas implementation, because
- // for canvas that makes sense).
- p.path = getPath(d);
- p.coordsize = (connector.w * scale) + "," + (connector.h * scale);
-
- dim[0] = connector.x;
- dim[1] = connector.y;
- dim[2] = connector.w;
- dim[3] = connector.h;
-
- if (self.canvas == null) {
- var overlayClass = connector._jsPlumb.overlayClass || "";
- var clazz = originalArgs && (originalArgs.length == 1) ? (originalArgs[0].cssClass || "") : "";
- p["class"] = clazz + " " + overlayClass;
- self.canvas = _node("shape", dim, p, connector.canvas.parentNode, connector._jsPlumb.instance, true);
- connector.appendDisplayElement(self.canvas, true);
- self.attachListeners(self.canvas, connector);
- self.attachListeners(self.canvas, self);
- }
- else {
- _pos(self.canvas, dim);
- _atts(self.canvas, p);
- }
- };
-
- this.reattachListeners = function() {
- if (self.canvas) self.reattachListenersForElement(self.canvas, self);
- };
-
- this.cleanup = function() {
- if (self.canvas != null) jsPlumb.CurrentLibrary.removeElement(self.canvas);
- };
- };
- jsPlumbUtil.extend(AbstractVmlArrowOverlay, VmlComponent);
-
- jsPlumb.Overlays.vml.Arrow = function() {
- AbstractVmlArrowOverlay.apply(this, [jsPlumb.Overlays.Arrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.vml.Arrow, [ jsPlumb.Overlays.Arrow, AbstractVmlArrowOverlay ]);
-
- jsPlumb.Overlays.vml.PlainArrow = function() {
- AbstractVmlArrowOverlay.apply(this, [jsPlumb.Overlays.PlainArrow, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.vml.PlainArrow, [ jsPlumb.Overlays.PlainArrow, AbstractVmlArrowOverlay ]);
-
- jsPlumb.Overlays.vml.Diamond = function() {
- AbstractVmlArrowOverlay.apply(this, [jsPlumb.Overlays.Diamond, arguments]);
- };
- jsPlumbUtil.extend(jsPlumb.Overlays.vml.Diamond, [ jsPlumb.Overlays.Diamond, AbstractVmlArrowOverlay ]);
-
-// ******************************* /vml overlays *****************************************************
-
-})();
-/*
- * jsPlumb
- *
- * Title:jsPlumb 1.5.2
- *
- * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
- * elements, or VML.
- *
- * This file contains the jQuery adapter.
- *
- * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
- *
- * http://jsplumb.org
- * http://github.com/sporritt/jsplumb
- * http://code.google.com/p/jsplumb
- *
- * Dual licensed under the MIT and GPL2 licenses.
- */
-/*
- * the library specific functions, such as find offset, get id, get attribute, extend etc.
- * the full list is:
- *
- * addClass adds a class to the given element
- * animate calls the underlying library's animate functionality
- * appendElement appends a child element to a parent element.
- * bind binds some event to an element
- * dragEvents a dictionary of event names
- * extend extend some js object with another. probably not overly necessary; jsPlumb could just do this internally.
- * getDragObject gets the object that is being dragged, by extracting it from the arguments passed to a drag callback
- * getDragScope gets the drag scope for a given element.
- * getDropScope gets the drop scope for a given element.
- * getElementObject turns an id or dom element into an element object of the underlying library's type.
- * getOffset gets an element's offset
- * getOriginalEvent gets the original browser event from some wrapper event
- * getPageXY gets the page event's xy location.
- * getParen
@hodgestar
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment