smithycms 0.5.1 → 0.5.2

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