Chris@16: // Simple Set Clipboard System Chris@16: // Author: Joseph Huckaby Chris@16: Chris@16: var ZeroClipboard = { Chris@16: Chris@16: version: "1.0.7", Chris@16: clients: {}, // registered upload clients on page, indexed by id Chris@16: moviePath: 'ZeroClipboard.swf', // URL to movie Chris@16: nextId: 1, // ID of next movie Chris@16: Chris@16: $: function(thingy) { Chris@16: // simple DOM lookup utility function Chris@16: if (typeof(thingy) == 'string') thingy = document.getElementById(thingy); Chris@16: if (!thingy.addClass) { Chris@16: // extend element with a few useful methods Chris@16: thingy.hide = function() { this.style.display = 'none'; }; Chris@16: thingy.show = function() { this.style.display = ''; }; Chris@16: thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; }; Chris@16: thingy.removeClass = function(name) { Chris@16: var classes = this.className.split(/\s+/); Chris@16: var idx = -1; Chris@16: for (var k = 0; k < classes.length; k++) { Chris@16: if (classes[k] == name) { idx = k; k = classes.length; } Chris@16: } Chris@16: if (idx > -1) { Chris@16: classes.splice( idx, 1 ); Chris@16: this.className = classes.join(' '); Chris@16: } Chris@16: return this; Chris@16: }; Chris@16: thingy.hasClass = function(name) { Chris@16: return !!this.className.match( new RegExp("\\s*" + name + "\\s*") ); Chris@16: }; Chris@16: } Chris@16: return thingy; Chris@16: }, Chris@16: Chris@16: setMoviePath: function(path) { Chris@16: // set path to ZeroClipboard.swf Chris@16: this.moviePath = path; Chris@16: }, Chris@16: Chris@16: dispatch: function(id, eventName, args) { Chris@16: // receive event from flash movie, send to client Chris@16: var client = this.clients[id]; Chris@16: if (client) { Chris@16: client.receiveEvent(eventName, args); Chris@16: } Chris@16: }, Chris@16: Chris@16: register: function(id, client) { Chris@16: // register new client to receive events Chris@16: this.clients[id] = client; Chris@16: }, Chris@16: Chris@16: getDOMObjectPosition: function(obj, stopObj) { Chris@16: // get absolute coordinates for dom element Chris@16: var info = { Chris@16: left: 0, Chris@16: top: 0, Chris@16: width: obj.width ? obj.width : obj.offsetWidth, Chris@16: height: obj.height ? obj.height : obj.offsetHeight Chris@16: }; Chris@16: Chris@16: while (obj && (obj != stopObj)) { Chris@16: info.left += obj.offsetLeft; Chris@16: info.top += obj.offsetTop; Chris@16: obj = obj.offsetParent; Chris@16: } Chris@16: Chris@16: return info; Chris@16: }, Chris@16: Chris@16: Client: function(elem) { Chris@16: // constructor for new simple upload client Chris@16: this.handlers = {}; Chris@16: Chris@16: // unique ID Chris@16: this.id = ZeroClipboard.nextId++; Chris@16: this.movieId = 'ZeroClipboardMovie_' + this.id; Chris@16: Chris@16: // register client with singleton to receive flash events Chris@16: ZeroClipboard.register(this.id, this); Chris@16: Chris@16: // create movie Chris@16: if (elem) this.glue(elem); Chris@16: } Chris@16: }; Chris@16: Chris@16: ZeroClipboard.Client.prototype = { Chris@16: Chris@16: id: 0, // unique ID for us Chris@16: ready: false, // whether movie is ready to receive events or not Chris@16: movie: null, // reference to movie object Chris@16: clipText: '', // text to copy to clipboard Chris@16: handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor Chris@16: cssEffects: true, // enable CSS mouse effects on dom container Chris@16: handlers: null, // user event handlers Chris@16: Chris@16: glue: function(elem, appendElem, stylesToAdd) { Chris@16: // glue to DOM element Chris@16: // elem can be ID or actual DOM element object Chris@16: this.domElement = ZeroClipboard.$(elem); Chris@16: Chris@16: // float just above object, or zIndex 99 if dom element isn't set Chris@16: var zIndex = 99; Chris@16: if (this.domElement.style.zIndex) { Chris@16: zIndex = parseInt(this.domElement.style.zIndex, 10) + 1; Chris@16: } Chris@16: Chris@16: if (typeof(appendElem) == 'string') { Chris@16: appendElem = ZeroClipboard.$(appendElem); Chris@16: } Chris@16: else if (typeof(appendElem) == 'undefined') { Chris@16: appendElem = document.getElementsByTagName('body')[0]; Chris@16: } Chris@16: Chris@16: // find X/Y position of domElement Chris@16: var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem); Chris@16: Chris@16: // create floating DIV above element Chris@16: this.div = document.createElement('div'); Chris@16: var style = this.div.style; Chris@16: style.position = 'absolute'; Chris@16: style.left = '' + box.left + 'px'; Chris@16: style.top = '' + box.top + 'px'; Chris@16: style.width = '' + box.width + 'px'; Chris@16: style.height = '' + box.height + 'px'; Chris@16: style.zIndex = zIndex; Chris@16: Chris@16: if (typeof(stylesToAdd) == 'object') { Chris@16: for (addedStyle in stylesToAdd) { Chris@16: style[addedStyle] = stylesToAdd[addedStyle]; Chris@16: } Chris@16: } Chris@16: Chris@16: // style.backgroundColor = '#f00'; // debug Chris@16: Chris@16: appendElem.appendChild(this.div); Chris@16: Chris@16: this.div.innerHTML = this.getHTML( box.width, box.height ); Chris@16: }, Chris@16: Chris@16: getHTML: function(width, height) { Chris@16: // return HTML for movie Chris@16: var html = ''; Chris@16: var flashvars = 'id=' + this.id + Chris@16: '&width=' + width + Chris@16: '&height=' + height; Chris@16: Chris@16: if (navigator.userAgent.match(/MSIE/)) { Chris@16: // IE gets an OBJECT tag Chris@16: var protocol = location.href.match(/^https/i) ? 'https://' : 'http://'; Chris@16: html += ''; Chris@16: } Chris@16: else { Chris@16: // all other browsers get an EMBED tag Chris@16: html += ''; Chris@16: } Chris@16: return html; Chris@16: }, Chris@16: Chris@16: hide: function() { Chris@16: // temporarily hide floater offscreen Chris@16: if (this.div) { Chris@16: this.div.style.left = '-2000px'; Chris@16: } Chris@16: }, Chris@16: Chris@16: show: function() { Chris@16: // show ourselves after a call to hide() Chris@16: this.reposition(); Chris@16: }, Chris@16: Chris@16: destroy: function() { Chris@16: // destroy control and floater Chris@16: if (this.domElement && this.div) { Chris@16: this.hide(); Chris@16: this.div.innerHTML = ''; Chris@16: Chris@16: var body = document.getElementsByTagName('body')[0]; Chris@16: try { body.removeChild( this.div ); } catch(e) {;} Chris@16: Chris@16: this.domElement = null; Chris@16: this.div = null; Chris@16: } Chris@16: }, Chris@16: Chris@16: reposition: function(elem) { Chris@16: // reposition our floating div, optionally to new container Chris@16: // warning: container CANNOT change size, only position Chris@16: if (elem) { Chris@16: this.domElement = ZeroClipboard.$(elem); Chris@16: if (!this.domElement) this.hide(); Chris@16: } Chris@16: Chris@16: if (this.domElement && this.div) { Chris@16: var box = ZeroClipboard.getDOMObjectPosition(this.domElement); Chris@16: var style = this.div.style; Chris@16: style.left = '' + box.left + 'px'; Chris@16: style.top = '' + box.top + 'px'; Chris@16: } Chris@16: }, Chris@16: Chris@16: setText: function(newText) { Chris@16: // set text to be copied to clipboard Chris@16: this.clipText = newText; Chris@16: if (this.ready) this.movie.setText(newText); Chris@16: }, Chris@16: Chris@16: addEventListener: function(eventName, func) { Chris@16: // add user event listener for event Chris@16: // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel Chris@16: eventName = eventName.toString().toLowerCase().replace(/^on/, ''); Chris@16: if (!this.handlers[eventName]) this.handlers[eventName] = []; Chris@16: this.handlers[eventName].push(func); Chris@16: }, Chris@16: Chris@16: setHandCursor: function(enabled) { Chris@16: // enable hand cursor (true), or default arrow cursor (false) Chris@16: this.handCursorEnabled = enabled; Chris@16: if (this.ready) this.movie.setHandCursor(enabled); Chris@16: }, Chris@16: Chris@16: setCSSEffects: function(enabled) { Chris@16: // enable or disable CSS effects on DOM container Chris@16: this.cssEffects = !!enabled; Chris@16: }, Chris@16: Chris@16: receiveEvent: function(eventName, args) { Chris@16: // receive event from flash Chris@16: eventName = eventName.toString().toLowerCase().replace(/^on/, ''); Chris@16: Chris@16: // special behavior for certain events Chris@16: switch (eventName) { Chris@16: case 'load': Chris@16: // movie claims it is ready, but in IE this isn't always the case... Chris@16: // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function Chris@16: this.movie = document.getElementById(this.movieId); Chris@16: if (!this.movie) { Chris@16: var self = this; Chris@16: setTimeout( function() { self.receiveEvent('load', null); }, 1 ); Chris@16: return; Chris@16: } Chris@16: Chris@16: // firefox on pc needs a "kick" in order to set these in certain cases Chris@16: if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) { Chris@16: var self = this; Chris@16: setTimeout( function() { self.receiveEvent('load', null); }, 100 ); Chris@16: this.ready = true; Chris@16: return; Chris@16: } Chris@16: Chris@16: this.ready = true; Chris@16: this.movie.setText( this.clipText ); Chris@16: this.movie.setHandCursor( this.handCursorEnabled ); Chris@16: break; Chris@16: Chris@16: case 'mouseover': Chris@16: if (this.domElement && this.cssEffects) { Chris@16: this.domElement.addClass('hover'); Chris@16: if (this.recoverActive) this.domElement.addClass('active'); Chris@16: } Chris@16: break; Chris@16: Chris@16: case 'mouseout': Chris@16: if (this.domElement && this.cssEffects) { Chris@16: this.recoverActive = false; Chris@16: if (this.domElement.hasClass('active')) { Chris@16: this.domElement.removeClass('active'); Chris@16: this.recoverActive = true; Chris@16: } Chris@16: this.domElement.removeClass('hover'); Chris@16: } Chris@16: break; Chris@16: Chris@16: case 'mousedown': Chris@16: if (this.domElement && this.cssEffects) { Chris@16: this.domElement.addClass('active'); Chris@16: } Chris@16: break; Chris@16: Chris@16: case 'mouseup': Chris@16: if (this.domElement && this.cssEffects) { Chris@16: this.domElement.removeClass('active'); Chris@16: this.recoverActive = false; Chris@16: } Chris@16: break; Chris@16: } // switch eventName Chris@16: Chris@16: if (this.handlers[eventName]) { Chris@16: for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) { Chris@16: var func = this.handlers[eventName][idx]; Chris@16: Chris@16: if (typeof(func) == 'function') { Chris@16: // actual function reference Chris@16: func(this, args); Chris@16: } Chris@16: else if ((typeof(func) == 'object') && (func.length == 2)) { Chris@16: // PHP style object + method, i.e. [myObject, 'myMethod'] Chris@16: func[0][ func[1] ](this, args); Chris@16: } Chris@16: else if (typeof(func) == 'string') { Chris@16: // name of function Chris@16: window[func](this, args); Chris@16: } Chris@16: } // foreach event handler defined Chris@16: } // user defined handler for event Chris@16: } Chris@16: Chris@16: };