ZeroClipboard.min.js 84 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610
  1. /*!
  2. * ZeroClipboard
  3. * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
  4. * Copyright (c) 2009-2015 Jon Rohan, James M. Greene
  5. * Licensed MIT
  6. * http://zeroclipboard.org/
  7. * v2.3.0-beta.1
  8. */
  9. (function(window, undefined) {
  10. "use strict";
  11. /**
  12. * Store references to critically important global functions that may be
  13. * overridden on certain web pages.
  14. */
  15. var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _clearTimeout = _window.clearTimeout, _setInterval = _window.setInterval, _clearInterval = _window.clearInterval, _getComputedStyle = _window.getComputedStyle, _encodeURIComponent = _window.encodeURIComponent, _ActiveXObject = _window.ActiveXObject, _Error = _window.Error, _parseInt = _window.Number.parseInt || _window.parseInt, _parseFloat = _window.Number.parseFloat || _window.parseFloat, _isNaN = _window.Number.isNaN || _window.isNaN, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() {
  16. var unwrapper = function(el) {
  17. return el;
  18. };
  19. if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") {
  20. try {
  21. var div = _document.createElement("div");
  22. var unwrappedDiv = _window.unwrap(div);
  23. if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {
  24. unwrapper = _window.unwrap;
  25. }
  26. } catch (e) {}
  27. }
  28. return unwrapper;
  29. }();
  30. /**
  31. * Convert an `arguments` object into an Array.
  32. *
  33. * @returns The arguments as an Array
  34. * @private
  35. */
  36. var _args = function(argumentsObj) {
  37. return _slice.call(argumentsObj, 0);
  38. };
  39. /**
  40. * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.
  41. *
  42. * @returns The target object, augmented
  43. * @private
  44. */
  45. var _extend = function() {
  46. var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {};
  47. for (i = 1, len = args.length; i < len; i++) {
  48. if ((arg = args[i]) != null) {
  49. for (prop in arg) {
  50. if (_hasOwn.call(arg, prop)) {
  51. src = target[prop];
  52. copy = arg[prop];
  53. if (target !== copy && copy !== undefined) {
  54. target[prop] = copy;
  55. }
  56. }
  57. }
  58. }
  59. }
  60. return target;
  61. };
  62. /**
  63. * Return a deep copy of the source object or array.
  64. *
  65. * @returns Object or Array
  66. * @private
  67. */
  68. var _deepCopy = function(source) {
  69. var copy, i, len, prop;
  70. if (typeof source !== "object" || source == null || typeof source.nodeType === "number") {
  71. copy = source;
  72. } else if (typeof source.length === "number") {
  73. copy = [];
  74. for (i = 0, len = source.length; i < len; i++) {
  75. if (_hasOwn.call(source, i)) {
  76. copy[i] = _deepCopy(source[i]);
  77. }
  78. }
  79. } else {
  80. copy = {};
  81. for (prop in source) {
  82. if (_hasOwn.call(source, prop)) {
  83. copy[prop] = _deepCopy(source[prop]);
  84. }
  85. }
  86. }
  87. return copy;
  88. };
  89. /**
  90. * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.
  91. * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to
  92. * be kept.
  93. *
  94. * @returns A new filtered object.
  95. * @private
  96. */
  97. var _pick = function(obj, keys) {
  98. var newObj = {};
  99. for (var i = 0, len = keys.length; i < len; i++) {
  100. if (keys[i] in obj) {
  101. newObj[keys[i]] = obj[keys[i]];
  102. }
  103. }
  104. return newObj;
  105. };
  106. /**
  107. * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.
  108. * The inverse of `_pick`.
  109. *
  110. * @returns A new filtered object.
  111. * @private
  112. */
  113. var _omit = function(obj, keys) {
  114. var newObj = {};
  115. for (var prop in obj) {
  116. if (keys.indexOf(prop) === -1) {
  117. newObj[prop] = obj[prop];
  118. }
  119. }
  120. return newObj;
  121. };
  122. /**
  123. * Remove all owned, enumerable properties from an object.
  124. *
  125. * @returns The original object without its owned, enumerable properties.
  126. * @private
  127. */
  128. var _deleteOwnProperties = function(obj) {
  129. if (obj) {
  130. for (var prop in obj) {
  131. if (_hasOwn.call(obj, prop)) {
  132. delete obj[prop];
  133. }
  134. }
  135. }
  136. return obj;
  137. };
  138. /**
  139. * Determine if an element is contained within another element.
  140. *
  141. * @returns Boolean
  142. * @private
  143. */
  144. var _containedBy = function(el, ancestorEl) {
  145. if (el && el.nodeType === 1 && el.ownerDocument && ancestorEl && (ancestorEl.nodeType === 1 && ancestorEl.ownerDocument && ancestorEl.ownerDocument === el.ownerDocument || ancestorEl.nodeType === 9 && !ancestorEl.ownerDocument && ancestorEl === el.ownerDocument)) {
  146. do {
  147. if (el === ancestorEl) {
  148. return true;
  149. }
  150. el = el.parentNode;
  151. } while (el);
  152. }
  153. return false;
  154. };
  155. /**
  156. * Get the URL path's parent directory.
  157. *
  158. * @returns String or `undefined`
  159. * @private
  160. */
  161. var _getDirPathOfUrl = function(url) {
  162. var dir;
  163. if (typeof url === "string" && url) {
  164. dir = url.split("#")[0].split("?")[0];
  165. dir = url.slice(0, url.lastIndexOf("/") + 1);
  166. }
  167. return dir;
  168. };
  169. /**
  170. * Get the current script's URL by throwing an `Error` and analyzing it.
  171. *
  172. * @returns String or `undefined`
  173. * @private
  174. */
  175. var _getCurrentScriptUrlFromErrorStack = function(stack) {
  176. var url, matches;
  177. if (typeof stack === "string" && stack) {
  178. matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
  179. if (matches && matches[1]) {
  180. url = matches[1];
  181. } else {
  182. matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
  183. if (matches && matches[1]) {
  184. url = matches[1];
  185. }
  186. }
  187. }
  188. return url;
  189. };
  190. /**
  191. * Get the current script's URL by throwing an `Error` and analyzing it.
  192. *
  193. * @returns String or `undefined`
  194. * @private
  195. */
  196. var _getCurrentScriptUrlFromError = function() {
  197. var url, err;
  198. try {
  199. throw new _Error();
  200. } catch (e) {
  201. err = e;
  202. }
  203. if (err) {
  204. url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);
  205. }
  206. return url;
  207. };
  208. /**
  209. * Get the current script's URL.
  210. *
  211. * @returns String or `undefined`
  212. * @private
  213. */
  214. var _getCurrentScriptUrl = function() {
  215. var jsPath, scripts, i;
  216. if (_document.currentScript && (jsPath = _document.currentScript.src)) {
  217. return jsPath;
  218. }
  219. scripts = _document.getElementsByTagName("script");
  220. if (scripts.length === 1) {
  221. return scripts[0].src || undefined;
  222. }
  223. if ("readyState" in scripts[0]) {
  224. for (i = scripts.length; i--; ) {
  225. if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) {
  226. return jsPath;
  227. }
  228. }
  229. }
  230. if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) {
  231. return jsPath;
  232. }
  233. if (jsPath = _getCurrentScriptUrlFromError()) {
  234. return jsPath;
  235. }
  236. return undefined;
  237. };
  238. /**
  239. * Get the unanimous parent directory of ALL script tags.
  240. * If any script tags are either (a) inline or (b) from differing parent
  241. * directories, this method must return `undefined`.
  242. *
  243. * @returns String or `undefined`
  244. * @private
  245. */
  246. var _getUnanimousScriptParentDir = function() {
  247. var i, jsDir, jsPath, scripts = _document.getElementsByTagName("script");
  248. for (i = scripts.length; i--; ) {
  249. if (!(jsPath = scripts[i].src)) {
  250. jsDir = null;
  251. break;
  252. }
  253. jsPath = _getDirPathOfUrl(jsPath);
  254. if (jsDir == null) {
  255. jsDir = jsPath;
  256. } else if (jsDir !== jsPath) {
  257. jsDir = null;
  258. break;
  259. }
  260. }
  261. return jsDir || undefined;
  262. };
  263. /**
  264. * Get the presumed location of the "ZeroClipboard.swf" file, based on the location
  265. * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.).
  266. *
  267. * @returns String
  268. * @private
  269. */
  270. var _getDefaultSwfPath = function() {
  271. var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || "";
  272. return jsDir + "ZeroClipboard.swf";
  273. };
  274. /**
  275. * Is the client's operating system some version of Windows?
  276. *
  277. * @returns Boolean
  278. * @private
  279. */
  280. var _isWindows = function() {
  281. var isWindowsRegex = /win(dows|[\s]?(nt|me|ce|xp|vista|[\d]+))/i;
  282. return !!_navigator && (isWindowsRegex.test(_navigator.appVersion || "") || isWindowsRegex.test(_navigator.platform || "") || (_navigator.userAgent || "").indexOf("Windows") !== -1);
  283. };
  284. /**
  285. * Keep track of if the page is framed (in an `iframe`). This can never change.
  286. * @private
  287. */
  288. var _pageIsFramed = function() {
  289. return window.opener == null && (!!window.top && window != window.top || !!window.parent && window != window.parent);
  290. }();
  291. /**
  292. * Keep track of the state of the Flash object.
  293. * @private
  294. */
  295. var _flashState = {
  296. bridge: null,
  297. version: "0.0.0",
  298. pluginType: "unknown",
  299. disabled: null,
  300. outdated: null,
  301. sandboxed: null,
  302. unavailable: null,
  303. degraded: null,
  304. deactivated: null,
  305. overdue: null,
  306. ready: null
  307. };
  308. /**
  309. * The minimum Flash Player version required to use ZeroClipboard completely.
  310. * @readonly
  311. * @private
  312. */
  313. var _minimumFlashVersion = "11.0.0";
  314. /**
  315. * The ZeroClipboard library version number, as reported by Flash, at the time the SWF was compiled.
  316. */
  317. var _zcSwfVersion;
  318. /**
  319. * Keep track of all event listener registrations.
  320. * @private
  321. */
  322. var _handlers = {};
  323. /**
  324. * Keep track of the currently activated element.
  325. * @private
  326. */
  327. var _currentElement;
  328. /**
  329. * Keep track of the element that was activated when a `copy` process started.
  330. * @private
  331. */
  332. var _copyTarget;
  333. /**
  334. * Keep track of data for the pending clipboard transaction.
  335. * @private
  336. */
  337. var _clipData = {};
  338. /**
  339. * Keep track of data formats for the pending clipboard transaction.
  340. * @private
  341. */
  342. var _clipDataFormatMap = null;
  343. /**
  344. * Keep track of the Flash availability check timeout.
  345. * @private
  346. */
  347. var _flashCheckTimeout = 0;
  348. /**
  349. * Keep track of SWF network errors interval polling.
  350. * @private
  351. */
  352. var _swfFallbackCheckInterval = 0;
  353. /**
  354. * The `message` store for events
  355. * @private
  356. */
  357. var _eventMessages = {
  358. ready: "Flash communication is established",
  359. error: {
  360. "flash-disabled": "Flash is disabled or not installed. May also be attempting to run Flash in a sandboxed iframe, which is impossible.",
  361. "flash-outdated": "Flash is too outdated to support ZeroClipboard",
  362. "flash-sandboxed": "Attempting to run Flash in a sandboxed iframe, which is impossible",
  363. "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript",
  364. "flash-degraded": "Flash is unable to preserve data fidelity when communicating with JavaScript",
  365. "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate.\nThis may also mean that the ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity.\nMay also be attempting to run Flash in a sandboxed iframe, which is impossible.",
  366. "flash-overdue": "Flash communication was established but NOT within the acceptable time limit",
  367. "version-mismatch": "ZeroClipboard JS version number does not match ZeroClipboard SWF version number",
  368. "clipboard-error": "At least one error was thrown while ZeroClipboard was attempting to inject your data into the clipboard",
  369. "config-mismatch": "ZeroClipboard configuration does not match Flash's reality",
  370. "swf-not-found": "The ZeroClipboard SWF object could not be loaded, so please check your `swfPath` configuration and/or network connectivity"
  371. }
  372. };
  373. /**
  374. * The `name`s of `error` events that can only occur is Flash has at least
  375. * been able to load the SWF successfully.
  376. * @private
  377. */
  378. var _errorsThatOnlyOccurAfterFlashLoads = [ "flash-unavailable", "flash-degraded", "flash-overdue", "version-mismatch", "config-mismatch", "clipboard-error" ];
  379. /**
  380. * The `name`s of `error` events that should likely result in the `_flashState`
  381. * variable's property values being updated.
  382. * @private
  383. */
  384. var _flashStateErrorNames = [ "flash-disabled", "flash-outdated", "flash-sandboxed", "flash-unavailable", "flash-degraded", "flash-deactivated", "flash-overdue" ];
  385. /**
  386. * A RegExp to match the `name` property of `error` events related to Flash.
  387. * @private
  388. */
  389. var _flashStateErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.map(function(errorName) {
  390. return errorName.replace(/^flash-/, "");
  391. }).join("|") + ")$");
  392. /**
  393. * A RegExp to match the `name` property of `error` events related to Flash,
  394. * which is enabled.
  395. * @private
  396. */
  397. var _flashStateEnabledErrorNameMatchingRegex = new RegExp("^flash-(" + _flashStateErrorNames.slice(1).map(function(errorName) {
  398. return errorName.replace(/^flash-/, "");
  399. }).join("|") + ")$");
  400. /**
  401. * ZeroClipboard configuration defaults for the Core module.
  402. * @private
  403. */
  404. var _globalConfig = {
  405. swfPath: _getDefaultSwfPath(),
  406. trustedDomains: window.location.host ? [ window.location.host ] : [],
  407. cacheBust: true,
  408. forceEnhancedClipboard: false,
  409. flashLoadTimeout: 3e4,
  410. autoActivate: true,
  411. bubbleEvents: true,
  412. fixLineEndings: true,
  413. containerId: "global-zeroclipboard-html-bridge",
  414. containerClass: "global-zeroclipboard-container",
  415. swfObjectId: "global-zeroclipboard-flash-bridge",
  416. hoverClass: "zeroclipboard-is-hover",
  417. activeClass: "zeroclipboard-is-active",
  418. forceHandCursor: false,
  419. title: null,
  420. zIndex: 999999999
  421. };
  422. /**
  423. * The underlying implementation of `ZeroClipboard.config`.
  424. * @private
  425. */
  426. var _config = function(options) {
  427. if (typeof options === "object" && options !== null) {
  428. for (var prop in options) {
  429. if (_hasOwn.call(options, prop)) {
  430. if (/^(?:forceHandCursor|title|zIndex|bubbleEvents|fixLineEndings)$/.test(prop)) {
  431. _globalConfig[prop] = options[prop];
  432. } else if (_flashState.bridge == null) {
  433. if (prop === "containerId" || prop === "swfObjectId") {
  434. if (_isValidHtml4Id(options[prop])) {
  435. _globalConfig[prop] = options[prop];
  436. } else {
  437. throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID");
  438. }
  439. } else {
  440. _globalConfig[prop] = options[prop];
  441. }
  442. }
  443. }
  444. }
  445. }
  446. if (typeof options === "string" && options) {
  447. if (_hasOwn.call(_globalConfig, options)) {
  448. return _globalConfig[options];
  449. }
  450. return;
  451. }
  452. return _deepCopy(_globalConfig);
  453. };
  454. /**
  455. * The underlying implementation of `ZeroClipboard.state`.
  456. * @private
  457. */
  458. var _state = function() {
  459. _detectSandbox();
  460. return {
  461. browser: _pick(_navigator, [ "userAgent", "platform", "appName", "appVersion" ]),
  462. flash: _omit(_flashState, [ "bridge" ]),
  463. zeroclipboard: {
  464. version: ZeroClipboard.version,
  465. config: ZeroClipboard.config()
  466. }
  467. };
  468. };
  469. /**
  470. * The underlying implementation of `ZeroClipboard.isFlashUnusable`.
  471. * @private
  472. */
  473. var _isFlashUnusable = function() {
  474. return !!(_flashState.disabled || _flashState.outdated || _flashState.sandboxed || _flashState.unavailable || _flashState.degraded || _flashState.deactivated);
  475. };
  476. /**
  477. * The underlying implementation of `ZeroClipboard.on`.
  478. * @private
  479. */
  480. var _on = function(eventType, listener) {
  481. var i, len, events, added = {};
  482. if (typeof eventType === "string" && eventType) {
  483. events = eventType.toLowerCase().split(/\s+/);
  484. } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
  485. for (i in eventType) {
  486. if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
  487. ZeroClipboard.on(i, eventType[i]);
  488. }
  489. }
  490. }
  491. if (events && events.length) {
  492. for (i = 0, len = events.length; i < len; i++) {
  493. eventType = events[i].replace(/^on/, "");
  494. added[eventType] = true;
  495. if (!_handlers[eventType]) {
  496. _handlers[eventType] = [];
  497. }
  498. _handlers[eventType].push(listener);
  499. }
  500. if (added.ready && _flashState.ready) {
  501. ZeroClipboard.emit({
  502. type: "ready"
  503. });
  504. }
  505. if (added.error) {
  506. for (i = 0, len = _flashStateErrorNames.length; i < len; i++) {
  507. if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, "")] === true) {
  508. ZeroClipboard.emit({
  509. type: "error",
  510. name: _flashStateErrorNames[i]
  511. });
  512. break;
  513. }
  514. }
  515. if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) {
  516. ZeroClipboard.emit({
  517. type: "error",
  518. name: "version-mismatch",
  519. jsVersion: ZeroClipboard.version,
  520. swfVersion: _zcSwfVersion
  521. });
  522. }
  523. }
  524. }
  525. return ZeroClipboard;
  526. };
  527. /**
  528. * The underlying implementation of `ZeroClipboard.off`.
  529. * @private
  530. */
  531. var _off = function(eventType, listener) {
  532. var i, len, foundIndex, events, perEventHandlers;
  533. if (arguments.length === 0) {
  534. events = _keys(_handlers);
  535. } else if (typeof eventType === "string" && eventType) {
  536. events = eventType.split(/\s+/);
  537. } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
  538. for (i in eventType) {
  539. if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
  540. ZeroClipboard.off(i, eventType[i]);
  541. }
  542. }
  543. }
  544. if (events && events.length) {
  545. for (i = 0, len = events.length; i < len; i++) {
  546. eventType = events[i].toLowerCase().replace(/^on/, "");
  547. perEventHandlers = _handlers[eventType];
  548. if (perEventHandlers && perEventHandlers.length) {
  549. if (listener) {
  550. foundIndex = perEventHandlers.indexOf(listener);
  551. while (foundIndex !== -1) {
  552. perEventHandlers.splice(foundIndex, 1);
  553. foundIndex = perEventHandlers.indexOf(listener, foundIndex);
  554. }
  555. } else {
  556. perEventHandlers.length = 0;
  557. }
  558. }
  559. }
  560. }
  561. return ZeroClipboard;
  562. };
  563. /**
  564. * The underlying implementation of `ZeroClipboard.handlers`.
  565. * @private
  566. */
  567. var _listeners = function(eventType) {
  568. var copy;
  569. if (typeof eventType === "string" && eventType) {
  570. copy = _deepCopy(_handlers[eventType]) || null;
  571. } else {
  572. copy = _deepCopy(_handlers);
  573. }
  574. return copy;
  575. };
  576. /**
  577. * The underlying implementation of `ZeroClipboard.emit`.
  578. * @private
  579. */
  580. var _emit = function(event) {
  581. var eventCopy, returnVal, tmp;
  582. event = _createEvent(event);
  583. if (!event) {
  584. return;
  585. }
  586. if (_preprocessEvent(event)) {
  587. return;
  588. }
  589. if (event.type === "ready" && _flashState.overdue === true) {
  590. return ZeroClipboard.emit({
  591. type: "error",
  592. name: "flash-overdue"
  593. });
  594. }
  595. eventCopy = _extend({}, event);
  596. _dispatchCallbacks.call(this, eventCopy);
  597. if (event.type === "copy") {
  598. tmp = _mapClipDataToFlash(_clipData);
  599. returnVal = tmp.data;
  600. _clipDataFormatMap = tmp.formatMap;
  601. }
  602. return returnVal;
  603. };
  604. /**
  605. * The underlying implementation of `ZeroClipboard.create`.
  606. * @private
  607. */
  608. var _create = function() {
  609. var previousState = _flashState.sandboxed;
  610. _detectSandbox();
  611. if (typeof _flashState.ready !== "boolean") {
  612. _flashState.ready = false;
  613. }
  614. if (_flashState.sandboxed !== previousState && _flashState.sandboxed === true) {
  615. _flashState.ready = false;
  616. ZeroClipboard.emit({
  617. type: "error",
  618. name: "flash-sandboxed"
  619. });
  620. } else if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {
  621. var maxWait = _globalConfig.flashLoadTimeout;
  622. if (typeof maxWait === "number" && maxWait >= 0) {
  623. _flashCheckTimeout = _setTimeout(function() {
  624. if (typeof _flashState.deactivated !== "boolean") {
  625. _flashState.deactivated = true;
  626. }
  627. if (_flashState.deactivated === true) {
  628. ZeroClipboard.emit({
  629. type: "error",
  630. name: "flash-deactivated"
  631. });
  632. }
  633. }, maxWait);
  634. }
  635. _flashState.overdue = false;
  636. _embedSwf();
  637. }
  638. };
  639. /**
  640. * The underlying implementation of `ZeroClipboard.destroy`.
  641. * @private
  642. */
  643. var _destroy = function() {
  644. ZeroClipboard.clearData();
  645. ZeroClipboard.blur();
  646. ZeroClipboard.emit("destroy");
  647. _unembedSwf();
  648. ZeroClipboard.off();
  649. };
  650. /**
  651. * The underlying implementation of `ZeroClipboard.setData`.
  652. * @private
  653. */
  654. var _setData = function(format, data) {
  655. var dataObj;
  656. if (typeof format === "object" && format && typeof data === "undefined") {
  657. dataObj = format;
  658. ZeroClipboard.clearData();
  659. } else if (typeof format === "string" && format) {
  660. dataObj = {};
  661. dataObj[format] = data;
  662. } else {
  663. return;
  664. }
  665. for (var dataFormat in dataObj) {
  666. if (typeof dataFormat === "string" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]) {
  667. _clipData[dataFormat] = _fixLineEndings(dataObj[dataFormat]);
  668. }
  669. }
  670. };
  671. /**
  672. * The underlying implementation of `ZeroClipboard.clearData`.
  673. * @private
  674. */
  675. var _clearData = function(format) {
  676. if (typeof format === "undefined") {
  677. _deleteOwnProperties(_clipData);
  678. _clipDataFormatMap = null;
  679. } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
  680. delete _clipData[format];
  681. }
  682. };
  683. /**
  684. * The underlying implementation of `ZeroClipboard.getData`.
  685. * @private
  686. */
  687. var _getData = function(format) {
  688. if (typeof format === "undefined") {
  689. return _deepCopy(_clipData);
  690. } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
  691. return _clipData[format];
  692. }
  693. };
  694. /**
  695. * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.
  696. * @private
  697. */
  698. var _focus = function(element) {
  699. if (!(element && element.nodeType === 1)) {
  700. return;
  701. }
  702. if (_currentElement) {
  703. _removeClass(_currentElement, _globalConfig.activeClass);
  704. if (_currentElement !== element) {
  705. _removeClass(_currentElement, _globalConfig.hoverClass);
  706. }
  707. }
  708. _currentElement = element;
  709. _addClass(element, _globalConfig.hoverClass);
  710. var newTitle = element.getAttribute("title") || _globalConfig.title;
  711. if (typeof newTitle === "string" && newTitle) {
  712. var htmlBridge = _getHtmlBridge(_flashState.bridge);
  713. if (htmlBridge) {
  714. htmlBridge.setAttribute("title", newTitle);
  715. }
  716. }
  717. var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer";
  718. _setHandCursor(useHandCursor);
  719. _reposition();
  720. };
  721. /**
  722. * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.
  723. * @private
  724. */
  725. var _blur = function() {
  726. var htmlBridge = _getHtmlBridge(_flashState.bridge);
  727. if (htmlBridge) {
  728. htmlBridge.removeAttribute("title");
  729. htmlBridge.style.left = "0px";
  730. htmlBridge.style.top = "-9999px";
  731. htmlBridge.style.width = "1px";
  732. htmlBridge.style.height = "1px";
  733. }
  734. if (_currentElement) {
  735. _removeClass(_currentElement, _globalConfig.hoverClass);
  736. _removeClass(_currentElement, _globalConfig.activeClass);
  737. _currentElement = null;
  738. }
  739. };
  740. /**
  741. * The underlying implementation of `ZeroClipboard.activeElement`.
  742. * @private
  743. */
  744. var _activeElement = function() {
  745. return _currentElement || null;
  746. };
  747. /**
  748. * Check if a value is a valid HTML4 `ID` or `Name` token.
  749. * @private
  750. */
  751. var _isValidHtml4Id = function(id) {
  752. return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id);
  753. };
  754. /**
  755. * Create or update an `event` object, based on the `eventType`.
  756. * @private
  757. */
  758. var _createEvent = function(event) {
  759. var eventType;
  760. if (typeof event === "string" && event) {
  761. eventType = event;
  762. event = {};
  763. } else if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
  764. eventType = event.type;
  765. }
  766. if (!eventType) {
  767. return;
  768. }
  769. eventType = eventType.toLowerCase();
  770. if (!event.target && (/^(copy|aftercopy|_click)$/.test(eventType) || eventType === "error" && event.name === "clipboard-error")) {
  771. event.target = _copyTarget;
  772. }
  773. _extend(event, {
  774. type: eventType,
  775. target: event.target || _currentElement || null,
  776. relatedTarget: event.relatedTarget || null,
  777. currentTarget: _flashState && _flashState.bridge || null,
  778. timeStamp: event.timeStamp || _now() || null
  779. });
  780. var msg = _eventMessages[event.type];
  781. if (event.type === "error" && event.name && msg) {
  782. msg = msg[event.name];
  783. }
  784. if (msg) {
  785. event.message = msg;
  786. }
  787. if (event.type === "ready") {
  788. _extend(event, {
  789. target: null,
  790. version: _flashState.version
  791. });
  792. }
  793. if (event.type === "error") {
  794. if (_flashStateErrorNameMatchingRegex.test(event.name)) {
  795. _extend(event, {
  796. target: null,
  797. minimumVersion: _minimumFlashVersion
  798. });
  799. }
  800. if (_flashStateEnabledErrorNameMatchingRegex.test(event.name)) {
  801. _extend(event, {
  802. version: _flashState.version
  803. });
  804. }
  805. }
  806. if (event.type === "copy") {
  807. event.clipboardData = {
  808. setData: ZeroClipboard.setData,
  809. clearData: ZeroClipboard.clearData
  810. };
  811. }
  812. if (event.type === "aftercopy") {
  813. event = _mapClipResultsFromFlash(event, _clipDataFormatMap);
  814. }
  815. if (event.target && !event.relatedTarget) {
  816. event.relatedTarget = _getRelatedTarget(event.target);
  817. }
  818. return _addMouseData(event);
  819. };
  820. /**
  821. * Get a relatedTarget from the target's `data-clipboard-target` attribute
  822. * @private
  823. */
  824. var _getRelatedTarget = function(targetEl) {
  825. var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target");
  826. return relatedTargetId ? _document.getElementById(relatedTargetId) : null;
  827. };
  828. /**
  829. * Add element and position data to `MouseEvent` instances
  830. * @private
  831. */
  832. var _addMouseData = function(event) {
  833. if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
  834. var srcElement = event.target;
  835. var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined;
  836. var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined;
  837. var pos = _getElementPosition(srcElement);
  838. var screenLeft = _window.screenLeft || _window.screenX || 0;
  839. var screenTop = _window.screenTop || _window.screenY || 0;
  840. var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;
  841. var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;
  842. var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0);
  843. var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0);
  844. var clientX = pageX - scrollLeft;
  845. var clientY = pageY - scrollTop;
  846. var screenX = screenLeft + clientX;
  847. var screenY = screenTop + clientY;
  848. var moveX = typeof event.movementX === "number" ? event.movementX : 0;
  849. var moveY = typeof event.movementY === "number" ? event.movementY : 0;
  850. delete event._stageX;
  851. delete event._stageY;
  852. _extend(event, {
  853. srcElement: srcElement,
  854. fromElement: fromElement,
  855. toElement: toElement,
  856. screenX: screenX,
  857. screenY: screenY,
  858. pageX: pageX,
  859. pageY: pageY,
  860. clientX: clientX,
  861. clientY: clientY,
  862. x: clientX,
  863. y: clientY,
  864. movementX: moveX,
  865. movementY: moveY,
  866. offsetX: 0,
  867. offsetY: 0,
  868. layerX: 0,
  869. layerY: 0
  870. });
  871. }
  872. return event;
  873. };
  874. /**
  875. * Determine if an event's registered handlers should be execute synchronously or asynchronously.
  876. *
  877. * @returns {boolean}
  878. * @private
  879. */
  880. var _shouldPerformAsync = function(event) {
  881. var eventType = event && typeof event.type === "string" && event.type || "";
  882. return !/^(?:(?:before)?copy|destroy)$/.test(eventType);
  883. };
  884. /**
  885. * Control if a callback should be executed asynchronously or not.
  886. *
  887. * @returns `undefined`
  888. * @private
  889. */
  890. var _dispatchCallback = function(func, context, args, async) {
  891. if (async) {
  892. _setTimeout(function() {
  893. func.apply(context, args);
  894. }, 0);
  895. } else {
  896. func.apply(context, args);
  897. }
  898. };
  899. /**
  900. * Handle the actual dispatching of events to client instances.
  901. *
  902. * @returns `undefined`
  903. * @private
  904. */
  905. var _dispatchCallbacks = function(event) {
  906. if (!(typeof event === "object" && event && event.type)) {
  907. return;
  908. }
  909. var async = _shouldPerformAsync(event);
  910. var wildcardTypeHandlers = _handlers["*"] || [];
  911. var specificTypeHandlers = _handlers[event.type] || [];
  912. var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
  913. if (handlers && handlers.length) {
  914. var i, len, func, context, eventCopy, originalContext = this;
  915. for (i = 0, len = handlers.length; i < len; i++) {
  916. func = handlers[i];
  917. context = originalContext;
  918. if (typeof func === "string" && typeof _window[func] === "function") {
  919. func = _window[func];
  920. }
  921. if (typeof func === "object" && func && typeof func.handleEvent === "function") {
  922. context = func;
  923. func = func.handleEvent;
  924. }
  925. if (typeof func === "function") {
  926. eventCopy = _extend({}, event);
  927. _dispatchCallback(func, context, [ eventCopy ], async);
  928. }
  929. }
  930. }
  931. return this;
  932. };
  933. /**
  934. * Check an `error` event's `name` property to see if Flash has
  935. * already loaded, which rules out possible `iframe` sandboxing.
  936. * @private
  937. */
  938. var _getSandboxStatusFromErrorEvent = function(event) {
  939. var isSandboxed = null;
  940. if (_pageIsFramed === false || event && event.type === "error" && event.name && _errorsThatOnlyOccurAfterFlashLoads.indexOf(event.name) !== -1) {
  941. isSandboxed = false;
  942. }
  943. return isSandboxed;
  944. };
  945. /**
  946. * Preprocess any special behaviors, reactions, or state changes after receiving this event.
  947. * Executes only once per event emitted, NOT once per client.
  948. * @private
  949. */
  950. var _preprocessEvent = function(event) {
  951. var element = event.target || _currentElement || null;
  952. var sourceIsSwf = event._source === "swf";
  953. delete event._source;
  954. switch (event.type) {
  955. case "error":
  956. var isSandboxed = event.name === "flash-sandboxed" || _getSandboxStatusFromErrorEvent(event);
  957. if (typeof isSandboxed === "boolean") {
  958. _flashState.sandboxed = isSandboxed;
  959. }
  960. if (_flashStateErrorNames.indexOf(event.name) !== -1) {
  961. _extend(_flashState, {
  962. disabled: event.name === "flash-disabled",
  963. outdated: event.name === "flash-outdated",
  964. unavailable: event.name === "flash-unavailable",
  965. degraded: event.name === "flash-degraded",
  966. deactivated: event.name === "flash-deactivated",
  967. overdue: event.name === "flash-overdue",
  968. ready: false
  969. });
  970. } else if (event.name === "version-mismatch") {
  971. _zcSwfVersion = event.swfVersion;
  972. _extend(_flashState, {
  973. disabled: false,
  974. outdated: false,
  975. unavailable: false,
  976. degraded: false,
  977. deactivated: false,
  978. overdue: false,
  979. ready: false
  980. });
  981. }
  982. _clearTimeoutsAndPolling();
  983. break;
  984. case "ready":
  985. _zcSwfVersion = event.swfVersion;
  986. var wasDeactivated = _flashState.deactivated === true;
  987. _extend(_flashState, {
  988. disabled: false,
  989. outdated: false,
  990. sandboxed: false,
  991. unavailable: false,
  992. degraded: false,
  993. deactivated: false,
  994. overdue: wasDeactivated,
  995. ready: !wasDeactivated
  996. });
  997. _clearTimeoutsAndPolling();
  998. break;
  999. case "beforecopy":
  1000. _copyTarget = element;
  1001. break;
  1002. case "copy":
  1003. var textContent, htmlContent, targetEl = event.relatedTarget;
  1004. if (!(_clipData["text/html"] || _clipData["text/plain"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {
  1005. event.clipboardData.clearData();
  1006. event.clipboardData.setData("text/plain", textContent);
  1007. if (htmlContent !== textContent) {
  1008. event.clipboardData.setData("text/html", htmlContent);
  1009. }
  1010. } else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) {
  1011. event.clipboardData.clearData();
  1012. event.clipboardData.setData("text/plain", textContent);
  1013. }
  1014. break;
  1015. case "aftercopy":
  1016. _queueEmitClipboardErrors(event);
  1017. ZeroClipboard.clearData();
  1018. if (element && element !== _safeActiveElement() && element.focus) {
  1019. element.focus();
  1020. }
  1021. break;
  1022. case "_mouseover":
  1023. ZeroClipboard.focus(element);
  1024. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1025. if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
  1026. _fireMouseEvent(_extend({}, event, {
  1027. type: "mouseenter",
  1028. bubbles: false,
  1029. cancelable: false
  1030. }));
  1031. }
  1032. _fireMouseEvent(_extend({}, event, {
  1033. type: "mouseover"
  1034. }));
  1035. }
  1036. break;
  1037. case "_mouseout":
  1038. ZeroClipboard.blur();
  1039. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1040. if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
  1041. _fireMouseEvent(_extend({}, event, {
  1042. type: "mouseleave",
  1043. bubbles: false,
  1044. cancelable: false
  1045. }));
  1046. }
  1047. _fireMouseEvent(_extend({}, event, {
  1048. type: "mouseout"
  1049. }));
  1050. }
  1051. break;
  1052. case "_mousedown":
  1053. _addClass(element, _globalConfig.activeClass);
  1054. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1055. _fireMouseEvent(_extend({}, event, {
  1056. type: event.type.slice(1)
  1057. }));
  1058. }
  1059. break;
  1060. case "_mouseup":
  1061. _removeClass(element, _globalConfig.activeClass);
  1062. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1063. _fireMouseEvent(_extend({}, event, {
  1064. type: event.type.slice(1)
  1065. }));
  1066. }
  1067. break;
  1068. case "_click":
  1069. _copyTarget = null;
  1070. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1071. _fireMouseEvent(_extend({}, event, {
  1072. type: event.type.slice(1)
  1073. }));
  1074. }
  1075. break;
  1076. case "_mousemove":
  1077. if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
  1078. _fireMouseEvent(_extend({}, event, {
  1079. type: event.type.slice(1)
  1080. }));
  1081. }
  1082. break;
  1083. }
  1084. if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
  1085. return true;
  1086. }
  1087. };
  1088. /**
  1089. * Check an "aftercopy" event for clipboard errors and emit a corresponding "error" event.
  1090. * @private
  1091. */
  1092. var _queueEmitClipboardErrors = function(aftercopyEvent) {
  1093. if (aftercopyEvent.errors && aftercopyEvent.errors.length > 0) {
  1094. var errorEvent = _deepCopy(aftercopyEvent);
  1095. _extend(errorEvent, {
  1096. type: "error",
  1097. name: "clipboard-error"
  1098. });
  1099. delete errorEvent.success;
  1100. _setTimeout(function() {
  1101. ZeroClipboard.emit(errorEvent);
  1102. }, 0);
  1103. }
  1104. };
  1105. /**
  1106. * Dispatch a synthetic MouseEvent.
  1107. *
  1108. * @returns `undefined`
  1109. * @private
  1110. */
  1111. var _fireMouseEvent = function(event) {
  1112. if (!(event && typeof event.type === "string" && event)) {
  1113. return;
  1114. }
  1115. var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {
  1116. view: doc.defaultView || _window,
  1117. canBubble: true,
  1118. cancelable: true,
  1119. detail: event.type === "click" ? 1 : 0,
  1120. button: typeof event.which === "number" ? event.which - 1 : typeof event.button === "number" ? event.button : doc.createEvent ? 0 : 1
  1121. }, args = _extend(defaults, event);
  1122. if (!target) {
  1123. return;
  1124. }
  1125. if (doc.createEvent && target.dispatchEvent) {
  1126. args = [ args.type, args.canBubble, args.cancelable, args.view, args.detail, args.screenX, args.screenY, args.clientX, args.clientY, args.ctrlKey, args.altKey, args.shiftKey, args.metaKey, args.button, args.relatedTarget ];
  1127. e = doc.createEvent("MouseEvents");
  1128. if (e.initMouseEvent) {
  1129. e.initMouseEvent.apply(e, args);
  1130. e._source = "js";
  1131. target.dispatchEvent(e);
  1132. }
  1133. }
  1134. };
  1135. /**
  1136. * Continuously poll the DOM until either:
  1137. * (a) the fallback content becomes visible, or
  1138. * (b) we receive an event from SWF (handled elsewhere)
  1139. *
  1140. * IMPORTANT:
  1141. * This is NOT a necessary check but it can result in significantly faster
  1142. * detection of bad `swfPath` configuration and/or network/server issues [in
  1143. * supported browsers] than waiting for the entire `flashLoadTimeout` duration
  1144. * to elapse before detecting that the SWF cannot be loaded. The detection
  1145. * duration can be anywhere from 10-30 times faster [in supported browsers] by
  1146. * using this approach.
  1147. *
  1148. * @returns `undefined`
  1149. * @private
  1150. */
  1151. var _watchForSwfFallbackContent = function() {
  1152. var maxWait = _globalConfig.flashLoadTimeout;
  1153. if (typeof maxWait === "number" && maxWait >= 0) {
  1154. var pollWait = Math.min(1e3, maxWait / 10);
  1155. var fallbackContentId = _globalConfig.swfObjectId + "_fallbackContent";
  1156. _swfFallbackCheckInterval = _setInterval(function() {
  1157. var el = _document.getElementById(fallbackContentId);
  1158. if (_isElementVisible(el)) {
  1159. _clearTimeoutsAndPolling();
  1160. _flashState.deactivated = null;
  1161. ZeroClipboard.emit({
  1162. type: "error",
  1163. name: "swf-not-found"
  1164. });
  1165. }
  1166. }, pollWait);
  1167. }
  1168. };
  1169. /**
  1170. * Create the HTML bridge element to embed the Flash object into.
  1171. * @private
  1172. */
  1173. var _createHtmlBridge = function() {
  1174. var container = _document.createElement("div");
  1175. container.id = _globalConfig.containerId;
  1176. container.className = _globalConfig.containerClass;
  1177. container.style.position = "absolute";
  1178. container.style.left = "0px";
  1179. container.style.top = "-9999px";
  1180. container.style.width = "1px";
  1181. container.style.height = "1px";
  1182. container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex);
  1183. return container;
  1184. };
  1185. /**
  1186. * Get the HTML element container that wraps the Flash bridge object/element.
  1187. * @private
  1188. */
  1189. var _getHtmlBridge = function(flashBridge) {
  1190. var htmlBridge = flashBridge && flashBridge.parentNode;
  1191. while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) {
  1192. htmlBridge = htmlBridge.parentNode;
  1193. }
  1194. return htmlBridge || null;
  1195. };
  1196. /**
  1197. * Create the SWF object.
  1198. *
  1199. * @returns The SWF object reference.
  1200. * @private
  1201. */
  1202. var _embedSwf = function() {
  1203. var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);
  1204. if (!flashBridge) {
  1205. var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);
  1206. var allowNetworking = allowScriptAccess === "never" ? "none" : "all";
  1207. var flashvars = _vars(_extend({
  1208. jsVersion: ZeroClipboard.version
  1209. }, _globalConfig));
  1210. var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);
  1211. container = _createHtmlBridge();
  1212. var divToBeReplaced = _document.createElement("div");
  1213. container.appendChild(divToBeReplaced);
  1214. _document.body.appendChild(container);
  1215. var tmpDiv = _document.createElement("div");
  1216. var usingActiveX = _flashState.pluginType === "activex";
  1217. tmpDiv.innerHTML = '<object id="' + _globalConfig.swfObjectId + '" name="' + _globalConfig.swfObjectId + '" ' + 'width="100%" height="100%" ' + (usingActiveX ? 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' : 'type="application/x-shockwave-flash" data="' + swfUrl + '"') + ">" + (usingActiveX ? '<param name="movie" value="' + swfUrl + '"/>' : "") + '<param name="allowScriptAccess" value="' + allowScriptAccess + '"/>' + '<param name="allowNetworking" value="' + allowNetworking + '"/>' + '<param name="menu" value="false"/>' + '<param name="wmode" value="transparent"/>' + '<param name="flashvars" value="' + flashvars + '"/>' + '<div id="' + _globalConfig.swfObjectId + '_fallbackContent">&nbsp;</div>' + "</object>";
  1218. flashBridge = tmpDiv.firstChild;
  1219. tmpDiv = null;
  1220. _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;
  1221. container.replaceChild(flashBridge, divToBeReplaced);
  1222. _watchForSwfFallbackContent();
  1223. }
  1224. if (!flashBridge) {
  1225. flashBridge = _document[_globalConfig.swfObjectId];
  1226. if (flashBridge && (len = flashBridge.length)) {
  1227. flashBridge = flashBridge[len - 1];
  1228. }
  1229. if (!flashBridge && container) {
  1230. flashBridge = container.firstChild;
  1231. }
  1232. }
  1233. _flashState.bridge = flashBridge || null;
  1234. return flashBridge;
  1235. };
  1236. /**
  1237. * Destroy the SWF object.
  1238. * @private
  1239. */
  1240. var _unembedSwf = function() {
  1241. var flashBridge = _flashState.bridge;
  1242. if (flashBridge) {
  1243. var htmlBridge = _getHtmlBridge(flashBridge);
  1244. if (htmlBridge) {
  1245. if (_flashState.pluginType === "activex" && "readyState" in flashBridge) {
  1246. flashBridge.style.display = "none";
  1247. (function removeSwfFromIE() {
  1248. if (flashBridge.readyState === 4) {
  1249. for (var prop in flashBridge) {
  1250. if (typeof flashBridge[prop] === "function") {
  1251. flashBridge[prop] = null;
  1252. }
  1253. }
  1254. if (flashBridge.parentNode) {
  1255. flashBridge.parentNode.removeChild(flashBridge);
  1256. }
  1257. if (htmlBridge.parentNode) {
  1258. htmlBridge.parentNode.removeChild(htmlBridge);
  1259. }
  1260. } else {
  1261. _setTimeout(removeSwfFromIE, 10);
  1262. }
  1263. })();
  1264. } else {
  1265. if (flashBridge.parentNode) {
  1266. flashBridge.parentNode.removeChild(flashBridge);
  1267. }
  1268. if (htmlBridge.parentNode) {
  1269. htmlBridge.parentNode.removeChild(htmlBridge);
  1270. }
  1271. }
  1272. }
  1273. _clearTimeoutsAndPolling();
  1274. _flashState.ready = null;
  1275. _flashState.bridge = null;
  1276. _flashState.deactivated = null;
  1277. _zcSwfVersion = undefined;
  1278. }
  1279. };
  1280. /**
  1281. * Map the data format names of the "clipData" to Flash-friendly names.
  1282. *
  1283. * @returns A new transformed object.
  1284. * @private
  1285. */
  1286. var _mapClipDataToFlash = function(clipData) {
  1287. var newClipData = {}, formatMap = {};
  1288. if (!(typeof clipData === "object" && clipData)) {
  1289. return;
  1290. }
  1291. for (var dataFormat in clipData) {
  1292. if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) {
  1293. switch (dataFormat.toLowerCase()) {
  1294. case "text/plain":
  1295. case "text":
  1296. case "air:text":
  1297. case "flash:text":
  1298. newClipData.text = clipData[dataFormat];
  1299. formatMap.text = dataFormat;
  1300. break;
  1301. case "text/html":
  1302. case "html":
  1303. case "air:html":
  1304. case "flash:html":
  1305. newClipData.html = clipData[dataFormat];
  1306. formatMap.html = dataFormat;
  1307. break;
  1308. case "application/rtf":
  1309. case "text/rtf":
  1310. case "rtf":
  1311. case "richtext":
  1312. case "air:rtf":
  1313. case "flash:rtf":
  1314. newClipData.rtf = clipData[dataFormat];
  1315. formatMap.rtf = dataFormat;
  1316. break;
  1317. default:
  1318. break;
  1319. }
  1320. }
  1321. }
  1322. return {
  1323. data: newClipData,
  1324. formatMap: formatMap
  1325. };
  1326. };
  1327. /**
  1328. * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping).
  1329. *
  1330. * @returns A new transformed object.
  1331. * @private
  1332. */
  1333. var _mapClipResultsFromFlash = function(clipResults, formatMap) {
  1334. if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) {
  1335. return clipResults;
  1336. }
  1337. var newResults = {};
  1338. for (var prop in clipResults) {
  1339. if (_hasOwn.call(clipResults, prop)) {
  1340. if (prop === "errors") {
  1341. newResults[prop] = clipResults[prop] ? clipResults[prop].slice() : [];
  1342. for (var i = 0, len = newResults[prop].length; i < len; i++) {
  1343. newResults[prop][i].format = formatMap[newResults[prop][i].format];
  1344. }
  1345. } else if (prop !== "success" && prop !== "data") {
  1346. newResults[prop] = clipResults[prop];
  1347. } else {
  1348. newResults[prop] = {};
  1349. var tmpHash = clipResults[prop];
  1350. for (var dataFormat in tmpHash) {
  1351. if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {
  1352. newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];
  1353. }
  1354. }
  1355. }
  1356. }
  1357. }
  1358. return newResults;
  1359. };
  1360. /**
  1361. * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}"
  1362. * query param string to return. Does NOT append that string to the original path.
  1363. * This is useful because ExternalInterface often breaks when a Flash SWF is cached.
  1364. *
  1365. * @returns The `noCache` query param with necessary "?"/"&" prefix.
  1366. * @private
  1367. */
  1368. var _cacheBust = function(path, options) {
  1369. var cacheBust = options == null || options && options.cacheBust === true;
  1370. if (cacheBust) {
  1371. return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now();
  1372. } else {
  1373. return "";
  1374. }
  1375. };
  1376. /**
  1377. * Creates a query string for the FlashVars param.
  1378. * Does NOT include the cache-busting query param.
  1379. *
  1380. * @returns FlashVars query string
  1381. * @private
  1382. */
  1383. var _vars = function(options) {
  1384. var i, len, domain, domains, str = "", trustedOriginsExpanded = [];
  1385. if (options.trustedDomains) {
  1386. if (typeof options.trustedDomains === "string") {
  1387. domains = [ options.trustedDomains ];
  1388. } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) {
  1389. domains = options.trustedDomains;
  1390. }
  1391. }
  1392. if (domains && domains.length) {
  1393. for (i = 0, len = domains.length; i < len; i++) {
  1394. if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") {
  1395. domain = _extractDomain(domains[i]);
  1396. if (!domain) {
  1397. continue;
  1398. }
  1399. if (domain === "*") {
  1400. trustedOriginsExpanded.length = 0;
  1401. trustedOriginsExpanded.push(domain);
  1402. break;
  1403. }
  1404. trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, "//" + domain, _window.location.protocol + "//" + domain ]);
  1405. }
  1406. }
  1407. }
  1408. if (trustedOriginsExpanded.length) {
  1409. str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(","));
  1410. }
  1411. if (options.forceEnhancedClipboard === true) {
  1412. str += (str ? "&" : "") + "forceEnhancedClipboard=true";
  1413. }
  1414. if (typeof options.swfObjectId === "string" && options.swfObjectId) {
  1415. str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId);
  1416. }
  1417. if (typeof options.jsVersion === "string" && options.jsVersion) {
  1418. str += (str ? "&" : "") + "jsVersion=" + _encodeURIComponent(options.jsVersion);
  1419. }
  1420. return str;
  1421. };
  1422. /**
  1423. * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or
  1424. * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/").
  1425. *
  1426. * @returns the domain
  1427. * @private
  1428. */
  1429. var _extractDomain = function(originOrUrl) {
  1430. if (originOrUrl == null || originOrUrl === "") {
  1431. return null;
  1432. }
  1433. originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, "");
  1434. if (originOrUrl === "") {
  1435. return null;
  1436. }
  1437. var protocolIndex = originOrUrl.indexOf("//");
  1438. originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);
  1439. var pathIndex = originOrUrl.indexOf("/");
  1440. originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);
  1441. if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") {
  1442. return null;
  1443. }
  1444. return originOrUrl || null;
  1445. };
  1446. /**
  1447. * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.
  1448. *
  1449. * @returns The appropriate script access level.
  1450. * @private
  1451. */
  1452. var _determineScriptAccess = function() {
  1453. var _extractAllDomains = function(origins) {
  1454. var i, len, tmp, resultsArray = [];
  1455. if (typeof origins === "string") {
  1456. origins = [ origins ];
  1457. }
  1458. if (!(typeof origins === "object" && origins && typeof origins.length === "number")) {
  1459. return resultsArray;
  1460. }
  1461. for (i = 0, len = origins.length; i < len; i++) {
  1462. if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {
  1463. if (tmp === "*") {
  1464. resultsArray.length = 0;
  1465. resultsArray.push("*");
  1466. break;
  1467. }
  1468. if (resultsArray.indexOf(tmp) === -1) {
  1469. resultsArray.push(tmp);
  1470. }
  1471. }
  1472. }
  1473. return resultsArray;
  1474. };
  1475. return function(currentDomain, configOptions) {
  1476. var swfDomain = _extractDomain(configOptions.swfPath);
  1477. if (swfDomain === null) {
  1478. swfDomain = currentDomain;
  1479. }
  1480. var trustedDomains = _extractAllDomains(configOptions.trustedDomains);
  1481. var len = trustedDomains.length;
  1482. if (len > 0) {
  1483. if (len === 1 && trustedDomains[0] === "*") {
  1484. return "always";
  1485. }
  1486. if (trustedDomains.indexOf(currentDomain) !== -1) {
  1487. if (len === 1 && currentDomain === swfDomain) {
  1488. return "sameDomain";
  1489. }
  1490. return "always";
  1491. }
  1492. }
  1493. return "never";
  1494. };
  1495. }();
  1496. /**
  1497. * Get the currently active/focused DOM element.
  1498. *
  1499. * @returns the currently active/focused element, or `null`
  1500. * @private
  1501. */
  1502. var _safeActiveElement = function() {
  1503. try {
  1504. return _document.activeElement;
  1505. } catch (err) {
  1506. return null;
  1507. }
  1508. };
  1509. /**
  1510. * Add a class to an element, if it doesn't already have it.
  1511. *
  1512. * @returns The element, with its new class added.
  1513. * @private
  1514. */
  1515. var _addClass = function(element, value) {
  1516. var c, cl, className, classNames = [];
  1517. if (typeof value === "string" && value) {
  1518. classNames = value.split(/\s+/);
  1519. }
  1520. if (element && element.nodeType === 1 && classNames.length > 0) {
  1521. className = (" " + (element.className || "") + " ").replace(/[\t\r\n\f]/g, " ");
  1522. for (c = 0, cl = classNames.length; c < cl; c++) {
  1523. if (className.indexOf(" " + classNames[c] + " ") === -1) {
  1524. className += classNames[c] + " ";
  1525. }
  1526. }
  1527. className = className.replace(/^\s+|\s+$/g, "");
  1528. if (className !== element.className) {
  1529. element.className = className;
  1530. }
  1531. }
  1532. return element;
  1533. };
  1534. /**
  1535. * Remove a class from an element, if it has it.
  1536. *
  1537. * @returns The element, with its class removed.
  1538. * @private
  1539. */
  1540. var _removeClass = function(element, value) {
  1541. var c, cl, className, classNames = [];
  1542. if (typeof value === "string" && value) {
  1543. classNames = value.split(/\s+/);
  1544. }
  1545. if (element && element.nodeType === 1 && classNames.length > 0) {
  1546. if (element.className) {
  1547. className = (" " + element.className + " ").replace(/[\t\r\n\f]/g, " ");
  1548. for (c = 0, cl = classNames.length; c < cl; c++) {
  1549. className = className.replace(" " + classNames[c] + " ", " ");
  1550. }
  1551. className = className.replace(/^\s+|\s+$/g, "");
  1552. if (className !== element.className) {
  1553. element.className = className;
  1554. }
  1555. }
  1556. }
  1557. return element;
  1558. };
  1559. /**
  1560. * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`,
  1561. * then we assume that it should be a hand ("pointer") cursor if the element
  1562. * is an anchor element ("a" tag).
  1563. *
  1564. * @returns The computed style property.
  1565. * @private
  1566. */
  1567. var _getStyle = function(el, prop) {
  1568. var value = _getComputedStyle(el, null).getPropertyValue(prop);
  1569. if (prop === "cursor") {
  1570. if (!value || value === "auto") {
  1571. if (el.nodeName === "A") {
  1572. return "pointer";
  1573. }
  1574. }
  1575. }
  1576. return value;
  1577. };
  1578. /**
  1579. * Get the absolutely positioned coordinates of a DOM element.
  1580. *
  1581. * @returns Object containing the element's position, width, and height.
  1582. * @private
  1583. */
  1584. var _getElementPosition = function(el) {
  1585. var pos = {
  1586. left: 0,
  1587. top: 0,
  1588. width: 0,
  1589. height: 0
  1590. };
  1591. if (el.getBoundingClientRect) {
  1592. var elRect = el.getBoundingClientRect();
  1593. var pageXOffset = _window.pageXOffset;
  1594. var pageYOffset = _window.pageYOffset;
  1595. var leftBorderWidth = _document.documentElement.clientLeft || 0;
  1596. var topBorderWidth = _document.documentElement.clientTop || 0;
  1597. var leftBodyOffset = 0;
  1598. var topBodyOffset = 0;
  1599. if (_getStyle(_document.body, "position") === "relative") {
  1600. var bodyRect = _document.body.getBoundingClientRect();
  1601. var htmlRect = _document.documentElement.getBoundingClientRect();
  1602. leftBodyOffset = bodyRect.left - htmlRect.left || 0;
  1603. topBodyOffset = bodyRect.top - htmlRect.top || 0;
  1604. }
  1605. pos.left = elRect.left + pageXOffset - leftBorderWidth - leftBodyOffset;
  1606. pos.top = elRect.top + pageYOffset - topBorderWidth - topBodyOffset;
  1607. pos.width = "width" in elRect ? elRect.width : elRect.right - elRect.left;
  1608. pos.height = "height" in elRect ? elRect.height : elRect.bottom - elRect.top;
  1609. }
  1610. return pos;
  1611. };
  1612. /**
  1613. * Determine is an element is visible somewhere within the document (page).
  1614. *
  1615. * @returns Boolean
  1616. * @private
  1617. */
  1618. var _isElementVisible = function(el) {
  1619. if (!el) {
  1620. return false;
  1621. }
  1622. var styles = _getComputedStyle(el, null);
  1623. if (!styles) {
  1624. return false;
  1625. }
  1626. var hasCssHeight = _parseFloat(styles.height) > 0;
  1627. var hasCssWidth = _parseFloat(styles.width) > 0;
  1628. var hasCssTop = _parseFloat(styles.top) >= 0;
  1629. var hasCssLeft = _parseFloat(styles.left) >= 0;
  1630. var cssKnows = hasCssHeight && hasCssWidth && hasCssTop && hasCssLeft;
  1631. var rect = cssKnows ? null : _getElementPosition(el);
  1632. var isVisible = styles.display !== "none" && styles.visibility !== "collapse" && (cssKnows || !!rect && (hasCssHeight || rect.height > 0) && (hasCssWidth || rect.width > 0) && (hasCssTop || rect.top >= 0) && (hasCssLeft || rect.left >= 0));
  1633. return isVisible;
  1634. };
  1635. /**
  1636. * Clear all existing timeouts and interval polling delegates.
  1637. *
  1638. * @returns `undefined`
  1639. * @private
  1640. */
  1641. var _clearTimeoutsAndPolling = function() {
  1642. _clearTimeout(_flashCheckTimeout);
  1643. _flashCheckTimeout = 0;
  1644. _clearInterval(_swfFallbackCheckInterval);
  1645. _swfFallbackCheckInterval = 0;
  1646. };
  1647. /**
  1648. * Reposition the Flash object to cover the currently activated element.
  1649. *
  1650. * @returns `undefined`
  1651. * @private
  1652. */
  1653. var _reposition = function() {
  1654. var htmlBridge;
  1655. if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {
  1656. var pos = _getElementPosition(_currentElement);
  1657. _extend(htmlBridge.style, {
  1658. width: pos.width + "px",
  1659. height: pos.height + "px",
  1660. top: pos.top + "px",
  1661. left: pos.left + "px",
  1662. zIndex: "" + _getSafeZIndex(_globalConfig.zIndex)
  1663. });
  1664. }
  1665. };
  1666. /**
  1667. * Sends a signal to the Flash object to display the hand cursor if `true`.
  1668. *
  1669. * @returns `undefined`
  1670. * @private
  1671. */
  1672. var _setHandCursor = function(enabled) {
  1673. if (_flashState.ready === true) {
  1674. if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") {
  1675. _flashState.bridge.setHandCursor(enabled);
  1676. } else {
  1677. _flashState.ready = false;
  1678. }
  1679. }
  1680. };
  1681. /**
  1682. * Get a safe value for `zIndex`
  1683. *
  1684. * @returns an integer, or "auto"
  1685. * @private
  1686. */
  1687. var _getSafeZIndex = function(val) {
  1688. if (/^(?:auto|inherit)$/.test(val)) {
  1689. return val;
  1690. }
  1691. var zIndex;
  1692. if (typeof val === "number" && !_isNaN(val)) {
  1693. zIndex = val;
  1694. } else if (typeof val === "string") {
  1695. zIndex = _getSafeZIndex(_parseInt(val, 10));
  1696. }
  1697. return typeof zIndex === "number" ? zIndex : "auto";
  1698. };
  1699. /**
  1700. * Ensure OS-compliant line endings, i.e. "\r\n" on Windows, "\n" elsewhere
  1701. *
  1702. * @returns string
  1703. * @private
  1704. */
  1705. var _fixLineEndings = function(content) {
  1706. var replaceRegex = /(\r\n|\r|\n)/g;
  1707. if (typeof content === "string" && _globalConfig.fixLineEndings === true) {
  1708. if (_isWindows()) {
  1709. if (/((^|[^\r])\n|\r([^\n]|$))/.test(content)) {
  1710. content = content.replace(replaceRegex, "\r\n");
  1711. }
  1712. } else if (/\r/.test(content)) {
  1713. content = content.replace(replaceRegex, "\n");
  1714. }
  1715. }
  1716. return content;
  1717. };
  1718. /**
  1719. * Attempt to detect if ZeroClipboard is executing inside of a sandboxed iframe.
  1720. * If it is, Flash Player cannot be used, so ZeroClipboard is dead in the water.
  1721. *
  1722. * @see {@link http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Dec/0002.html}
  1723. * @see {@link https://github.com/zeroclipboard/zeroclipboard/issues/511}
  1724. * @see {@link http://zeroclipboard.org/test-iframes.html}
  1725. *
  1726. * @returns `true` (is sandboxed), `false` (is not sandboxed), or `null` (uncertain)
  1727. * @private
  1728. */
  1729. var _detectSandbox = function(doNotReassessFlashSupport) {
  1730. var effectiveScriptOrigin, frame, frameError, previousState = _flashState.sandboxed, isSandboxed = null;
  1731. doNotReassessFlashSupport = doNotReassessFlashSupport === true;
  1732. if (_pageIsFramed === false) {
  1733. isSandboxed = false;
  1734. } else {
  1735. try {
  1736. frame = window.frameElement || null;
  1737. } catch (e) {
  1738. frameError = {
  1739. name: e.name,
  1740. message: e.message
  1741. };
  1742. }
  1743. if (frame && frame.nodeType === 1 && frame.nodeName === "IFRAME") {
  1744. try {
  1745. isSandboxed = frame.hasAttribute("sandbox");
  1746. } catch (e) {
  1747. isSandboxed = null;
  1748. }
  1749. } else {
  1750. try {
  1751. effectiveScriptOrigin = document.domain || null;
  1752. } catch (e) {
  1753. effectiveScriptOrigin = null;
  1754. }
  1755. if (effectiveScriptOrigin === null || frameError && frameError.name === "SecurityError" && /(^|[\s\(\[@])sandbox(es|ed|ing|[\s\.,!\)\]@]|$)/.test(frameError.message.toLowerCase())) {
  1756. isSandboxed = true;
  1757. }
  1758. }
  1759. }
  1760. _flashState.sandboxed = isSandboxed;
  1761. if (previousState !== isSandboxed && !doNotReassessFlashSupport) {
  1762. _detectFlashSupport(_ActiveXObject);
  1763. }
  1764. return isSandboxed;
  1765. };
  1766. /**
  1767. * Detect the Flash Player status, version, and plugin type.
  1768. *
  1769. * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}
  1770. * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}
  1771. *
  1772. * @returns `undefined`
  1773. * @private
  1774. */
  1775. var _detectFlashSupport = function(ActiveXObject) {
  1776. var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = "";
  1777. /**
  1778. * Derived from Apple's suggested sniffer.
  1779. * @param {String} desc e.g. "Shockwave Flash 7.0 r61"
  1780. * @returns {String} "7.0.61"
  1781. * @private
  1782. */
  1783. function parseFlashVersion(desc) {
  1784. var matches = desc.match(/[\d]+/g);
  1785. matches.length = 3;
  1786. return matches.join(".");
  1787. }
  1788. function isPepperFlash(flashPlayerFileName) {
  1789. return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === "chrome.plugin");
  1790. }
  1791. function inspectPlugin(plugin) {
  1792. if (plugin) {
  1793. hasFlash = true;
  1794. if (plugin.version) {
  1795. flashVersion = parseFlashVersion(plugin.version);
  1796. }
  1797. if (!flashVersion && plugin.description) {
  1798. flashVersion = parseFlashVersion(plugin.description);
  1799. }
  1800. if (plugin.filename) {
  1801. isPPAPI = isPepperFlash(plugin.filename);
  1802. }
  1803. }
  1804. }
  1805. if (_navigator.plugins && _navigator.plugins.length) {
  1806. plugin = _navigator.plugins["Shockwave Flash"];
  1807. inspectPlugin(plugin);
  1808. if (_navigator.plugins["Shockwave Flash 2.0"]) {
  1809. hasFlash = true;
  1810. flashVersion = "2.0.0.11";
  1811. }
  1812. } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {
  1813. mimeType = _navigator.mimeTypes["application/x-shockwave-flash"];
  1814. plugin = mimeType && mimeType.enabledPlugin;
  1815. inspectPlugin(plugin);
  1816. } else if (typeof ActiveXObject !== "undefined") {
  1817. isActiveX = true;
  1818. try {
  1819. ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
  1820. hasFlash = true;
  1821. flashVersion = parseFlashVersion(ax.GetVariable("$version"));
  1822. } catch (e1) {
  1823. try {
  1824. ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
  1825. hasFlash = true;
  1826. flashVersion = "6.0.21";
  1827. } catch (e2) {
  1828. try {
  1829. ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
  1830. hasFlash = true;
  1831. flashVersion = parseFlashVersion(ax.GetVariable("$version"));
  1832. } catch (e3) {
  1833. isActiveX = false;
  1834. }
  1835. }
  1836. }
  1837. }
  1838. _flashState.disabled = hasFlash !== true;
  1839. _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);
  1840. _flashState.version = flashVersion || "0.0.0";
  1841. _flashState.pluginType = isPPAPI ? "pepper" : isActiveX ? "activex" : hasFlash ? "netscape" : "unknown";
  1842. };
  1843. /**
  1844. * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.
  1845. */
  1846. _detectFlashSupport(_ActiveXObject);
  1847. /**
  1848. * Always assess the `sandboxed` state of the page at important Flash-related moments.
  1849. */
  1850. _detectSandbox(true);
  1851. /**
  1852. * A shell constructor for `ZeroClipboard` client instances.
  1853. *
  1854. * @constructor
  1855. */
  1856. var ZeroClipboard = function() {
  1857. if (!(this instanceof ZeroClipboard)) {
  1858. return new ZeroClipboard();
  1859. }
  1860. if (typeof ZeroClipboard._createClient === "function") {
  1861. ZeroClipboard._createClient.apply(this, _args(arguments));
  1862. }
  1863. };
  1864. /**
  1865. * The ZeroClipboard library's version number.
  1866. *
  1867. * @static
  1868. * @readonly
  1869. * @property {string}
  1870. */
  1871. _defineProperty(ZeroClipboard, "version", {
  1872. value: "2.3.0-beta.1",
  1873. writable: false,
  1874. configurable: true,
  1875. enumerable: true
  1876. });
  1877. /**
  1878. * Update or get a copy of the ZeroClipboard global configuration.
  1879. * Returns a copy of the current/updated configuration.
  1880. *
  1881. * @returns Object
  1882. * @static
  1883. */
  1884. ZeroClipboard.config = function() {
  1885. return _config.apply(this, _args(arguments));
  1886. };
  1887. /**
  1888. * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
  1889. *
  1890. * @returns Object
  1891. * @static
  1892. */
  1893. ZeroClipboard.state = function() {
  1894. return _state.apply(this, _args(arguments));
  1895. };
  1896. /**
  1897. * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.
  1898. *
  1899. * @returns Boolean
  1900. * @static
  1901. */
  1902. ZeroClipboard.isFlashUnusable = function() {
  1903. return _isFlashUnusable.apply(this, _args(arguments));
  1904. };
  1905. /**
  1906. * Register an event listener.
  1907. *
  1908. * @returns `ZeroClipboard`
  1909. * @static
  1910. */
  1911. ZeroClipboard.on = function() {
  1912. return _on.apply(this, _args(arguments));
  1913. };
  1914. /**
  1915. * Unregister an event listener.
  1916. * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.
  1917. * If no `eventType` is provided, it will unregister all listeners for every event type.
  1918. *
  1919. * @returns `ZeroClipboard`
  1920. * @static
  1921. */
  1922. ZeroClipboard.off = function() {
  1923. return _off.apply(this, _args(arguments));
  1924. };
  1925. /**
  1926. * Retrieve event listeners for an `eventType`.
  1927. * If no `eventType` is provided, it will retrieve all listeners for every event type.
  1928. *
  1929. * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
  1930. */
  1931. ZeroClipboard.handlers = function() {
  1932. return _listeners.apply(this, _args(arguments));
  1933. };
  1934. /**
  1935. * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.
  1936. *
  1937. * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
  1938. * @static
  1939. */
  1940. ZeroClipboard.emit = function() {
  1941. return _emit.apply(this, _args(arguments));
  1942. };
  1943. /**
  1944. * Create and embed the Flash object.
  1945. *
  1946. * @returns The Flash object
  1947. * @static
  1948. */
  1949. ZeroClipboard.create = function() {
  1950. return _create.apply(this, _args(arguments));
  1951. };
  1952. /**
  1953. * Self-destruct and clean up everything, including the embedded Flash object.
  1954. *
  1955. * @returns `undefined`
  1956. * @static
  1957. */
  1958. ZeroClipboard.destroy = function() {
  1959. return _destroy.apply(this, _args(arguments));
  1960. };
  1961. /**
  1962. * Set the pending data for clipboard injection.
  1963. *
  1964. * @returns `undefined`
  1965. * @static
  1966. */
  1967. ZeroClipboard.setData = function() {
  1968. return _setData.apply(this, _args(arguments));
  1969. };
  1970. /**
  1971. * Clear the pending data for clipboard injection.
  1972. * If no `format` is provided, all pending data formats will be cleared.
  1973. *
  1974. * @returns `undefined`
  1975. * @static
  1976. */
  1977. ZeroClipboard.clearData = function() {
  1978. return _clearData.apply(this, _args(arguments));
  1979. };
  1980. /**
  1981. * Get a copy of the pending data for clipboard injection.
  1982. * If no `format` is provided, a copy of ALL pending data formats will be returned.
  1983. *
  1984. * @returns `String` or `Object`
  1985. * @static
  1986. */
  1987. ZeroClipboard.getData = function() {
  1988. return _getData.apply(this, _args(arguments));
  1989. };
  1990. /**
  1991. * Sets the current HTML object that the Flash object should overlay. This will put the global
  1992. * Flash object on top of the current element; depending on the setup, this may also set the
  1993. * pending clipboard text data as well as the Flash object's wrapping element's title attribute
  1994. * based on the underlying HTML element and ZeroClipboard configuration.
  1995. *
  1996. * @returns `undefined`
  1997. * @static
  1998. */
  1999. ZeroClipboard.focus = ZeroClipboard.activate = function() {
  2000. return _focus.apply(this, _args(arguments));
  2001. };
  2002. /**
  2003. * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on
  2004. * the setup, this may also unset the Flash object's wrapping element's title attribute based on
  2005. * the underlying HTML element and ZeroClipboard configuration.
  2006. *
  2007. * @returns `undefined`
  2008. * @static
  2009. */
  2010. ZeroClipboard.blur = ZeroClipboard.deactivate = function() {
  2011. return _blur.apply(this, _args(arguments));
  2012. };
  2013. /**
  2014. * Returns the currently focused/"activated" HTML element that the Flash object is wrapping.
  2015. *
  2016. * @returns `HTMLElement` or `null`
  2017. * @static
  2018. */
  2019. ZeroClipboard.activeElement = function() {
  2020. return _activeElement.apply(this, _args(arguments));
  2021. };
  2022. /**
  2023. * Keep track of the ZeroClipboard client instance counter.
  2024. */
  2025. var _clientIdCounter = 0;
  2026. /**
  2027. * Keep track of the state of the client instances.
  2028. *
  2029. * Entry structure:
  2030. * _clientMeta[client.id] = {
  2031. * instance: client,
  2032. * elements: [],
  2033. * handlers: {}
  2034. * };
  2035. */
  2036. var _clientMeta = {};
  2037. /**
  2038. * Keep track of the ZeroClipboard clipped elements counter.
  2039. */
  2040. var _elementIdCounter = 0;
  2041. /**
  2042. * Keep track of the state of the clipped element relationships to clients.
  2043. *
  2044. * Entry structure:
  2045. * _elementMeta[element.zcClippingId] = [client1.id, client2.id];
  2046. */
  2047. var _elementMeta = {};
  2048. /**
  2049. * Keep track of the state of the mouse event handlers for clipped elements.
  2050. *
  2051. * Entry structure:
  2052. * _mouseHandlers[element.zcClippingId] = {
  2053. * mouseover: function(event) {},
  2054. * mouseout: function(event) {},
  2055. * mouseenter: function(event) {},
  2056. * mouseleave: function(event) {},
  2057. * mousemove: function(event) {}
  2058. * };
  2059. */
  2060. var _mouseHandlers = {};
  2061. /**
  2062. * Extending the ZeroClipboard configuration defaults for the Client module.
  2063. */
  2064. _extend(_globalConfig, {
  2065. autoActivate: true
  2066. });
  2067. /**
  2068. * The real constructor for `ZeroClipboard` client instances.
  2069. * @private
  2070. */
  2071. var _clientConstructor = function(elements) {
  2072. var client = this;
  2073. client.id = "" + _clientIdCounter++;
  2074. _clientMeta[client.id] = {
  2075. instance: client,
  2076. elements: [],
  2077. handlers: {}
  2078. };
  2079. if (elements) {
  2080. client.clip(elements);
  2081. }
  2082. ZeroClipboard.on("*", function(event) {
  2083. return client.emit(event);
  2084. });
  2085. ZeroClipboard.on("destroy", function() {
  2086. client.destroy();
  2087. });
  2088. ZeroClipboard.create();
  2089. };
  2090. /**
  2091. * The underlying implementation of `ZeroClipboard.Client.prototype.on`.
  2092. * @private
  2093. */
  2094. var _clientOn = function(eventType, listener) {
  2095. var i, len, events, added = {}, meta = _clientMeta[this.id], handlers = meta && meta.handlers;
  2096. if (!meta) {
  2097. throw new Error("Attempted to add new listener(s) to a destroyed ZeroClipboard client instance");
  2098. }
  2099. if (typeof eventType === "string" && eventType) {
  2100. events = eventType.toLowerCase().split(/\s+/);
  2101. } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
  2102. for (i in eventType) {
  2103. if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
  2104. this.on(i, eventType[i]);
  2105. }
  2106. }
  2107. }
  2108. if (events && events.length) {
  2109. for (i = 0, len = events.length; i < len; i++) {
  2110. eventType = events[i].replace(/^on/, "");
  2111. added[eventType] = true;
  2112. if (!handlers[eventType]) {
  2113. handlers[eventType] = [];
  2114. }
  2115. handlers[eventType].push(listener);
  2116. }
  2117. if (added.ready && _flashState.ready) {
  2118. this.emit({
  2119. type: "ready",
  2120. client: this
  2121. });
  2122. }
  2123. if (added.error) {
  2124. for (i = 0, len = _flashStateErrorNames.length; i < len; i++) {
  2125. if (_flashState[_flashStateErrorNames[i].replace(/^flash-/, "")]) {
  2126. this.emit({
  2127. type: "error",
  2128. name: _flashStateErrorNames[i],
  2129. client: this
  2130. });
  2131. break;
  2132. }
  2133. }
  2134. if (_zcSwfVersion !== undefined && ZeroClipboard.version !== _zcSwfVersion) {
  2135. this.emit({
  2136. type: "error",
  2137. name: "version-mismatch",
  2138. jsVersion: ZeroClipboard.version,
  2139. swfVersion: _zcSwfVersion
  2140. });
  2141. }
  2142. }
  2143. }
  2144. return this;
  2145. };
  2146. /**
  2147. * The underlying implementation of `ZeroClipboard.Client.prototype.off`.
  2148. * @private
  2149. */
  2150. var _clientOff = function(eventType, listener) {
  2151. var i, len, foundIndex, events, perEventHandlers, meta = _clientMeta[this.id], handlers = meta && meta.handlers;
  2152. if (!handlers) {
  2153. return this;
  2154. }
  2155. if (arguments.length === 0) {
  2156. events = _keys(handlers);
  2157. } else if (typeof eventType === "string" && eventType) {
  2158. events = eventType.split(/\s+/);
  2159. } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
  2160. for (i in eventType) {
  2161. if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
  2162. this.off(i, eventType[i]);
  2163. }
  2164. }
  2165. }
  2166. if (events && events.length) {
  2167. for (i = 0, len = events.length; i < len; i++) {
  2168. eventType = events[i].toLowerCase().replace(/^on/, "");
  2169. perEventHandlers = handlers[eventType];
  2170. if (perEventHandlers && perEventHandlers.length) {
  2171. if (listener) {
  2172. foundIndex = perEventHandlers.indexOf(listener);
  2173. while (foundIndex !== -1) {
  2174. perEventHandlers.splice(foundIndex, 1);
  2175. foundIndex = perEventHandlers.indexOf(listener, foundIndex);
  2176. }
  2177. } else {
  2178. perEventHandlers.length = 0;
  2179. }
  2180. }
  2181. }
  2182. }
  2183. return this;
  2184. };
  2185. /**
  2186. * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.
  2187. * @private
  2188. */
  2189. var _clientListeners = function(eventType) {
  2190. var copy = null, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
  2191. if (handlers) {
  2192. if (typeof eventType === "string" && eventType) {
  2193. copy = handlers[eventType] ? handlers[eventType].slice(0) : [];
  2194. } else {
  2195. copy = _deepCopy(handlers);
  2196. }
  2197. }
  2198. return copy;
  2199. };
  2200. /**
  2201. * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.
  2202. * @private
  2203. */
  2204. var _clientEmit = function(event) {
  2205. if (_clientShouldEmit.call(this, event)) {
  2206. if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
  2207. event = _extend({}, event);
  2208. }
  2209. var eventCopy = _extend({}, _createEvent(event), {
  2210. client: this
  2211. });
  2212. _clientDispatchCallbacks.call(this, eventCopy);
  2213. }
  2214. return this;
  2215. };
  2216. /**
  2217. * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.
  2218. * @private
  2219. */
  2220. var _clientClip = function(elements) {
  2221. if (!_clientMeta[this.id]) {
  2222. throw new Error("Attempted to clip element(s) to a destroyed ZeroClipboard client instance");
  2223. }
  2224. elements = _prepClip(elements);
  2225. for (var i = 0; i < elements.length; i++) {
  2226. if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
  2227. if (!elements[i].zcClippingId) {
  2228. elements[i].zcClippingId = "zcClippingId_" + _elementIdCounter++;
  2229. _elementMeta[elements[i].zcClippingId] = [ this.id ];
  2230. if (_globalConfig.autoActivate === true) {
  2231. _addMouseHandlers(elements[i]);
  2232. }
  2233. } else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {
  2234. _elementMeta[elements[i].zcClippingId].push(this.id);
  2235. }
  2236. var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;
  2237. if (clippedElements.indexOf(elements[i]) === -1) {
  2238. clippedElements.push(elements[i]);
  2239. }
  2240. }
  2241. }
  2242. return this;
  2243. };
  2244. /**
  2245. * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.
  2246. * @private
  2247. */
  2248. var _clientUnclip = function(elements) {
  2249. var meta = _clientMeta[this.id];
  2250. if (!meta) {
  2251. return this;
  2252. }
  2253. var clippedElements = meta.elements;
  2254. var arrayIndex;
  2255. if (typeof elements === "undefined") {
  2256. elements = clippedElements.slice(0);
  2257. } else {
  2258. elements = _prepClip(elements);
  2259. }
  2260. for (var i = elements.length; i--; ) {
  2261. if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
  2262. arrayIndex = 0;
  2263. while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {
  2264. clippedElements.splice(arrayIndex, 1);
  2265. }
  2266. var clientIds = _elementMeta[elements[i].zcClippingId];
  2267. if (clientIds) {
  2268. arrayIndex = 0;
  2269. while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {
  2270. clientIds.splice(arrayIndex, 1);
  2271. }
  2272. if (clientIds.length === 0) {
  2273. if (_globalConfig.autoActivate === true) {
  2274. _removeMouseHandlers(elements[i]);
  2275. }
  2276. delete elements[i].zcClippingId;
  2277. }
  2278. }
  2279. }
  2280. }
  2281. return this;
  2282. };
  2283. /**
  2284. * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.
  2285. * @private
  2286. */
  2287. var _clientElements = function() {
  2288. var meta = _clientMeta[this.id];
  2289. return meta && meta.elements ? meta.elements.slice(0) : [];
  2290. };
  2291. /**
  2292. * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.
  2293. * @private
  2294. */
  2295. var _clientDestroy = function() {
  2296. if (!_clientMeta[this.id]) {
  2297. return;
  2298. }
  2299. this.unclip();
  2300. this.off();
  2301. delete _clientMeta[this.id];
  2302. };
  2303. /**
  2304. * Inspect an Event to see if the Client (`this`) should honor it for emission.
  2305. * @private
  2306. */
  2307. var _clientShouldEmit = function(event) {
  2308. if (!(event && event.type)) {
  2309. return false;
  2310. }
  2311. if (event.client && event.client !== this) {
  2312. return false;
  2313. }
  2314. var meta = _clientMeta[this.id];
  2315. var clippedEls = meta && meta.elements;
  2316. var hasClippedEls = !!clippedEls && clippedEls.length > 0;
  2317. var goodTarget = !event.target || hasClippedEls && clippedEls.indexOf(event.target) !== -1;
  2318. var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;
  2319. var goodClient = event.client && event.client === this;
  2320. if (!meta || !(goodTarget || goodRelTarget || goodClient)) {
  2321. return false;
  2322. }
  2323. return true;
  2324. };
  2325. /**
  2326. * Handle the actual dispatching of events to a client instance.
  2327. *
  2328. * @returns `undefined`
  2329. * @private
  2330. */
  2331. var _clientDispatchCallbacks = function(event) {
  2332. var meta = _clientMeta[this.id];
  2333. if (!(typeof event === "object" && event && event.type && meta)) {
  2334. return;
  2335. }
  2336. var async = _shouldPerformAsync(event);
  2337. var wildcardTypeHandlers = meta && meta.handlers["*"] || [];
  2338. var specificTypeHandlers = meta && meta.handlers[event.type] || [];
  2339. var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
  2340. if (handlers && handlers.length) {
  2341. var i, len, func, context, eventCopy, originalContext = this;
  2342. for (i = 0, len = handlers.length; i < len; i++) {
  2343. func = handlers[i];
  2344. context = originalContext;
  2345. if (typeof func === "string" && typeof _window[func] === "function") {
  2346. func = _window[func];
  2347. }
  2348. if (typeof func === "object" && func && typeof func.handleEvent === "function") {
  2349. context = func;
  2350. func = func.handleEvent;
  2351. }
  2352. if (typeof func === "function") {
  2353. eventCopy = _extend({}, event);
  2354. _dispatchCallback(func, context, [ eventCopy ], async);
  2355. }
  2356. }
  2357. }
  2358. };
  2359. /**
  2360. * Prepares the elements for clipping/unclipping.
  2361. *
  2362. * @returns An Array of elements.
  2363. * @private
  2364. */
  2365. var _prepClip = function(elements) {
  2366. if (typeof elements === "string") {
  2367. elements = [];
  2368. }
  2369. return typeof elements.length !== "number" ? [ elements ] : elements;
  2370. };
  2371. /**
  2372. * Add a `mouseover` handler function for a clipped element.
  2373. *
  2374. * @returns `undefined`
  2375. * @private
  2376. */
  2377. var _addMouseHandlers = function(element) {
  2378. if (!(element && element.nodeType === 1)) {
  2379. return;
  2380. }
  2381. var _suppressMouseEvents = function(event) {
  2382. if (!(event || (event = _window.event))) {
  2383. return;
  2384. }
  2385. if (event._source !== "js") {
  2386. event.stopImmediatePropagation();
  2387. event.preventDefault();
  2388. }
  2389. delete event._source;
  2390. };
  2391. var _elementMouseOver = function(event) {
  2392. if (!(event || (event = _window.event))) {
  2393. return;
  2394. }
  2395. _suppressMouseEvents(event);
  2396. ZeroClipboard.focus(element);
  2397. };
  2398. element.addEventListener("mouseover", _elementMouseOver, false);
  2399. element.addEventListener("mouseout", _suppressMouseEvents, false);
  2400. element.addEventListener("mouseenter", _suppressMouseEvents, false);
  2401. element.addEventListener("mouseleave", _suppressMouseEvents, false);
  2402. element.addEventListener("mousemove", _suppressMouseEvents, false);
  2403. _mouseHandlers[element.zcClippingId] = {
  2404. mouseover: _elementMouseOver,
  2405. mouseout: _suppressMouseEvents,
  2406. mouseenter: _suppressMouseEvents,
  2407. mouseleave: _suppressMouseEvents,
  2408. mousemove: _suppressMouseEvents
  2409. };
  2410. };
  2411. /**
  2412. * Remove a `mouseover` handler function for a clipped element.
  2413. *
  2414. * @returns `undefined`
  2415. * @private
  2416. */
  2417. var _removeMouseHandlers = function(element) {
  2418. if (!(element && element.nodeType === 1)) {
  2419. return;
  2420. }
  2421. var mouseHandlers = _mouseHandlers[element.zcClippingId];
  2422. if (!(typeof mouseHandlers === "object" && mouseHandlers)) {
  2423. return;
  2424. }
  2425. var key, val, mouseEvents = [ "move", "leave", "enter", "out", "over" ];
  2426. for (var i = 0, len = mouseEvents.length; i < len; i++) {
  2427. key = "mouse" + mouseEvents[i];
  2428. val = mouseHandlers[key];
  2429. if (typeof val === "function") {
  2430. element.removeEventListener(key, val, false);
  2431. }
  2432. }
  2433. delete _mouseHandlers[element.zcClippingId];
  2434. };
  2435. /**
  2436. * Creates a new ZeroClipboard client instance.
  2437. * Optionally, auto-`clip` an element or collection of elements.
  2438. *
  2439. * @constructor
  2440. */
  2441. ZeroClipboard._createClient = function() {
  2442. _clientConstructor.apply(this, _args(arguments));
  2443. };
  2444. /**
  2445. * Register an event listener to the client.
  2446. *
  2447. * @returns `this`
  2448. */
  2449. ZeroClipboard.prototype.on = function() {
  2450. return _clientOn.apply(this, _args(arguments));
  2451. };
  2452. /**
  2453. * Unregister an event handler from the client.
  2454. * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`.
  2455. * If no `eventType` is provided, it will unregister all handlers for every event type.
  2456. *
  2457. * @returns `this`
  2458. */
  2459. ZeroClipboard.prototype.off = function() {
  2460. return _clientOff.apply(this, _args(arguments));
  2461. };
  2462. /**
  2463. * Retrieve event listeners for an `eventType` from the client.
  2464. * If no `eventType` is provided, it will retrieve all listeners for every event type.
  2465. *
  2466. * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
  2467. */
  2468. ZeroClipboard.prototype.handlers = function() {
  2469. return _clientListeners.apply(this, _args(arguments));
  2470. };
  2471. /**
  2472. * Event emission receiver from the Flash object for this client's registered JavaScript event listeners.
  2473. *
  2474. * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
  2475. */
  2476. ZeroClipboard.prototype.emit = function() {
  2477. return _clientEmit.apply(this, _args(arguments));
  2478. };
  2479. /**
  2480. * Register clipboard actions for new element(s) to the client.
  2481. *
  2482. * @returns `this`
  2483. */
  2484. ZeroClipboard.prototype.clip = function() {
  2485. return _clientClip.apply(this, _args(arguments));
  2486. };
  2487. /**
  2488. * Unregister the clipboard actions of previously registered element(s) on the page.
  2489. * If no elements are provided, ALL registered elements will be unregistered.
  2490. *
  2491. * @returns `this`
  2492. */
  2493. ZeroClipboard.prototype.unclip = function() {
  2494. return _clientUnclip.apply(this, _args(arguments));
  2495. };
  2496. /**
  2497. * Get all of the elements to which this client is clipped.
  2498. *
  2499. * @returns array of clipped elements
  2500. */
  2501. ZeroClipboard.prototype.elements = function() {
  2502. return _clientElements.apply(this, _args(arguments));
  2503. };
  2504. /**
  2505. * Self-destruct and clean up everything for a single client.
  2506. * This will NOT destroy the embedded Flash object.
  2507. *
  2508. * @returns `undefined`
  2509. */
  2510. ZeroClipboard.prototype.destroy = function() {
  2511. return _clientDestroy.apply(this, _args(arguments));
  2512. };
  2513. /**
  2514. * Stores the pending plain text to inject into the clipboard.
  2515. *
  2516. * @returns `this`
  2517. */
  2518. ZeroClipboard.prototype.setText = function(text) {
  2519. if (!_clientMeta[this.id]) {
  2520. throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");
  2521. }
  2522. ZeroClipboard.setData("text/plain", text);
  2523. return this;
  2524. };
  2525. /**
  2526. * Stores the pending HTML text to inject into the clipboard.
  2527. *
  2528. * @returns `this`
  2529. */
  2530. ZeroClipboard.prototype.setHtml = function(html) {
  2531. if (!_clientMeta[this.id]) {
  2532. throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");
  2533. }
  2534. ZeroClipboard.setData("text/html", html);
  2535. return this;
  2536. };
  2537. /**
  2538. * Stores the pending rich text (RTF) to inject into the clipboard.
  2539. *
  2540. * @returns `this`
  2541. */
  2542. ZeroClipboard.prototype.setRichText = function(richText) {
  2543. if (!_clientMeta[this.id]) {
  2544. throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");
  2545. }
  2546. ZeroClipboard.setData("application/rtf", richText);
  2547. return this;
  2548. };
  2549. /**
  2550. * Stores the pending data to inject into the clipboard.
  2551. *
  2552. * @returns `this`
  2553. */
  2554. ZeroClipboard.prototype.setData = function() {
  2555. if (!_clientMeta[this.id]) {
  2556. throw new Error("Attempted to set pending clipboard data from a destroyed ZeroClipboard client instance");
  2557. }
  2558. ZeroClipboard.setData.apply(this, _args(arguments));
  2559. return this;
  2560. };
  2561. /**
  2562. * Clears the pending data to inject into the clipboard.
  2563. * If no `format` is provided, all pending data formats will be cleared.
  2564. *
  2565. * @returns `this`
  2566. */
  2567. ZeroClipboard.prototype.clearData = function() {
  2568. if (!_clientMeta[this.id]) {
  2569. throw new Error("Attempted to clear pending clipboard data from a destroyed ZeroClipboard client instance");
  2570. }
  2571. ZeroClipboard.clearData.apply(this, _args(arguments));
  2572. return this;
  2573. };
  2574. /**
  2575. * Gets a copy of the pending data to inject into the clipboard.
  2576. * If no `format` is provided, a copy of ALL pending data formats will be returned.
  2577. *
  2578. * @returns `String` or `Object`
  2579. */
  2580. ZeroClipboard.prototype.getData = function() {
  2581. if (!_clientMeta[this.id]) {
  2582. throw new Error("Attempted to get pending clipboard data from a destroyed ZeroClipboard client instance");
  2583. }
  2584. return ZeroClipboard.getData.apply(this, _args(arguments));
  2585. };
  2586. if (typeof define === "function" && define.amd) {
  2587. define(function() {
  2588. return ZeroClipboard;
  2589. });
  2590. } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) {
  2591. module.exports = ZeroClipboard;
  2592. } else {
  2593. window.ZeroClipboard = ZeroClipboard;
  2594. }
  2595. })(function() {
  2596. return this || window;
  2597. }());