├── LICENSE ├── README.md ├── ZeroClipboard.swf ├── bower.json └── jquery.zclip.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011, SteamDev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jquery-zclip 2 | ============ 3 | 4 | jQuery ZeroClipboard 5 | 6 | Copyright 2011, SteamDev 7 | 8 | Originally forked from: http://steamdev.com/zclip 9 | 10 | Released under the MIT license, see [LICENSE](LICENSE). 11 | 12 | ### Usage 13 | 14 | ```javascript 15 | jQuery({selector}).zclip({options}); 16 | ``` 17 | 18 | - ```selector```: any valid jquery object selector 19 | - ```options```: Object, see section below. 20 | 21 | 22 | ### Options 23 | 24 | Option | Default value | Description 25 | ------------- | ------------------------- | ------------ 26 | path | ```'ZeroClipboard.swf'``` | The path to ZeroClipboard.swf 27 | copy | ```null``` | String to copy or function that returns a string to copy 28 | afterCopy | ```null``` | Function to execute after copying 29 | beforeCopy | ```null``` | Function to execute before copying 30 | clickAfter | ```true``` | Relay a click event to the element bound to after copying 31 | setHandCursor | ```true``` | Set the cursor to pointer 32 | setCSSEffects | ```true``` | Add ```hover``` and ```active``` classes to the element bound to 33 | 34 | NOTE: Since v1.1.5, default options can be set globally by setting the value of ```ZeroclipBoard.defaults.{option}```. 35 | -------------------------------------------------------------------------------- /ZeroClipboard.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patricklodder/jquery-zclip/39e8f553a29006aa91fe1b7c5d0d7f559276e180/ZeroClipboard.swf -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-zclip", 3 | "description": "JQuery wrapper library for ZeroClipboard. Provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie.", 4 | "version": "1.1.5", 5 | "main": ["./jquery.zclip.js", "./ZeroClipboard.swf"], 6 | "keywords": ["flash","clipboard","copy","cut","paste","zclip","clip","clippy", "zeroclipboard", "jquery"], 7 | "license": "https://github.com/patricklodder/jquery-zclip/blob/master/LICENSE", 8 | "authors": [{"name":"SteamDev","url":"http://www.steamdev.com/zclip/"},{"name":"Patrick Lodder","url":"https://github.com/patricklodder"}], 9 | "homepage": "https://github.com/patricklodder/jquery-zclip", 10 | "repository": {"type":"git","url":"https://github.com/patricklodder/jquery-zclip.git"}, 11 | "location": "git://github.com/patricklodder/jquery-zclip.git" 12 | } 13 | -------------------------------------------------------------------------------- /jquery.zclip.js: -------------------------------------------------------------------------------- 1 | /* 2 | * zClip :: jQuery ZeroClipboard v1.1.5 3 | * Originally forked from: http://steamdev.com/zclip 4 | * 5 | * Copyright 2011, SteamDev 6 | * 7 | * Released under the MIT license. 8 | * https://github.com/patricklodder/jquery-zclip/blob/master/LICENSE 9 | */ 10 | 11 | (function (jQuery) { 12 | 13 | jQuery.fn.zclip = function (params) { 14 | 15 | if (typeof params == "object" && !params.length) { 16 | 17 | var settings = jQuery.extend({}, ZeroClipboard.defaults, params); 18 | 19 | return this.each(function () { 20 | 21 | var o = jQuery(this); 22 | 23 | if (o.is(':visible') && (typeof settings.copy == 'string' || jQuery.isFunction(settings.copy))) { 24 | 25 | ZeroClipboard.setMoviePath(settings.path); 26 | var clip = new ZeroClipboard.Client(); 27 | 28 | if (jQuery.isFunction(settings.copy)) { 29 | o.bind('zClip_copy', settings.copy); 30 | } 31 | 32 | if (jQuery.isFunction(settings.beforeCopy)) { 33 | o.bind('zClip_beforeCopy', settings.beforeCopy); 34 | } 35 | 36 | if (jQuery.isFunction(settings.afterCopy)) { 37 | o.bind('zClip_afterCopy', settings.afterCopy); 38 | } 39 | 40 | clip.setHandCursor(settings.setHandCursor); 41 | 42 | clip.setCSSEffects(settings.setCSSEffects); 43 | 44 | clip.addEventListener('mouseOver', function (client) { 45 | o.trigger('mouseenter'); 46 | }); 47 | 48 | clip.addEventListener('mouseOut', function (client) { 49 | o.trigger('mouseleave'); 50 | }); 51 | 52 | clip.addEventListener('mouseDown', function (client) { 53 | 54 | o.trigger('mousedown'); 55 | 56 | if (jQuery.isFunction(settings.beforeCopy)) { 57 | o.trigger('zClip_beforeCopy'); 58 | } 59 | 60 | if (!jQuery.isFunction(settings.copy)) { 61 | clip.setText(settings.copy); 62 | } else { 63 | clip.setText(o.triggerHandler('zClip_copy')); 64 | } 65 | 66 | }); 67 | 68 | clip.addEventListener('complete', function (client, text) { 69 | 70 | if (jQuery.isFunction(settings.afterCopy)) { 71 | 72 | o.trigger('zClip_afterCopy'); 73 | 74 | } else { 75 | if (text.length > 500) { 76 | text = text.substr(0, 500) + "...\n\n(" + (text.length - 500) + " characters not shown)"; 77 | } 78 | 79 | o.removeClass('hover'); 80 | alert("Copied text to clipboard:\n\n " + text); 81 | } 82 | 83 | if (settings.clickAfter) { 84 | o.trigger('click'); 85 | } 86 | 87 | }); 88 | 89 | clip.glue(o[0], o.parent()[0]); 90 | 91 | jQuery(window).bind('load resize', function () {clip.reposition();}); 92 | 93 | } 94 | 95 | }); 96 | 97 | } else if (typeof params == "string") { 98 | 99 | return this.each(function () { 100 | 101 | var o = jQuery(this); 102 | 103 | params = params.toLowerCase(); 104 | var zclipId = o.data('zclipId'); 105 | var clipElm = jQuery('#' + zclipId + '.zclip'); 106 | var clientId = clipElm.attr('id').replace(/^.*_/g, '') || null; 107 | 108 | if (params == "remove") { 109 | 110 | clipElm.remove(); 111 | o.removeClass('active hover'); 112 | o.unbind('zClip_copy'); 113 | o.unbind('zClip_beforeCopy'); 114 | o.unbind('zClip_afterCopy'); 115 | ZeroClipboard.unregister(clientId); 116 | 117 | } else if (params == "hide") { 118 | 119 | clipElm.hide(); 120 | o.removeClass('active hover'); 121 | 122 | } else if (params == "show") { 123 | 124 | clipElm.show(); 125 | 126 | } 127 | 128 | }); 129 | 130 | } 131 | 132 | }; 133 | 134 | })(jQuery); 135 | 136 | // ZeroClipboard 137 | // Simple Set Clipboard System 138 | // Author: Joseph Huckaby 139 | var ZeroClipboard = { 140 | 141 | version: "1.0.7", 142 | clients: {}, 143 | // registered upload clients on page, indexed by id 144 | moviePath: 'ZeroClipboard.swf', 145 | // URL to movie 146 | nextId: 1, 147 | // ID of next movie 148 | 149 | defaults: { 150 | path: 'ZeroClipboard.swf', 151 | clickAfter: true, 152 | setHandCursor: true, 153 | setCSSEffects: true, 154 | 155 | copy: null, 156 | // a string or function that returns string 157 | 158 | beforeCopy: null, 159 | afterCopy: null 160 | }, 161 | 162 | jQuery: function (thingy) { 163 | // simple DOM lookup utility function 164 | if (typeof(thingy) == 'string') thingy = document.getElementById(thingy); 165 | if (!thingy.addClass) { 166 | // extend element with a few useful methods 167 | thingy.hide = function () { 168 | this.style.display = 'none'; 169 | }; 170 | thingy.show = function () { 171 | this.style.display = ''; 172 | }; 173 | thingy.addClass = function (name) { 174 | this.removeClass(name); 175 | this.className += ' ' + name; 176 | }; 177 | thingy.removeClass = function (name) { 178 | var classes = this.className.split(/\s+/); 179 | var idx = -1; 180 | for (var k = 0; k < classes.length; k++) { 181 | if (classes[k] == name) { 182 | idx = k; 183 | k = classes.length; 184 | } 185 | } 186 | if (idx > -1) { 187 | classes.splice(idx, 1); 188 | this.className = classes.join(' '); 189 | } 190 | return this; 191 | }; 192 | thingy.hasClass = function (name) { 193 | return !!this.className.match(new RegExp("\\s*" + name + "\\s*")); 194 | }; 195 | } 196 | return thingy; 197 | }, 198 | 199 | setMoviePath: function (path) { 200 | // set path to ZeroClipboard.swf 201 | this.moviePath = path; 202 | }, 203 | 204 | dispatch: function (id, eventName, args) { 205 | // receive event from flash movie, send to client 206 | var client = this.clients[id]; 207 | if (client) { 208 | client.receiveEvent(eventName, args); 209 | } 210 | }, 211 | 212 | register: function (id, client) { 213 | // register new client to receive events 214 | this.clients[id] = client; 215 | }, 216 | 217 | unregister: function (id) { 218 | if (typeof(id) === 'number' && this.clients.hasOwnProperty(id)) { 219 | delete this.clients[id]; 220 | } 221 | }, 222 | 223 | getDOMObjectPosition: function (obj, stopObj) { 224 | // get absolute coordinates for dom element 225 | var info = { 226 | left: 0, 227 | top: 0, 228 | width: obj.width ? obj.width : obj.offsetWidth, 229 | height: obj.height ? obj.height : obj.offsetHeight 230 | }; 231 | 232 | if (obj && (obj != stopObj)) { 233 | info.left += obj.offsetLeft; 234 | info.top += obj.offsetTop; 235 | } 236 | 237 | return info; 238 | }, 239 | 240 | Client: function (elem) { 241 | // constructor for new simple upload client 242 | this.handlers = {}; 243 | 244 | // unique ID 245 | this.id = ZeroClipboard.nextId++; 246 | this.movieId = 'ZeroClipboardMovie_' + this.id; 247 | 248 | // register client with singleton to receive flash events 249 | ZeroClipboard.register(this.id, this); 250 | 251 | // create movie 252 | if (elem) this.glue(elem); 253 | } 254 | }; 255 | 256 | ZeroClipboard.Client.prototype = { 257 | 258 | id: 0, 259 | // unique ID for us 260 | ready: false, 261 | // whether movie is ready to receive events or not 262 | movie: null, 263 | // reference to movie object 264 | clipText: '', 265 | // text to copy to clipboard 266 | handCursorEnabled: true, 267 | // whether to show hand cursor, or default pointer cursor 268 | cssEffects: true, 269 | // enable CSS mouse effects on dom container 270 | handlers: null, 271 | // user event handlers 272 | glue: function (elem, appendElem, stylesToAdd) { 273 | // glue to DOM element 274 | // elem can be ID or actual DOM element object 275 | this.domElement = ZeroClipboard.jQuery(elem); 276 | 277 | // float just above object, or zIndex 99 if dom element isn't set 278 | var zIndex = 99; 279 | if (this.domElement.style.zIndex) { 280 | zIndex = parseInt(this.domElement.style.zIndex, 10) + 1; 281 | } 282 | 283 | if (typeof(appendElem) == 'string') { 284 | appendElem = ZeroClipboard.jQuery(appendElem); 285 | } else if (typeof(appendElem) == 'undefined') { 286 | appendElem = document.getElementsByTagName('body')[0]; 287 | } 288 | 289 | // find X/Y position of domElement 290 | var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem); 291 | 292 | // create floating DIV above element 293 | this.div = document.createElement('div'); 294 | this.div.className = "zclip"; 295 | this.div.id = "zclip-" + this.movieId; 296 | jQuery(this.domElement).data('zclipId', 'zclip-' + this.movieId); 297 | var style = this.div.style; 298 | style.position = 'absolute'; 299 | style.left = '' + box.left + 'px'; 300 | style.top = '' + box.top + 'px'; 301 | style.width = '' + box.width + 'px'; 302 | style.height = '' + box.height + 'px'; 303 | style.zIndex = zIndex; 304 | 305 | if (typeof(stylesToAdd) == 'object') { 306 | for (var addedStyle in stylesToAdd) { 307 | style[addedStyle] = stylesToAdd[addedStyle]; 308 | } 309 | } 310 | 311 | // style.backgroundColor = '#f00'; // debug 312 | appendElem.appendChild(this.div); 313 | 314 | this.div.innerHTML = this.getHTML(box.width, box.height); 315 | }, 316 | 317 | getHTML: function (width, height) { 318 | // return HTML for movie 319 | var html = ''; 320 | var flashvars = 'id=' + this.id + '&width=' + width + '&height=' + height; 321 | 322 | if (navigator.userAgent.match(/MSIE/)) { 323 | // IE gets an OBJECT tag 324 | var protocol = location.href.match(/^https/i) ? 'https://' : 'http://'; 325 | html += ''; 326 | } else { 327 | // all other browsers get an EMBED tag 328 | html += ''; 329 | } 330 | return html; 331 | }, 332 | 333 | hide: function () { 334 | // temporarily hide floater offscreen 335 | if (this.div) { 336 | this.div.style.left = '-2000px'; 337 | } 338 | }, 339 | 340 | show: function () { 341 | // show ourselves after a call to hide() 342 | this.reposition(); 343 | }, 344 | 345 | destroy: function () { 346 | // destroy control and floater 347 | if (this.domElement && this.div) { 348 | this.hide(); 349 | this.div.innerHTML = ''; 350 | 351 | var body = document.getElementsByTagName('body')[0]; 352 | try { 353 | body.removeChild(this.div); 354 | } catch (e) { 355 | //do nothing 356 | } 357 | 358 | this.domElement = null; 359 | this.div = null; 360 | } 361 | }, 362 | 363 | reposition: function (elem) { 364 | // reposition our floating div, optionally to new container 365 | // warning: container CANNOT change size, only position 366 | if (elem) { 367 | this.domElement = ZeroClipboard.jQuery(elem); 368 | if (!this.domElement) this.hide(); 369 | } 370 | 371 | if (this.domElement && this.div) { 372 | var box = ZeroClipboard.getDOMObjectPosition(this.domElement); 373 | var style = this.div.style; 374 | style.left = '' + box.left + 'px'; 375 | style.top = '' + box.top + 'px'; 376 | } 377 | }, 378 | 379 | setText: function (newText) { 380 | // set text to be copied to clipboard 381 | this.clipText = newText; 382 | if (this.ready) { 383 | this.movie.setText(newText); 384 | } 385 | }, 386 | 387 | addEventListener: function (eventName, func) { 388 | // add user event listener for event 389 | // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel 390 | eventName = eventName.toString().toLowerCase().replace(/^on/, ''); 391 | if (!this.handlers[eventName]) { 392 | this.handlers[eventName] = []; 393 | } 394 | this.handlers[eventName].push(func); 395 | }, 396 | 397 | setHandCursor: function (enabled) { 398 | // enable hand cursor (true), or default arrow cursor (false) 399 | this.handCursorEnabled = enabled; 400 | if (this.ready) { 401 | this.movie.setHandCursor(enabled); 402 | } 403 | }, 404 | 405 | setCSSEffects: function (enabled) { 406 | // enable or disable CSS effects on DOM container 407 | this.cssEffects = !! enabled; 408 | }, 409 | 410 | receiveEvent: function (eventName, args) { 411 | // receive event from flash 412 | eventName = eventName.toString().toLowerCase().replace(/^on/, ''); 413 | 414 | // special behavior for certain events 415 | switch (eventName) { 416 | case 'load': 417 | // movie claims it is ready, but in IE this isn't always the case... 418 | // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function 419 | this.movie = document.getElementById(this.movieId); 420 | var self = this; 421 | 422 | if (!this.movie) { 423 | setTimeout(function () { 424 | self.receiveEvent('load', null); 425 | }, 1); 426 | return; 427 | } 428 | 429 | // firefox on pc needs a "kick" in order to set these in certain cases 430 | if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) { 431 | setTimeout(function () { 432 | self.receiveEvent('load', null); 433 | }, 100); 434 | this.ready = true; 435 | return; 436 | } 437 | 438 | this.ready = true; 439 | try { 440 | this.movie.setText(this.clipText); 441 | } catch (e) {} 442 | try { 443 | this.movie.setHandCursor(this.handCursorEnabled); 444 | } catch (e) {} 445 | break; 446 | 447 | case 'mouseover': 448 | if (this.domElement && this.cssEffects) { 449 | this.domElement.addClass('hover'); 450 | if (this.recoverActive) { 451 | this.domElement.addClass('active'); 452 | } 453 | 454 | } 455 | break; 456 | 457 | case 'mouseout': 458 | if (this.domElement && this.cssEffects) { 459 | this.recoverActive = false; 460 | if (this.domElement.hasClass('active')) { 461 | this.domElement.removeClass('active'); 462 | this.recoverActive = true; 463 | } 464 | this.domElement.removeClass('hover'); 465 | 466 | } 467 | break; 468 | 469 | case 'mousedown': 470 | if (this.domElement && this.cssEffects) { 471 | this.domElement.addClass('active'); 472 | } 473 | break; 474 | 475 | case 'mouseup': 476 | if (this.domElement && this.cssEffects) { 477 | this.domElement.removeClass('active'); 478 | this.recoverActive = false; 479 | } 480 | break; 481 | } // switch eventName 482 | if (this.handlers[eventName]) { 483 | for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) { 484 | var func = this.handlers[eventName][idx]; 485 | 486 | if (jQuery.isFunction(func)) { 487 | // actual function reference 488 | func(this, args); 489 | } else if ((typeof(func) == 'object') && (func.length == 2)) { 490 | // PHP style object + method, i.e. [myObject, 'myMethod'] 491 | func[0][func[1]](this, args); 492 | } else if (typeof(func) == 'string') { 493 | // name of function 494 | window[func](this, args); 495 | } 496 | } // foreach event handler defined 497 | } // user defined handler for event 498 | } 499 | 500 | }; 501 | --------------------------------------------------------------------------------