text: /**
   * The real constructor for `ZeroClipboard` client instances.
   * @private
   */
  var _clientConstructor = function(elements) {
    // Save a closure reference for the following event handlers
    var client = this;
  
    // Assign an ID to the client instance
    client.id = "" + (_clientIdCounter++);
  
    // Create the meta information store for this client
    _clientMeta[client.id] = {
      instance: client,
      elements: [],
      handlers: {}
    };
  
    // If the elements argument exists, clip it
    if (elements) {
      client.clip(elements);
    }
  
    // ECHO! Our client's sounding board.
    ZeroClipboard.on("*", function(event) {
      return client.emit(event);
    });
  
    // Await imminent destruction...
    ZeroClipboard.on("destroy", function() {
      client.destroy();
    });
  
    // Move on: embed the SWF
    ZeroClipboard.create();
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.on`.
   * @private
   */
  var _clientOn = function(eventType, listener) {
    // add user event handler for event
    var i, len, events,
        added = {},
        handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
  
    if (typeof eventType === "string" && eventType) {
      events = eventType.toLowerCase().split(/\s+/);
    }
    else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
      for (i in eventType) {
        if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
          this.on(i, eventType[i]);
        }
      }
    }
  
    if (events && events.length) {
      for (i = 0, len = events.length; i < len; i++) {
        eventType = events[i].replace(/^on/, "");
        added[eventType] = true;
        if (!handlers[eventType]) {
          handlers[eventType] = [];
        }
        handlers[eventType].push(listener);
      }
  
      // The following events must be memorized and fired immediately if relevant as they only occur
      // once per Flash object load.
  
      // If the SWF was already loaded, we're à gogo!
      if (added.ready && _flashState.ready) {
        this.emit({
          type: "ready",
          client: this
        });
      }
      if (added.error) {
        var errorTypes = ["disabled", "outdated", "unavailable", "deactivated", "overdue"];
        for (i = 0, len = errorTypes.length; i < len; i++) {
          if (_flashState[errorTypes[i]]) {
            this.emit({
              type: "error",
              name: "flash-" + errorTypes[i],
              client: this
            });
            break;
          }
        }
      }
    }
    return this;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.off`.
   * @private
   */
  var _clientOff = function(eventType, listener) {
    var i, len, foundIndex, events, perEventHandlers,
        handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
    if (arguments.length === 0) {
      // Remove ALL of the handlers for ALL event types
      events = _keys(handlers);
    }
    else if (typeof eventType === "string" && eventType) {
      events = eventType.split(/\s+/);
    }
    else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
      for (i in eventType) {
        if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
          this.off(i, eventType[i]);
        }
      }
    }
  
    if (events && events.length) {
      for (i = 0, len = events.length; i < len; i++) {
        eventType = events[i].toLowerCase().replace(/^on/, "");
        perEventHandlers = handlers[eventType];
        if (perEventHandlers && perEventHandlers.length) {
          if (listener) {
            foundIndex = perEventHandlers.indexOf(listener);
            while (foundIndex !== -1) {
              perEventHandlers.splice(foundIndex, 1);
              foundIndex = perEventHandlers.indexOf(listener, foundIndex);
            }
          }
          else {
            // If no `listener` was provided, remove ALL of the handlers for this event type
            perEventHandlers.length = 0;
          }
        }
      }
    }
    return this;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.
   * @private
   */
  var _clientListeners = function(eventType) {
    var copy = null,
        handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
  
    if (handlers) {
      if (typeof eventType === "string" && eventType) {
        copy = handlers[eventType] ? handlers[eventType].slice(0) : [];
      }
      else {
        // Make a deep copy of the handlers object
        copy = _deepCopy(handlers);
      }
    }
    return copy;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.
   * @private
   */
  var _clientEmit = function(event) {
    if (_clientShouldEmit.call(this, event)) {
      // Don't modify the original Event, if it is an object (as expected)
      if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
        event = _extend({}, event);
      }
      var eventCopy = _extend({}, _createEvent(event), { client: this });
      _clientDispatchCallbacks.call(this, eventCopy);
    }
    return this;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.
   * @private
   */
  var _clientClip = function(elements) {
    elements = _prepClip(elements);
  
    for (var i = 0; i < elements.length ; i++) {
      if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
        // If the element hasn't been clipped to ANY client yet, add a metadata ID and event handler
        if (!elements[i].zcClippingId) {
          elements[i].zcClippingId = "zcClippingId_" + (_elementIdCounter++);
          _elementMeta[elements[i].zcClippingId] = [this.id];
          if (_globalConfig.autoActivate === true) {
            _addMouseHandlers(elements[i]);
          }
        }
        else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {
          _elementMeta[elements[i].zcClippingId].push(this.id);
        }
  
        // If the element hasn't been clipped to THIS client yet, add it
        var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;
        if (clippedElements.indexOf(elements[i]) === -1) {
          clippedElements.push(elements[i]);
        }
      }
    }
    return this;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.
   * @private
   */
  var _clientUnclip = function(elements) {
    var meta = _clientMeta[this.id];
  
    if (!meta) {
      return this;
    }
  
    var clippedElements = meta.elements;
    var arrayIndex;
  
    // If no elements were provided, unclip ALL of this client's clipped elements
    if (typeof elements === "undefined") {
      elements = clippedElements.slice(0);
    }
    else {
      elements = _prepClip(elements);
    }
  
    for (var i = elements.length; i--; ) {
      if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
        // If the element was clipped to THIS client yet, remove it
        arrayIndex = 0;
        while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {
          clippedElements.splice(arrayIndex, 1);
        }
  
        // If the element isn't clipped to ANY other client, remove its metadata ID and event handler
        var clientIds = _elementMeta[elements[i].zcClippingId];
        if (clientIds) {
          arrayIndex = 0;
          while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {
            clientIds.splice(arrayIndex, 1);
          }
          if (clientIds.length === 0) {
            if (_globalConfig.autoActivate === true) {
              _removeMouseHandlers(elements[i]);
            }
            delete elements[i].zcClippingId;
          }
        }
      }
    }
    return this;
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.
   * @private
   */
  var _clientElements = function() {
    var meta = _clientMeta[this.id];
    return (meta && meta.elements) ? meta.elements.slice(0) : [];
  };
  
  
  /**
   * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.
   * @private
   */
  var _clientDestroy = function() {
    // Unclip all the elements
    this.unclip();
  
    // Remove all event handlers
    this.off();
  
    // Delete the client's metadata store
    delete _clientMeta[this.id];
  };
  
  
  
  
  //
  // Helper functions
  //
  
  /**
   * Inspect an Event to see if the Client (`this`) should honor it for emission.
   * @private
   */
  var _clientShouldEmit = function(event) {
    // If no event is received
    if (!(event && event.type)) {
      return false;
    }
  
    // If this event's `client` was specifically set to a client other than this client, bail out
    if (event.client && event.client !== this) {
      return false;
    }
  
    // If this event's targeted element(s) is/are not clipped by this client, bail out
    // unless the event's `client` was specifically set to this client.
    var clippedEls = _clientMeta[this.id] && _clientMeta[this.id].elements;
    var hasClippedEls = !!clippedEls && clippedEls.length > 0;
    var goodTarget = !event.target || (hasClippedEls && clippedEls.indexOf(event.target) !== -1);
    var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;
    var goodClient = event.client && event.client === this;
    // At least one of these must be true....
    if (!(goodTarget || goodRelTarget || goodClient)) {
      return false;
    }
  
    // Otherwise... go for it!
    return true;
  };
  
  
  /**
   * Handle the actual dispatching of events to a client instance.
   *
   * @returns `this`
   * @private
   */
  var _clientDispatchCallbacks = function(event) {
    if (!(typeof event === "object" && event && event.type)) {
      return;
    }
  
    var async = _shouldPerformAsync(event);
  
    // User defined handlers for events
    var wildcardTypeHandlers = (_clientMeta[this.id] && _clientMeta[this.id].handlers["*"]) || [];
    var specificTypeHandlers = (_clientMeta[this.id] && _clientMeta[this.id].handlers[event.type]) || [];
    // Execute wildcard handlers before type-specific handlers
    var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
  
    if (handlers && handlers.length) {
      var i, len, func, context, eventCopy,
          originalContext = this;
      for (i = 0, len = handlers.length; i < len; i++) {
        func = handlers[i];
        context = originalContext;
  
        // If the user provided a string for their callback, grab that function
        if (typeof func === "string" && typeof _window[func] === "function") {
          func = _window[func];
        }
        if (typeof func === "object" && func && typeof func.handleEvent === "function") {
          context = func;
          func = func.handleEvent;
        }
  
        if (typeof func === "function") {
          eventCopy = _extend({}, event);
          _dispatchCallback(func, context, [eventCopy], async);
        }
      }
    }
    return this;
  };
  
  
  /**
   * Prepares the elements for clipping/unclipping.
   *
   * @returns An Array of elements.
   * @private
   */
  var _prepClip = function(elements) {
    // if elements is a string, ignore it
    if (typeof elements === "string") {
      elements = [];
    }
    // if the elements isn't an array, wrap it with one
    return typeof elements.length !== "number" ? [elements] : elements;
  };
  
  
  /**
   * Add a `mouseover` handler function for a clipped element.
   *
   * @returns `undefined`
   * @private
   */
  var _addMouseHandlers = function(element) {
    if (!(element && element.nodeType === 1)) {
      return;
    }
  
    // Create a `mouseout` handler function
    var _suppressMouseEvents = function(event) {
      if (!(event || (event = _window.event))) {
        return;
      }
  
      // Don't allow this event to be handled by consumers unless it originated from ZeroClipboard
      if (event._source !== "js") {
        event.stopImmediatePropagation();
        event.preventDefault();
      }
      delete event._source;
    };
  
    // Create a `mouseover` handler function
    var _elementMouseOver = function(event) {
      if (!(event || (event = _window.event))) {
        return;
      }
  
      // Don't allow this event to be handled by consumers unless it originated from ZeroClipboard
      _suppressMouseEvents(event);
  
      // Set this as the new currently active element
      ZeroClipboard.focus(element);
    };
  
    // Add the `mouseover` handler function
    element.addEventListener("mouseover", _elementMouseOver, false);
  
    // Add other mouse event handler functions
    element.addEventListener("mouseout", _suppressMouseEvents, false);
    element.addEventListener("mouseenter", _suppressMouseEvents, false);
    element.addEventListener("mouseleave", _suppressMouseEvents, false);
    element.addEventListener("mousemove", _suppressMouseEvents, false);
  
    // Save these function references to a global variable
    _mouseHandlers[element.zcClippingId] = {
      mouseover: _elementMouseOver,
      mouseout: _suppressMouseEvents,
      mouseenter: _suppressMouseEvents,
      mouseleave: _suppressMouseEvents,
      mousemove: _suppressMouseEvents
    };
  };
  
  
  /**
   * Remove a `mouseover` handler function for a clipped element.
   *
   * @returns `undefined`
   * @private
   */
  var _removeMouseHandlers = function(element) {
    if (!(element && element.nodeType === 1)) {
      return;
    }
  
    // Retrieve these function references from a global variable
    var mouseHandlers = _mouseHandlers[element.zcClippingId];
    if (!(typeof mouseHandlers === "object" && mouseHandlers)) {
      return;
    }
  
    // Remove the mouse event handlers
    var key, val,
        mouseEvents = ["move", "leave", "enter", "out", "over"];
    for (var i = 0, len = mouseEvents.length; i < len; i++) {
      key = "mouse" + mouseEvents[i];
      val = mouseHandlers[key];
      if (typeof val === "function") {
        element.removeEventListener(key, val, false);
      }
    }
  
    // Delete these function references from a global variable
    delete _mouseHandlers[element.zcClippingId];
  };
  