+
+
\ No newline at end of file
diff --git a/showcase/includes/accessibility.html b/showcase/includes/accessibility.html
new file mode 100644
index 0000000..d6c791e
--- /dev/null
+++ b/showcase/includes/accessibility.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
Semantics & Accessibility
+
+
+
+
+ Buttons should be used responsibly with accessibility in mind. You should use anchor tags when you are linking to an external resource or an internal page link. Use the button tag when you have an application command such as add to shopping cart. Use input tags (such as the submit input type) when creating forms. If you would like to learn more on this subject we highly reccomend reading the following articles.
\ No newline at end of file
diff --git a/showcase/includes/block.html b/showcase/includes/block.html
new file mode 100644
index 0000000..d8b6d20
--- /dev/null
+++ b/showcase/includes/block.html
@@ -0,0 +1,15 @@
+
+
+
+
Stacked Buttons
+
Block level buttons that expand the width of their container
+
\ No newline at end of file
diff --git a/showcase/includes/border.html b/showcase/includes/border.html
new file mode 100644
index 0000000..d35478c
--- /dev/null
+++ b/showcase/includes/border.html
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
Border & Borderless Buttons
+
+ Icons provided by Font Awesome Great for mobile devices
+
+
\ No newline at end of file
diff --git a/showcase/includes/glowing.html b/showcase/includes/glowing.html
new file mode 100644
index 0000000..22e2eee
--- /dev/null
+++ b/showcase/includes/glowing.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ U
+ A Unicorn UI Module
+ www.unicorn-ui.com
+
+
diff --git a/showcase/includes/longshadow.html b/showcase/includes/longshadow.html
new file mode 100644
index 0000000..096af7f
--- /dev/null
+++ b/showcase/includes/longshadow.html
@@ -0,0 +1,27 @@
+
+
+
Long Shadows
+
A visual effect adding a flat shadow to the text of a button
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/showcase/includes/nav.html b/showcase/includes/nav.html
new file mode 100644
index 0000000..43c1827
--- /dev/null
+++ b/showcase/includes/nav.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/showcase/includes/promo.html b/showcase/includes/promo.html
new file mode 100644
index 0000000..4c83087
--- /dev/null
+++ b/showcase/includes/promo.html
@@ -0,0 +1,39 @@
+
\ No newline at end of file
diff --git a/showcase/includes/raised.html b/showcase/includes/raised.html
new file mode 100644
index 0000000..ab45fe9
--- /dev/null
+++ b/showcase/includes/raised.html
@@ -0,0 +1,17 @@
+
+
+
+
Raised Buttons
+
A classic looking button that offers great depth and affordance
+
\ No newline at end of file
diff --git a/showcase/includes/setup.html b/showcase/includes/setup.html
new file mode 100644
index 0000000..7437c3e
--- /dev/null
+++ b/showcase/includes/setup.html
@@ -0,0 +1,47 @@
+
+
+
+
+<!-- Buttons core css -->
+<link rel="stylesheet" href="css/buttons.css">
+
+<!-- Only needed if you want support for dropdown menus -->
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+<script type="text/javascript" src="js/buttons.js"></script>
+
+<!-- Only needed if you want font icons -->
+<link href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css" rel="stylesheet">
+
\ No newline at end of file
diff --git a/showcase/includes/templates.html b/showcase/includes/templates.html
new file mode 100644
index 0000000..2cca797
--- /dev/null
+++ b/showcase/includes/templates.html
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/showcase/includes/text.html b/showcase/includes/text.html
new file mode 100644
index 0000000..ea887bd
--- /dev/null
+++ b/showcase/includes/text.html
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/showcase/includes/transition-guide.html b/showcase/includes/transition-guide.html
new file mode 100644
index 0000000..c7699f0
--- /dev/null
+++ b/showcase/includes/transition-guide.html
@@ -0,0 +1,15 @@
+
+
+
+
Transition Guide 1.0 -> 2.0
+
+
+
We've made some major improvements to the Buttons library. In order to integrate buttons into your current project you'll need to make the following changes:
+
+
+
+
Compass has been replaced with [autoprefixer](https://github.com/postcss/autoprefixer). Compass is not recommended but it is still supported.
+
Button colors are now complete independent (ex. button-primary) we no longer have classes like button-flat-primary to achieve this you now simply add button-flat button-primary
+
Buttons styles are now independent (ex. button-flat, button-3d, etc.). You can apply these styles and they will automatically pick up the color attached to the button (ex. button-primary button-3d)
+
+
\ No newline at end of file
diff --git a/showcase/includes/unicorn-nav.html b/showcase/includes/unicorn-nav.html
new file mode 100644
index 0000000..9681799
--- /dev/null
+++ b/showcase/includes/unicorn-nav.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/showcase/includes/wrapper.html b/showcase/includes/wrapper.html
new file mode 100644
index 0000000..538ca19
--- /dev/null
+++ b/showcase/includes/wrapper.html
@@ -0,0 +1,28 @@
+
+
+
+
Button Wrappers
+
A wrap around effect to highlight the shape of the button and offer a subtle visual effect
+ Buttons should be used responsibly with accessibility in mind. You should use anchor tags when you are linking to an external resource or an internal page link. Use the button tag when you have an application command such as add to shopping cart. Use input tags (such as the submit input type) when creating forms. If you would like to learn more on this subject we highly reccomend reading the following articles.
+ <!-- Buttons core css -->
+ <link rel="stylesheet" href="css/buttons.css">
+
+ <!-- Only needed if you want support for dropdown menus -->
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+ <script type="text/javascript" src="js/buttons.js"></script>
+
+ <!-- Only needed if you want font icons -->
+ <link href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css" rel="stylesheet">
+
+
+
+
+
+
+
+
+
Transition Guide 1.0 -> 2.0
+
+
+
We've made some major improvements to the Buttons library. In order to integrate buttons into your current project you'll need to make the following changes:
+
+
+
+
Compass has been replaced with [autoprefixer](https://github.com/postcss/autoprefixer). Compass is not recommended but it is still supported.
+
Button colors are now complete independent (ex. button-primary) we no longer have classes like button-flat-primary to achieve this you now simply add button-flat button-primary
+
Buttons styles are now independent (ex. button-flat, button-3d, etc.). You can apply these styles and they will automatically pick up the color attached to the button (ex. button-primary button-3d)
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
+},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("
+
+
+
+```
+
+```js
+// main.js
+var client = new ZeroClipboard( document.getElementById("copy-button") );
+
+client.on( "ready", function( readyEvent ) {
+ // alert( "ZeroClipboard SWF is ready!" );
+
+ client.on( "aftercopy", function( event ) {
+ // `this` === `client`
+ // `event.target` === the element that was clicked
+ event.target.style.display = "none";
+ alert("Copied text to clipboard: " + event.data["text/plain"] );
+ } );
+} );
+```
+
+See [docs/instructions.md](docs/instructions.md) for more advanced options in using the library on your site.
+See [docs/api/ZeroClipboard.md](docs/api/ZeroClipboard.md) for the complete API documentation.
+
+Here is a working [test page](http://zeroclipboard.org/#demo) where you can try out ZeroClipboard in your browser.
+
+
+## Testing ZeroClipboard Locally
+
+To test the page [demo page](http://zeroclipboard.org/#demo) locally, clone the [website repo](https://github.com/zeroclipboard/zeroclipboard.org).
+
+
+## Support
+
+This library is fully compatible with Flash Player 11.0.0 and above, which requires
+that the clipboard copy operation be initiated by a user click event inside the
+Flash movie. This is achieved by automatically floating the invisible movie on top
+of a [DOM](http://en.wikipedia.org/wiki/Document_Object_Model) element of your
+choice. Standard mouse events are even propagated out to your DOM element, so you
+can still have rollover and mousedown effects with just a _little_ extra effort.
+
+ZeroClipboard `v2.x` is expected to work in IE9+ and all of the evergreen browsers.
+
+
+## Contributing
+
+see [CONTRIBUTING.md](CONTRIBUTING.md)
+
+
+## Releases
+
+Starting with version [1.1.7](https://github.com/zeroclipboard/zeroclipboard/releases/tag/v1.1.7), ZeroClipboard uses [semantic versioning](http://semver.org/).
+
+see [releases](https://github.com/zeroclipboard/zeroclipboard/releases)
+
+
+## Roadmap
+
+see [roadmap.md](docs/roadmap.md)
diff --git a/showcase/js/zeroclipboard/bower.json b/showcase/js/zeroclipboard/bower.json
new file mode 100755
index 0000000..0196886
--- /dev/null
+++ b/showcase/js/zeroclipboard/bower.json
@@ -0,0 +1,17 @@
+{
+ "name": "zeroclipboard",
+ "description": "The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.",
+ "version": "2.1.6",
+ "main": ["./dist/ZeroClipboard.js", "./dist/ZeroClipboard.swf"],
+ "keywords": ["flash","clipboard","copy","cut","paste","zclip","clip","clippy"],
+ "license": "https://github.com/zeroclipboard/zeroclipboard/blob/master/LICENSE",
+ "authors": [{"name":"Jon Rohan","url":"http://jonrohan.me/"},{"name":"James M. Greene","email":"james.m.greene@gmail.com","url":"http://jamesgreene.net/"}],
+ "homepage": "http://zeroclipboard.org/",
+ "repository": {"type":"git","url":"https://github.com/zeroclipboard/zeroclipboard.git"},
+ "location": "git://github.com/zeroclipboard/zeroclipboard.git",
+ "ignore": [
+ "*",
+ "!/bower.json",
+ "!/dist/**"
+ ]
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/composer.json b/showcase/js/zeroclipboard/composer.json
new file mode 100755
index 0000000..8e6ed0b
--- /dev/null
+++ b/showcase/js/zeroclipboard/composer.json
@@ -0,0 +1,14 @@
+{
+ "name": "zeroclipboard/zeroclipboard",
+ "description": "The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.",
+ "version": "2.1.6",
+ "type": "library",
+ "keywords": ["flash","clipboard","copy","cut","paste","zclip","clip","clippy"],
+ "license": "MIT",
+ "authors": [{"name":"Jon Rohan","homepage":"http://jonrohan.me/","role":"Developer"},{"name":"James M. Greene","email":"james.m.greene@gmail.com","homepage":"http://jamesgreene.net/","role":"Developer"}],
+ "homepage": "http://zeroclipboard.org/",
+ "support": {
+ "source": "https://github.com/zeroclipboard/zeroclipboard.git",
+ "issues": "https://github.com/zeroclipboard/zeroclipboard/issues"
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/.jshintrc b/showcase/js/zeroclipboard/dist/.jshintrc
new file mode 100755
index 0000000..15359aa
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/.jshintrc
@@ -0,0 +1,70 @@
+{
+ /* Enforcing options */
+ "bitwise": true,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "es3": true,
+ "es5": false,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nonbsp": true,
+ "nonew": true,
+ "plusplus": false,
+ "quotmark": "double",
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "trailing": true,
+ "maxparams": 4,
+ "maxdepth": 5,
+ "maxstatements": false,
+ "maxlen": false, /* IDEAL: 120? */
+
+
+ /* Relaxing options */
+ "asi": false,
+ "boss": false,
+ "debug": false,
+ "eqnull": true,
+ "esnext": false,
+ "evil": false,
+ "expr": false,
+ "funcscope": false,
+ "gcl": false,
+ "globalstrict": false,
+ "iterator": false,
+ "lastsemic": false,
+ "laxbreak": false,
+ "laxcomma": false,
+ "loopfunc": false,
+ "maxerr": 50,
+ "moz": false,
+ "multistr": false,
+ "notypeof": false,
+ "proto": false,
+ "scripturl": false,
+ "smarttabs": false,
+ "shadow": false,
+ "sub": false,
+ "supernew": false,
+ "validthis": false,
+ "noyield": false,
+
+ /* Environments */
+ "browser": true,
+
+ /* Global variables */
+ "globals": {
+ /* AMD */
+ "define": false,
+ /* CommonJS */
+ "module": false
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.js b/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.js
new file mode 100755
index 0000000..b5f3015
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.js
@@ -0,0 +1,1788 @@
+/*!
+ * ZeroClipboard
+ * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
+ * Copyright (c) 2014 Jon Rohan, James M. Greene
+ * Licensed MIT
+ * http://zeroclipboard.org/
+ * v2.1.6
+ */
+(function(window, undefined) {
+ "use strict";
+ /**
+ * Store references to critically important global functions that may be
+ * overridden on certain web pages.
+ */
+ var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _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, _round = _window.Math.round, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() {
+ var unwrapper = function(el) {
+ return el;
+ };
+ if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") {
+ try {
+ var div = _document.createElement("div");
+ var unwrappedDiv = _window.unwrap(div);
+ if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {
+ unwrapper = _window.unwrap;
+ }
+ } catch (e) {}
+ }
+ return unwrapper;
+ }();
+ /**
+ * Convert an `arguments` object into an Array.
+ *
+ * @returns The arguments as an Array
+ * @private
+ */
+ var _args = function(argumentsObj) {
+ return _slice.call(argumentsObj, 0);
+ };
+ /**
+ * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.
+ *
+ * @returns The target object, augmented
+ * @private
+ */
+ var _extend = function() {
+ var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {};
+ for (i = 1, len = args.length; i < len; i++) {
+ if ((arg = args[i]) != null) {
+ for (prop in arg) {
+ if (_hasOwn.call(arg, prop)) {
+ src = target[prop];
+ copy = arg[prop];
+ if (target !== copy && copy !== undefined) {
+ target[prop] = copy;
+ }
+ }
+ }
+ }
+ }
+ return target;
+ };
+ /**
+ * Return a deep copy of the source object or array.
+ *
+ * @returns Object or Array
+ * @private
+ */
+ var _deepCopy = function(source) {
+ var copy, i, len, prop;
+ if (typeof source !== "object" || source == null) {
+ copy = source;
+ } else if (typeof source.length === "number") {
+ copy = [];
+ for (i = 0, len = source.length; i < len; i++) {
+ if (_hasOwn.call(source, i)) {
+ copy[i] = _deepCopy(source[i]);
+ }
+ }
+ } else {
+ copy = {};
+ for (prop in source) {
+ if (_hasOwn.call(source, prop)) {
+ copy[prop] = _deepCopy(source[prop]);
+ }
+ }
+ }
+ return copy;
+ };
+ /**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.
+ * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to
+ * be kept.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+ var _pick = function(obj, keys) {
+ var newObj = {};
+ for (var i = 0, len = keys.length; i < len; i++) {
+ if (keys[i] in obj) {
+ newObj[keys[i]] = obj[keys[i]];
+ }
+ }
+ return newObj;
+ };
+ /**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.
+ * The inverse of `_pick`.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+ var _omit = function(obj, keys) {
+ var newObj = {};
+ for (var prop in obj) {
+ if (keys.indexOf(prop) === -1) {
+ newObj[prop] = obj[prop];
+ }
+ }
+ return newObj;
+ };
+ /**
+ * Remove all owned, enumerable properties from an object.
+ *
+ * @returns The original object without its owned, enumerable properties.
+ * @private
+ */
+ var _deleteOwnProperties = function(obj) {
+ if (obj) {
+ for (var prop in obj) {
+ if (_hasOwn.call(obj, prop)) {
+ delete obj[prop];
+ }
+ }
+ }
+ return obj;
+ };
+ /**
+ * Determine if an element is contained within another element.
+ *
+ * @returns Boolean
+ * @private
+ */
+ var _containedBy = function(el, ancestorEl) {
+ 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)) {
+ do {
+ if (el === ancestorEl) {
+ return true;
+ }
+ el = el.parentNode;
+ } while (el);
+ }
+ return false;
+ };
+ /**
+ * Get the URL path's parent directory.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getDirPathOfUrl = function(url) {
+ var dir;
+ if (typeof url === "string" && url) {
+ dir = url.split("#")[0].split("?")[0];
+ dir = url.slice(0, url.lastIndexOf("/") + 1);
+ }
+ return dir;
+ };
+ /**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrlFromErrorStack = function(stack) {
+ var url, matches;
+ if (typeof stack === "string" && stack) {
+ matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ } else {
+ matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ }
+ }
+ }
+ return url;
+ };
+ /**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrlFromError = function() {
+ var url, err;
+ try {
+ throw new _Error();
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);
+ }
+ return url;
+ };
+ /**
+ * Get the current script's URL.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrl = function() {
+ var jsPath, scripts, i;
+ if (_document.currentScript && (jsPath = _document.currentScript.src)) {
+ return jsPath;
+ }
+ scripts = _document.getElementsByTagName("script");
+ if (scripts.length === 1) {
+ return scripts[0].src || undefined;
+ }
+ if ("readyState" in scripts[0]) {
+ for (i = scripts.length; i--; ) {
+ if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) {
+ return jsPath;
+ }
+ }
+ }
+ if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) {
+ return jsPath;
+ }
+ if (jsPath = _getCurrentScriptUrlFromError()) {
+ return jsPath;
+ }
+ return undefined;
+ };
+ /**
+ * Get the unanimous parent directory of ALL script tags.
+ * If any script tags are either (a) inline or (b) from differing parent
+ * directories, this method must return `undefined`.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getUnanimousScriptParentDir = function() {
+ var i, jsDir, jsPath, scripts = _document.getElementsByTagName("script");
+ for (i = scripts.length; i--; ) {
+ if (!(jsPath = scripts[i].src)) {
+ jsDir = null;
+ break;
+ }
+ jsPath = _getDirPathOfUrl(jsPath);
+ if (jsDir == null) {
+ jsDir = jsPath;
+ } else if (jsDir !== jsPath) {
+ jsDir = null;
+ break;
+ }
+ }
+ return jsDir || undefined;
+ };
+ /**
+ * Get the presumed location of the "ZeroClipboard.swf" file, based on the location
+ * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.).
+ *
+ * @returns String
+ * @private
+ */
+ var _getDefaultSwfPath = function() {
+ var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || "";
+ return jsDir + "ZeroClipboard.swf";
+ };
+ /**
+ * Keep track of the state of the Flash object.
+ * @private
+ */
+ var _flashState = {
+ bridge: null,
+ version: "0.0.0",
+ pluginType: "unknown",
+ disabled: null,
+ outdated: null,
+ unavailable: null,
+ deactivated: null,
+ overdue: null,
+ ready: null
+ };
+ /**
+ * The minimum Flash Player version required to use ZeroClipboard completely.
+ * @readonly
+ * @private
+ */
+ var _minimumFlashVersion = "11.0.0";
+ /**
+ * Keep track of all event listener registrations.
+ * @private
+ */
+ var _handlers = {};
+ /**
+ * Keep track of the currently activated element.
+ * @private
+ */
+ var _currentElement;
+ /**
+ * Keep track of the element that was activated when a `copy` process started.
+ * @private
+ */
+ var _copyTarget;
+ /**
+ * Keep track of data for the pending clipboard transaction.
+ * @private
+ */
+ var _clipData = {};
+ /**
+ * Keep track of data formats for the pending clipboard transaction.
+ * @private
+ */
+ var _clipDataFormatMap = null;
+ /**
+ * The `message` store for events
+ * @private
+ */
+ var _eventMessages = {
+ ready: "Flash communication is established",
+ error: {
+ "flash-disabled": "Flash is disabled or not installed",
+ "flash-outdated": "Flash is too outdated to support ZeroClipboard",
+ "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript",
+ "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate",
+ "flash-overdue": "Flash communication was established but NOT within the acceptable time limit"
+ }
+ };
+ /**
+ * ZeroClipboard configuration defaults for the Core module.
+ * @private
+ */
+ var _globalConfig = {
+ swfPath: _getDefaultSwfPath(),
+ trustedDomains: window.location.host ? [ window.location.host ] : [],
+ cacheBust: true,
+ forceEnhancedClipboard: false,
+ flashLoadTimeout: 3e4,
+ autoActivate: true,
+ bubbleEvents: true,
+ containerId: "global-zeroclipboard-html-bridge",
+ containerClass: "global-zeroclipboard-container",
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+ hoverClass: "zeroclipboard-is-hover",
+ activeClass: "zeroclipboard-is-active",
+ forceHandCursor: false,
+ title: null,
+ zIndex: 999999999
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.config`.
+ * @private
+ */
+ var _config = function(options) {
+ if (typeof options === "object" && options !== null) {
+ for (var prop in options) {
+ if (_hasOwn.call(options, prop)) {
+ if (/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(prop)) {
+ _globalConfig[prop] = options[prop];
+ } else if (_flashState.bridge == null) {
+ if (prop === "containerId" || prop === "swfObjectId") {
+ if (_isValidHtml4Id(options[prop])) {
+ _globalConfig[prop] = options[prop];
+ } else {
+ throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID");
+ }
+ } else {
+ _globalConfig[prop] = options[prop];
+ }
+ }
+ }
+ }
+ }
+ if (typeof options === "string" && options) {
+ if (_hasOwn.call(_globalConfig, options)) {
+ return _globalConfig[options];
+ }
+ return;
+ }
+ return _deepCopy(_globalConfig);
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.state`.
+ * @private
+ */
+ var _state = function() {
+ return {
+ browser: _pick(_navigator, [ "userAgent", "platform", "appName" ]),
+ flash: _omit(_flashState, [ "bridge" ]),
+ zeroclipboard: {
+ version: ZeroClipboard.version,
+ config: ZeroClipboard.config()
+ }
+ };
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.isFlashUnusable`.
+ * @private
+ */
+ var _isFlashUnusable = function() {
+ return !!(_flashState.disabled || _flashState.outdated || _flashState.unavailable || _flashState.deactivated);
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.on`.
+ * @private
+ */
+ var _on = function(eventType, listener) {
+ var i, len, events, added = {};
+ if (typeof eventType === "string" && eventType) {
+ events = eventType.toLowerCase().split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.on(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].replace(/^on/, "");
+ added[eventType] = true;
+ if (!_handlers[eventType]) {
+ _handlers[eventType] = [];
+ }
+ _handlers[eventType].push(listener);
+ }
+ if (added.ready && _flashState.ready) {
+ ZeroClipboard.emit({
+ type: "ready"
+ });
+ }
+ if (added.error) {
+ var errorTypes = [ "disabled", "outdated", "unavailable", "deactivated", "overdue" ];
+ for (i = 0, len = errorTypes.length; i < len; i++) {
+ if (_flashState[errorTypes[i]] === true) {
+ ZeroClipboard.emit({
+ type: "error",
+ name: "flash-" + errorTypes[i]
+ });
+ break;
+ }
+ }
+ }
+ }
+ return ZeroClipboard;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.off`.
+ * @private
+ */
+ var _off = function(eventType, listener) {
+ var i, len, foundIndex, events, perEventHandlers;
+ if (arguments.length === 0) {
+ events = _keys(_handlers);
+ } else if (typeof eventType === "string" && eventType) {
+ events = eventType.split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.off(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].toLowerCase().replace(/^on/, "");
+ perEventHandlers = _handlers[eventType];
+ if (perEventHandlers && perEventHandlers.length) {
+ if (listener) {
+ foundIndex = perEventHandlers.indexOf(listener);
+ while (foundIndex !== -1) {
+ perEventHandlers.splice(foundIndex, 1);
+ foundIndex = perEventHandlers.indexOf(listener, foundIndex);
+ }
+ } else {
+ perEventHandlers.length = 0;
+ }
+ }
+ }
+ }
+ return ZeroClipboard;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.handlers`.
+ * @private
+ */
+ var _listeners = function(eventType) {
+ var copy;
+ if (typeof eventType === "string" && eventType) {
+ copy = _deepCopy(_handlers[eventType]) || null;
+ } else {
+ copy = _deepCopy(_handlers);
+ }
+ return copy;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.emit`.
+ * @private
+ */
+ var _emit = function(event) {
+ var eventCopy, returnVal, tmp;
+ event = _createEvent(event);
+ if (!event) {
+ return;
+ }
+ if (_preprocessEvent(event)) {
+ return;
+ }
+ if (event.type === "ready" && _flashState.overdue === true) {
+ return ZeroClipboard.emit({
+ type: "error",
+ name: "flash-overdue"
+ });
+ }
+ eventCopy = _extend({}, event);
+ _dispatchCallbacks.call(this, eventCopy);
+ if (event.type === "copy") {
+ tmp = _mapClipDataToFlash(_clipData);
+ returnVal = tmp.data;
+ _clipDataFormatMap = tmp.formatMap;
+ }
+ return returnVal;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.create`.
+ * @private
+ */
+ var _create = function() {
+ if (typeof _flashState.ready !== "boolean") {
+ _flashState.ready = false;
+ }
+ if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {
+ var maxWait = _globalConfig.flashLoadTimeout;
+ if (typeof maxWait === "number" && maxWait >= 0) {
+ _setTimeout(function() {
+ if (typeof _flashState.deactivated !== "boolean") {
+ _flashState.deactivated = true;
+ }
+ if (_flashState.deactivated === true) {
+ ZeroClipboard.emit({
+ type: "error",
+ name: "flash-deactivated"
+ });
+ }
+ }, maxWait);
+ }
+ _flashState.overdue = false;
+ _embedSwf();
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.destroy`.
+ * @private
+ */
+ var _destroy = function() {
+ ZeroClipboard.clearData();
+ ZeroClipboard.blur();
+ ZeroClipboard.emit("destroy");
+ _unembedSwf();
+ ZeroClipboard.off();
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.setData`.
+ * @private
+ */
+ var _setData = function(format, data) {
+ var dataObj;
+ if (typeof format === "object" && format && typeof data === "undefined") {
+ dataObj = format;
+ ZeroClipboard.clearData();
+ } else if (typeof format === "string" && format) {
+ dataObj = {};
+ dataObj[format] = data;
+ } else {
+ return;
+ }
+ for (var dataFormat in dataObj) {
+ if (typeof dataFormat === "string" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]) {
+ _clipData[dataFormat] = dataObj[dataFormat];
+ }
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.clearData`.
+ * @private
+ */
+ var _clearData = function(format) {
+ if (typeof format === "undefined") {
+ _deleteOwnProperties(_clipData);
+ _clipDataFormatMap = null;
+ } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ delete _clipData[format];
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.getData`.
+ * @private
+ */
+ var _getData = function(format) {
+ if (typeof format === "undefined") {
+ return _deepCopy(_clipData);
+ } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ return _clipData[format];
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.
+ * @private
+ */
+ var _focus = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.activeClass);
+ if (_currentElement !== element) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ }
+ }
+ _currentElement = element;
+ _addClass(element, _globalConfig.hoverClass);
+ var newTitle = element.getAttribute("title") || _globalConfig.title;
+ if (typeof newTitle === "string" && newTitle) {
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.setAttribute("title", newTitle);
+ }
+ }
+ var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer";
+ _setHandCursor(useHandCursor);
+ _reposition();
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.
+ * @private
+ */
+ var _blur = function() {
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.removeAttribute("title");
+ htmlBridge.style.left = "0px";
+ htmlBridge.style.top = "-9999px";
+ htmlBridge.style.width = "1px";
+ htmlBridge.style.top = "1px";
+ }
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ _removeClass(_currentElement, _globalConfig.activeClass);
+ _currentElement = null;
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.activeElement`.
+ * @private
+ */
+ var _activeElement = function() {
+ return _currentElement || null;
+ };
+ /**
+ * Check if a value is a valid HTML4 `ID` or `Name` token.
+ * @private
+ */
+ var _isValidHtml4Id = function(id) {
+ return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id);
+ };
+ /**
+ * Create or update an `event` object, based on the `eventType`.
+ * @private
+ */
+ var _createEvent = function(event) {
+ var eventType;
+ if (typeof event === "string" && event) {
+ eventType = event;
+ event = {};
+ } else if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
+ eventType = event.type;
+ }
+ if (!eventType) {
+ return;
+ }
+ if (!event.target && /^(copy|aftercopy|_click)$/.test(eventType.toLowerCase())) {
+ event.target = _copyTarget;
+ }
+ _extend(event, {
+ type: eventType.toLowerCase(),
+ target: event.target || _currentElement || null,
+ relatedTarget: event.relatedTarget || null,
+ currentTarget: _flashState && _flashState.bridge || null,
+ timeStamp: event.timeStamp || _now() || null
+ });
+ var msg = _eventMessages[event.type];
+ if (event.type === "error" && event.name && msg) {
+ msg = msg[event.name];
+ }
+ if (msg) {
+ event.message = msg;
+ }
+ if (event.type === "ready") {
+ _extend(event, {
+ target: null,
+ version: _flashState.version
+ });
+ }
+ if (event.type === "error") {
+ if (/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ target: null,
+ minimumVersion: _minimumFlashVersion
+ });
+ }
+ if (/^flash-(outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ version: _flashState.version
+ });
+ }
+ }
+ if (event.type === "copy") {
+ event.clipboardData = {
+ setData: ZeroClipboard.setData,
+ clearData: ZeroClipboard.clearData
+ };
+ }
+ if (event.type === "aftercopy") {
+ event = _mapClipResultsFromFlash(event, _clipDataFormatMap);
+ }
+ if (event.target && !event.relatedTarget) {
+ event.relatedTarget = _getRelatedTarget(event.target);
+ }
+ event = _addMouseData(event);
+ return event;
+ };
+ /**
+ * Get a relatedTarget from the target's `data-clipboard-target` attribute
+ * @private
+ */
+ var _getRelatedTarget = function(targetEl) {
+ var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target");
+ return relatedTargetId ? _document.getElementById(relatedTargetId) : null;
+ };
+ /**
+ * Add element and position data to `MouseEvent` instances
+ * @private
+ */
+ var _addMouseData = function(event) {
+ if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ var srcElement = event.target;
+ var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined;
+ var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined;
+ var pos = _getDOMObjectPosition(srcElement);
+ var screenLeft = _window.screenLeft || _window.screenX || 0;
+ var screenTop = _window.screenTop || _window.screenY || 0;
+ var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;
+ var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;
+ var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0);
+ var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0);
+ var clientX = pageX - scrollLeft;
+ var clientY = pageY - scrollTop;
+ var screenX = screenLeft + clientX;
+ var screenY = screenTop + clientY;
+ var moveX = typeof event.movementX === "number" ? event.movementX : 0;
+ var moveY = typeof event.movementY === "number" ? event.movementY : 0;
+ delete event._stageX;
+ delete event._stageY;
+ _extend(event, {
+ srcElement: srcElement,
+ fromElement: fromElement,
+ toElement: toElement,
+ screenX: screenX,
+ screenY: screenY,
+ pageX: pageX,
+ pageY: pageY,
+ clientX: clientX,
+ clientY: clientY,
+ x: clientX,
+ y: clientY,
+ movementX: moveX,
+ movementY: moveY,
+ offsetX: 0,
+ offsetY: 0,
+ layerX: 0,
+ layerY: 0
+ });
+ }
+ return event;
+ };
+ /**
+ * Determine if an event's registered handlers should be execute synchronously or asynchronously.
+ *
+ * @returns {boolean}
+ * @private
+ */
+ var _shouldPerformAsync = function(event) {
+ var eventType = event && typeof event.type === "string" && event.type || "";
+ return !/^(?:(?:before)?copy|destroy)$/.test(eventType);
+ };
+ /**
+ * Control if a callback should be executed asynchronously or not.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _dispatchCallback = function(func, context, args, async) {
+ if (async) {
+ _setTimeout(function() {
+ func.apply(context, args);
+ }, 0);
+ } else {
+ func.apply(context, args);
+ }
+ };
+ /**
+ * Handle the actual dispatching of events to client instances.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _dispatchCallbacks = function(event) {
+ if (!(typeof event === "object" && event && event.type)) {
+ return;
+ }
+ var async = _shouldPerformAsync(event);
+ var wildcardTypeHandlers = _handlers["*"] || [];
+ var specificTypeHandlers = _handlers[event.type] || [];
+ var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
+ if (handlers && handlers.length) {
+ var i, len, func, context, eventCopy, originalContext = this;
+ for (i = 0, len = handlers.length; i < len; i++) {
+ func = handlers[i];
+ context = originalContext;
+ if (typeof func === "string" && typeof _window[func] === "function") {
+ func = _window[func];
+ }
+ if (typeof func === "object" && func && typeof func.handleEvent === "function") {
+ context = func;
+ func = func.handleEvent;
+ }
+ if (typeof func === "function") {
+ eventCopy = _extend({}, event);
+ _dispatchCallback(func, context, [ eventCopy ], async);
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Preprocess any special behaviors, reactions, or state changes after receiving this event.
+ * Executes only once per event emitted, NOT once per client.
+ * @private
+ */
+ var _preprocessEvent = function(event) {
+ var element = event.target || _currentElement || null;
+ var sourceIsSwf = event._source === "swf";
+ delete event._source;
+ var flashErrorNames = [ "flash-disabled", "flash-outdated", "flash-unavailable", "flash-deactivated", "flash-overdue" ];
+ switch (event.type) {
+ case "error":
+ if (flashErrorNames.indexOf(event.name) !== -1) {
+ _extend(_flashState, {
+ disabled: event.name === "flash-disabled",
+ outdated: event.name === "flash-outdated",
+ unavailable: event.name === "flash-unavailable",
+ deactivated: event.name === "flash-deactivated",
+ overdue: event.name === "flash-overdue",
+ ready: false
+ });
+ }
+ break;
+
+ case "ready":
+ var wasDeactivated = _flashState.deactivated === true;
+ _extend(_flashState, {
+ disabled: false,
+ outdated: false,
+ unavailable: false,
+ deactivated: false,
+ overdue: wasDeactivated,
+ ready: !wasDeactivated
+ });
+ break;
+
+ case "beforecopy":
+ _copyTarget = element;
+ break;
+
+ case "copy":
+ var textContent, htmlContent, targetEl = event.relatedTarget;
+ if (!(_clipData["text/html"] || _clipData["text/plain"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ if (htmlContent !== textContent) {
+ event.clipboardData.setData("text/html", htmlContent);
+ }
+ } else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ }
+ break;
+
+ case "aftercopy":
+ ZeroClipboard.clearData();
+ if (element && element !== _safeActiveElement() && element.focus) {
+ element.focus();
+ }
+ break;
+
+ case "_mouseover":
+ ZeroClipboard.focus(element);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseenter",
+ bubbles: false,
+ cancelable: false
+ }));
+ }
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseover"
+ }));
+ }
+ break;
+
+ case "_mouseout":
+ ZeroClipboard.blur();
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseleave",
+ bubbles: false,
+ cancelable: false
+ }));
+ }
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseout"
+ }));
+ }
+ break;
+
+ case "_mousedown":
+ _addClass(element, _globalConfig.activeClass);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_mouseup":
+ _removeClass(element, _globalConfig.activeClass);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_click":
+ _copyTarget = null;
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_mousemove":
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+ }
+ if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ return true;
+ }
+ };
+ /**
+ * Dispatch a synthetic MouseEvent.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _fireMouseEvent = function(event) {
+ if (!(event && typeof event.type === "string" && event)) {
+ return;
+ }
+ var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {
+ view: doc.defaultView || _window,
+ canBubble: true,
+ cancelable: true,
+ detail: event.type === "click" ? 1 : 0,
+ button: typeof event.which === "number" ? event.which - 1 : typeof event.button === "number" ? event.button : doc.createEvent ? 0 : 1
+ }, args = _extend(defaults, event);
+ if (!target) {
+ return;
+ }
+ if (doc.createEvent && target.dispatchEvent) {
+ 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 ];
+ e = doc.createEvent("MouseEvents");
+ if (e.initMouseEvent) {
+ e.initMouseEvent.apply(e, args);
+ e._source = "js";
+ target.dispatchEvent(e);
+ }
+ }
+ };
+ /**
+ * Create the HTML bridge element to embed the Flash object into.
+ * @private
+ */
+ var _createHtmlBridge = function() {
+ var container = _document.createElement("div");
+ container.id = _globalConfig.containerId;
+ container.className = _globalConfig.containerClass;
+ container.style.position = "absolute";
+ container.style.left = "0px";
+ container.style.top = "-9999px";
+ container.style.width = "1px";
+ container.style.height = "1px";
+ container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex);
+ return container;
+ };
+ /**
+ * Get the HTML element container that wraps the Flash bridge object/element.
+ * @private
+ */
+ var _getHtmlBridge = function(flashBridge) {
+ var htmlBridge = flashBridge && flashBridge.parentNode;
+ while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) {
+ htmlBridge = htmlBridge.parentNode;
+ }
+ return htmlBridge || null;
+ };
+ /**
+ * Create the SWF object.
+ *
+ * @returns The SWF object reference.
+ * @private
+ */
+ var _embedSwf = function() {
+ var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);
+ if (!flashBridge) {
+ var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);
+ var allowNetworking = allowScriptAccess === "never" ? "none" : "all";
+ var flashvars = _vars(_globalConfig);
+ var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);
+ container = _createHtmlBridge();
+ var divToBeReplaced = _document.createElement("div");
+ container.appendChild(divToBeReplaced);
+ _document.body.appendChild(container);
+ var tmpDiv = _document.createElement("div");
+ var oldIE = _flashState.pluginType === "activex";
+ tmpDiv.innerHTML = '";
+ flashBridge = tmpDiv.firstChild;
+ tmpDiv = null;
+ _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;
+ container.replaceChild(flashBridge, divToBeReplaced);
+ }
+ if (!flashBridge) {
+ flashBridge = _document[_globalConfig.swfObjectId];
+ if (flashBridge && (len = flashBridge.length)) {
+ flashBridge = flashBridge[len - 1];
+ }
+ if (!flashBridge && container) {
+ flashBridge = container.firstChild;
+ }
+ }
+ _flashState.bridge = flashBridge || null;
+ return flashBridge;
+ };
+ /**
+ * Destroy the SWF object.
+ * @private
+ */
+ var _unembedSwf = function() {
+ var flashBridge = _flashState.bridge;
+ if (flashBridge) {
+ var htmlBridge = _getHtmlBridge(flashBridge);
+ if (htmlBridge) {
+ if (_flashState.pluginType === "activex" && "readyState" in flashBridge) {
+ flashBridge.style.display = "none";
+ (function removeSwfFromIE() {
+ if (flashBridge.readyState === 4) {
+ for (var prop in flashBridge) {
+ if (typeof flashBridge[prop] === "function") {
+ flashBridge[prop] = null;
+ }
+ }
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ } else {
+ _setTimeout(removeSwfFromIE, 10);
+ }
+ })();
+ } else {
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ }
+ }
+ _flashState.ready = null;
+ _flashState.bridge = null;
+ _flashState.deactivated = null;
+ }
+ };
+ /**
+ * Map the data format names of the "clipData" to Flash-friendly names.
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+ var _mapClipDataToFlash = function(clipData) {
+ var newClipData = {}, formatMap = {};
+ if (!(typeof clipData === "object" && clipData)) {
+ return;
+ }
+ for (var dataFormat in clipData) {
+ if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) {
+ switch (dataFormat.toLowerCase()) {
+ case "text/plain":
+ case "text":
+ case "air:text":
+ case "flash:text":
+ newClipData.text = clipData[dataFormat];
+ formatMap.text = dataFormat;
+ break;
+
+ case "text/html":
+ case "html":
+ case "air:html":
+ case "flash:html":
+ newClipData.html = clipData[dataFormat];
+ formatMap.html = dataFormat;
+ break;
+
+ case "application/rtf":
+ case "text/rtf":
+ case "rtf":
+ case "richtext":
+ case "air:rtf":
+ case "flash:rtf":
+ newClipData.rtf = clipData[dataFormat];
+ formatMap.rtf = dataFormat;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return {
+ data: newClipData,
+ formatMap: formatMap
+ };
+ };
+ /**
+ * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping).
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+ var _mapClipResultsFromFlash = function(clipResults, formatMap) {
+ if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) {
+ return clipResults;
+ }
+ var newResults = {};
+ for (var prop in clipResults) {
+ if (_hasOwn.call(clipResults, prop)) {
+ if (prop !== "success" && prop !== "data") {
+ newResults[prop] = clipResults[prop];
+ continue;
+ }
+ newResults[prop] = {};
+ var tmpHash = clipResults[prop];
+ for (var dataFormat in tmpHash) {
+ if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {
+ newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];
+ }
+ }
+ }
+ }
+ return newResults;
+ };
+ /**
+ * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}"
+ * query param string to return. Does NOT append that string to the original path.
+ * This is useful because ExternalInterface often breaks when a Flash SWF is cached.
+ *
+ * @returns The `noCache` query param with necessary "?"/"&" prefix.
+ * @private
+ */
+ var _cacheBust = function(path, options) {
+ var cacheBust = options == null || options && options.cacheBust === true;
+ if (cacheBust) {
+ return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now();
+ } else {
+ return "";
+ }
+ };
+ /**
+ * Creates a query string for the FlashVars param.
+ * Does NOT include the cache-busting query param.
+ *
+ * @returns FlashVars query string
+ * @private
+ */
+ var _vars = function(options) {
+ var i, len, domain, domains, str = "", trustedOriginsExpanded = [];
+ if (options.trustedDomains) {
+ if (typeof options.trustedDomains === "string") {
+ domains = [ options.trustedDomains ];
+ } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) {
+ domains = options.trustedDomains;
+ }
+ }
+ if (domains && domains.length) {
+ for (i = 0, len = domains.length; i < len; i++) {
+ if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") {
+ domain = _extractDomain(domains[i]);
+ if (!domain) {
+ continue;
+ }
+ if (domain === "*") {
+ trustedOriginsExpanded.length = 0;
+ trustedOriginsExpanded.push(domain);
+ break;
+ }
+ trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, "//" + domain, _window.location.protocol + "//" + domain ]);
+ }
+ }
+ }
+ if (trustedOriginsExpanded.length) {
+ str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(","));
+ }
+ if (options.forceEnhancedClipboard === true) {
+ str += (str ? "&" : "") + "forceEnhancedClipboard=true";
+ }
+ if (typeof options.swfObjectId === "string" && options.swfObjectId) {
+ str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId);
+ }
+ return str;
+ };
+ /**
+ * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or
+ * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/").
+ *
+ * @returns the domain
+ * @private
+ */
+ var _extractDomain = function(originOrUrl) {
+ if (originOrUrl == null || originOrUrl === "") {
+ return null;
+ }
+ originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, "");
+ if (originOrUrl === "") {
+ return null;
+ }
+ var protocolIndex = originOrUrl.indexOf("//");
+ originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);
+ var pathIndex = originOrUrl.indexOf("/");
+ originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);
+ if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") {
+ return null;
+ }
+ return originOrUrl || null;
+ };
+ /**
+ * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.
+ *
+ * @returns The appropriate script access level.
+ * @private
+ */
+ var _determineScriptAccess = function() {
+ var _extractAllDomains = function(origins) {
+ var i, len, tmp, resultsArray = [];
+ if (typeof origins === "string") {
+ origins = [ origins ];
+ }
+ if (!(typeof origins === "object" && origins && typeof origins.length === "number")) {
+ return resultsArray;
+ }
+ for (i = 0, len = origins.length; i < len; i++) {
+ if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {
+ if (tmp === "*") {
+ resultsArray.length = 0;
+ resultsArray.push("*");
+ break;
+ }
+ if (resultsArray.indexOf(tmp) === -1) {
+ resultsArray.push(tmp);
+ }
+ }
+ }
+ return resultsArray;
+ };
+ return function(currentDomain, configOptions) {
+ var swfDomain = _extractDomain(configOptions.swfPath);
+ if (swfDomain === null) {
+ swfDomain = currentDomain;
+ }
+ var trustedDomains = _extractAllDomains(configOptions.trustedDomains);
+ var len = trustedDomains.length;
+ if (len > 0) {
+ if (len === 1 && trustedDomains[0] === "*") {
+ return "always";
+ }
+ if (trustedDomains.indexOf(currentDomain) !== -1) {
+ if (len === 1 && currentDomain === swfDomain) {
+ return "sameDomain";
+ }
+ return "always";
+ }
+ }
+ return "never";
+ };
+ }();
+ /**
+ * Get the currently active/focused DOM element.
+ *
+ * @returns the currently active/focused element, or `null`
+ * @private
+ */
+ var _safeActiveElement = function() {
+ try {
+ return _document.activeElement;
+ } catch (err) {
+ return null;
+ }
+ };
+ /**
+ * Add a class to an element, if it doesn't already have it.
+ *
+ * @returns The element, with its new class added.
+ * @private
+ */
+ var _addClass = function(element, value) {
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+ if (element.classList) {
+ if (!element.classList.contains(value)) {
+ element.classList.add(value);
+ }
+ return element;
+ }
+ if (value && typeof value === "string") {
+ var classNames = (value || "").split(/\s+/);
+ if (element.nodeType === 1) {
+ if (!element.className) {
+ element.className = value;
+ } else {
+ var className = " " + element.className + " ", setClass = element.className;
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ if (className.indexOf(" " + classNames[c] + " ") < 0) {
+ setClass += " " + classNames[c];
+ }
+ }
+ element.className = setClass.replace(/^\s+|\s+$/g, "");
+ }
+ }
+ }
+ return element;
+ };
+ /**
+ * Remove a class from an element, if it has it.
+ *
+ * @returns The element, with its class removed.
+ * @private
+ */
+ var _removeClass = function(element, value) {
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+ if (element.classList) {
+ if (element.classList.contains(value)) {
+ element.classList.remove(value);
+ }
+ return element;
+ }
+ if (typeof value === "string" && value) {
+ var classNames = value.split(/\s+/);
+ if (element.nodeType === 1 && element.className) {
+ var className = (" " + element.className + " ").replace(/[\n\t]/g, " ");
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ element.className = className.replace(/^\s+|\s+$/g, "");
+ }
+ }
+ return element;
+ };
+ /**
+ * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`,
+ * then we assume that it should be a hand ("pointer") cursor if the element
+ * is an anchor element ("a" tag).
+ *
+ * @returns The computed style property.
+ * @private
+ */
+ var _getStyle = function(el, prop) {
+ var value = _window.getComputedStyle(el, null).getPropertyValue(prop);
+ if (prop === "cursor") {
+ if (!value || value === "auto") {
+ if (el.nodeName === "A") {
+ return "pointer";
+ }
+ }
+ }
+ return value;
+ };
+ /**
+ * Get the zoom factor of the browser. Always returns `1.0`, except at
+ * non-default zoom levels in IE<8 and some older versions of WebKit.
+ *
+ * @returns Floating unit percentage of the zoom factor (e.g. 150% = `1.5`).
+ * @private
+ */
+ var _getZoomFactor = function() {
+ var rect, physicalWidth, logicalWidth, zoomFactor = 1;
+ if (typeof _document.body.getBoundingClientRect === "function") {
+ rect = _document.body.getBoundingClientRect();
+ physicalWidth = rect.right - rect.left;
+ logicalWidth = _document.body.offsetWidth;
+ zoomFactor = _round(physicalWidth / logicalWidth * 100) / 100;
+ }
+ return zoomFactor;
+ };
+ /**
+ * Get the DOM positioning info of an element.
+ *
+ * @returns Object containing the element's position, width, and height.
+ * @private
+ */
+ var _getDOMObjectPosition = function(obj) {
+ var info = {
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0
+ };
+ if (obj.getBoundingClientRect) {
+ var rect = obj.getBoundingClientRect();
+ var pageXOffset, pageYOffset, zoomFactor;
+ if ("pageXOffset" in _window && "pageYOffset" in _window) {
+ pageXOffset = _window.pageXOffset;
+ pageYOffset = _window.pageYOffset;
+ } else {
+ zoomFactor = _getZoomFactor();
+ pageXOffset = _round(_document.documentElement.scrollLeft / zoomFactor);
+ pageYOffset = _round(_document.documentElement.scrollTop / zoomFactor);
+ }
+ var leftBorderWidth = _document.documentElement.clientLeft || 0;
+ var topBorderWidth = _document.documentElement.clientTop || 0;
+ info.left = rect.left + pageXOffset - leftBorderWidth;
+ info.top = rect.top + pageYOffset - topBorderWidth;
+ info.width = "width" in rect ? rect.width : rect.right - rect.left;
+ info.height = "height" in rect ? rect.height : rect.bottom - rect.top;
+ }
+ return info;
+ };
+ /**
+ * Reposition the Flash object to cover the currently activated element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _reposition = function() {
+ var htmlBridge;
+ if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {
+ var pos = _getDOMObjectPosition(_currentElement);
+ _extend(htmlBridge.style, {
+ width: pos.width + "px",
+ height: pos.height + "px",
+ top: pos.top + "px",
+ left: pos.left + "px",
+ zIndex: "" + _getSafeZIndex(_globalConfig.zIndex)
+ });
+ }
+ };
+ /**
+ * Sends a signal to the Flash object to display the hand cursor if `true`.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _setHandCursor = function(enabled) {
+ if (_flashState.ready === true) {
+ if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") {
+ _flashState.bridge.setHandCursor(enabled);
+ } else {
+ _flashState.ready = false;
+ }
+ }
+ };
+ /**
+ * Get a safe value for `zIndex`
+ *
+ * @returns an integer, or "auto"
+ * @private
+ */
+ var _getSafeZIndex = function(val) {
+ if (/^(?:auto|inherit)$/.test(val)) {
+ return val;
+ }
+ var zIndex;
+ if (typeof val === "number" && !_isNaN(val)) {
+ zIndex = val;
+ } else if (typeof val === "string") {
+ zIndex = _getSafeZIndex(_parseInt(val, 10));
+ }
+ return typeof zIndex === "number" ? zIndex : "auto";
+ };
+ /**
+ * Detect the Flash Player status, version, and plugin type.
+ *
+ * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}
+ * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _detectFlashSupport = function(ActiveXObject) {
+ var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = "";
+ /**
+ * Derived from Apple's suggested sniffer.
+ * @param {String} desc e.g. "Shockwave Flash 7.0 r61"
+ * @returns {String} "7.0.61"
+ * @private
+ */
+ function parseFlashVersion(desc) {
+ var matches = desc.match(/[\d]+/g);
+ matches.length = 3;
+ return matches.join(".");
+ }
+ function isPepperFlash(flashPlayerFileName) {
+ return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === "chrome.plugin");
+ }
+ function inspectPlugin(plugin) {
+ if (plugin) {
+ hasFlash = true;
+ if (plugin.version) {
+ flashVersion = parseFlashVersion(plugin.version);
+ }
+ if (!flashVersion && plugin.description) {
+ flashVersion = parseFlashVersion(plugin.description);
+ }
+ if (plugin.filename) {
+ isPPAPI = isPepperFlash(plugin.filename);
+ }
+ }
+ }
+ if (_navigator.plugins && _navigator.plugins.length) {
+ plugin = _navigator.plugins["Shockwave Flash"];
+ inspectPlugin(plugin);
+ if (_navigator.plugins["Shockwave Flash 2.0"]) {
+ hasFlash = true;
+ flashVersion = "2.0.0.11";
+ }
+ } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {
+ mimeType = _navigator.mimeTypes["application/x-shockwave-flash"];
+ plugin = mimeType && mimeType.enabledPlugin;
+ inspectPlugin(plugin);
+ } else if (typeof ActiveXObject !== "undefined") {
+ isActiveX = true;
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ } catch (e1) {
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+ hasFlash = true;
+ flashVersion = "6.0.21";
+ } catch (e2) {
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ } catch (e3) {
+ isActiveX = false;
+ }
+ }
+ }
+ }
+ _flashState.disabled = hasFlash !== true;
+ _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);
+ _flashState.version = flashVersion || "0.0.0";
+ _flashState.pluginType = isPPAPI ? "pepper" : isActiveX ? "activex" : hasFlash ? "netscape" : "unknown";
+ };
+ /**
+ * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.
+ */
+ _detectFlashSupport(_ActiveXObject);
+ /**
+ * A shell constructor for `ZeroClipboard` client instances.
+ *
+ * @constructor
+ */
+ var ZeroClipboard = function() {
+ if (!(this instanceof ZeroClipboard)) {
+ return new ZeroClipboard();
+ }
+ if (typeof ZeroClipboard._createClient === "function") {
+ ZeroClipboard._createClient.apply(this, _args(arguments));
+ }
+ };
+ /**
+ * The ZeroClipboard library's version number.
+ *
+ * @static
+ * @readonly
+ * @property {string}
+ */
+ _defineProperty(ZeroClipboard, "version", {
+ value: "2.1.6",
+ writable: false,
+ configurable: true,
+ enumerable: true
+ });
+ /**
+ * Update or get a copy of the ZeroClipboard global configuration.
+ * Returns a copy of the current/updated configuration.
+ *
+ * @returns Object
+ * @static
+ */
+ ZeroClipboard.config = function() {
+ return _config.apply(this, _args(arguments));
+ };
+ /**
+ * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
+ *
+ * @returns Object
+ * @static
+ */
+ ZeroClipboard.state = function() {
+ return _state.apply(this, _args(arguments));
+ };
+ /**
+ * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.
+ *
+ * @returns Boolean
+ * @static
+ */
+ ZeroClipboard.isFlashUnusable = function() {
+ return _isFlashUnusable.apply(this, _args(arguments));
+ };
+ /**
+ * Register an event listener.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ ZeroClipboard.on = function() {
+ return _on.apply(this, _args(arguments));
+ };
+ /**
+ * Unregister an event listener.
+ * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.
+ * If no `eventType` is provided, it will unregister all listeners for every event type.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ ZeroClipboard.off = function() {
+ return _off.apply(this, _args(arguments));
+ };
+ /**
+ * Retrieve event listeners for an `eventType`.
+ * If no `eventType` is provided, it will retrieve all listeners for every event type.
+ *
+ * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
+ */
+ ZeroClipboard.handlers = function() {
+ return _listeners.apply(this, _args(arguments));
+ };
+ /**
+ * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.
+ *
+ * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
+ * @static
+ */
+ ZeroClipboard.emit = function() {
+ return _emit.apply(this, _args(arguments));
+ };
+ /**
+ * Create and embed the Flash object.
+ *
+ * @returns The Flash object
+ * @static
+ */
+ ZeroClipboard.create = function() {
+ return _create.apply(this, _args(arguments));
+ };
+ /**
+ * Self-destruct and clean up everything, including the embedded Flash object.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.destroy = function() {
+ return _destroy.apply(this, _args(arguments));
+ };
+ /**
+ * Set the pending data for clipboard injection.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.setData = function() {
+ return _setData.apply(this, _args(arguments));
+ };
+ /**
+ * Clear the pending data for clipboard injection.
+ * If no `format` is provided, all pending data formats will be cleared.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.clearData = function() {
+ return _clearData.apply(this, _args(arguments));
+ };
+ /**
+ * Get a copy of the pending data for clipboard injection.
+ * If no `format` is provided, a copy of ALL pending data formats will be returned.
+ *
+ * @returns `String` or `Object`
+ * @static
+ */
+ ZeroClipboard.getData = function() {
+ return _getData.apply(this, _args(arguments));
+ };
+ /**
+ * Sets the current HTML object that the Flash object should overlay. This will put the global
+ * Flash object on top of the current element; depending on the setup, this may also set the
+ * pending clipboard text data as well as the Flash object's wrapping element's title attribute
+ * based on the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.focus = ZeroClipboard.activate = function() {
+ return _focus.apply(this, _args(arguments));
+ };
+ /**
+ * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on
+ * the setup, this may also unset the Flash object's wrapping element's title attribute based on
+ * the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.blur = ZeroClipboard.deactivate = function() {
+ return _blur.apply(this, _args(arguments));
+ };
+ /**
+ * Returns the currently focused/"activated" HTML element that the Flash object is wrapping.
+ *
+ * @returns `HTMLElement` or `null`
+ * @static
+ */
+ ZeroClipboard.activeElement = function() {
+ return _activeElement.apply(this, _args(arguments));
+ };
+ if (typeof define === "function" && define.amd) {
+ define(function() {
+ return ZeroClipboard;
+ });
+ } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) {
+ module.exports = ZeroClipboard;
+ } else {
+ window.ZeroClipboard = ZeroClipboard;
+ }
+})(function() {
+ return this || window;
+}());
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.min.js b/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.min.js
new file mode 100755
index 0000000..883c936
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/ZeroClipboard.Core.min.js
@@ -0,0 +1,10 @@
+/*!
+ * ZeroClipboard
+ * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
+ * Copyright (c) 2014 Jon Rohan, James M. Greene
+ * Licensed MIT
+ * http://zeroclipboard.org/
+ * v2.1.6
+ */
+!function(a,b){"use strict";var c,d,e=a,f=e.document,g=e.navigator,h=e.setTimeout,i=e.encodeURIComponent,j=e.ActiveXObject,k=e.Error,l=e.Number.parseInt||e.parseInt,m=e.Number.parseFloat||e.parseFloat,n=e.Number.isNaN||e.isNaN,o=e.Math.round,p=e.Date.now,q=e.Object.keys,r=e.Object.defineProperty,s=e.Object.prototype.hasOwnProperty,t=e.Array.prototype.slice,u=function(){var a=function(a){return a};if("function"==typeof e.wrap&&"function"==typeof e.unwrap)try{var b=f.createElement("div"),c=e.unwrap(b);1===b.nodeType&&c&&1===c.nodeType&&(a=e.unwrap)}catch(d){}return a}(),v=function(a){return t.call(a,0)},w=function(){var a,c,d,e,f,g,h=v(arguments),i=h[0]||{};for(a=1,c=h.length;c>a;a++)if(null!=(d=h[a]))for(e in d)s.call(d,e)&&(f=i[e],g=d[e],i!==g&&g!==b&&(i[e]=g));return i},x=function(a){var b,c,d,e;if("object"!=typeof a||null==a)b=a;else if("number"==typeof a.length)for(b=[],c=0,d=a.length;d>c;c++)s.call(a,c)&&(b[c]=x(a[c]));else{b={};for(e in a)s.call(a,e)&&(b[e]=x(a[e]))}return b},y=function(a,b){for(var c={},d=0,e=b.length;e>d;d++)b[d]in a&&(c[b[d]]=a[b[d]]);return c},z=function(a,b){var c={};for(var d in a)-1===b.indexOf(d)&&(c[d]=a[d]);return c},A=function(a){if(a)for(var b in a)s.call(a,b)&&delete a[b];return a},B=function(a,b){if(a&&1===a.nodeType&&a.ownerDocument&&b&&(1===b.nodeType&&b.ownerDocument&&b.ownerDocument===a.ownerDocument||9===b.nodeType&&!b.ownerDocument&&b===a.ownerDocument))do{if(a===b)return!0;a=a.parentNode}while(a);return!1},C=function(a){var b;return"string"==typeof a&&a&&(b=a.split("#")[0].split("?")[0],b=a.slice(0,a.lastIndexOf("/")+1)),b},D=function(a){var b,c;return"string"==typeof a&&a&&(c=a.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]?b=c[1]:(c=a.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]&&(b=c[1]))),b},E=function(){var a,b;try{throw new k}catch(c){b=c}return b&&(a=b.sourceURL||b.fileName||D(b.stack)),a},F=function(){var a,c,d;if(f.currentScript&&(a=f.currentScript.src))return a;if(c=f.getElementsByTagName("script"),1===c.length)return c[0].src||b;if("readyState"in c[0])for(d=c.length;d--;)if("interactive"===c[d].readyState&&(a=c[d].src))return a;return"loading"===f.readyState&&(a=c[c.length-1].src)?a:(a=E())?a:b},G=function(){var a,c,d,e=f.getElementsByTagName("script");for(a=e.length;a--;){if(!(d=e[a].src)){c=null;break}if(d=C(d),null==c)c=d;else if(c!==d){c=null;break}}return c||b},H=function(){var a=C(F())||G()||"";return a+"ZeroClipboard.swf"},I={bridge:null,version:"0.0.0",pluginType:"unknown",disabled:null,outdated:null,unavailable:null,deactivated:null,overdue:null,ready:null},J="11.0.0",K={},L={},M=null,N={ready:"Flash communication is established",error:{"flash-disabled":"Flash is disabled or not installed","flash-outdated":"Flash is too outdated to support ZeroClipboard","flash-unavailable":"Flash is unable to communicate bidirectionally with JavaScript","flash-deactivated":"Flash is too outdated for your browser and/or is configured as click-to-activate","flash-overdue":"Flash communication was established but NOT within the acceptable time limit"}},O={swfPath:H(),trustedDomains:a.location.host?[a.location.host]:[],cacheBust:!0,forceEnhancedClipboard:!1,flashLoadTimeout:3e4,autoActivate:!0,bubbleEvents:!0,containerId:"global-zeroclipboard-html-bridge",containerClass:"global-zeroclipboard-container",swfObjectId:"global-zeroclipboard-flash-bridge",hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",forceHandCursor:!1,title:null,zIndex:999999999},P=function(a){if("object"==typeof a&&null!==a)for(var b in a)if(s.call(a,b))if(/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(b))O[b]=a[b];else if(null==I.bridge)if("containerId"===b||"swfObjectId"===b){if(!cb(a[b]))throw new Error("The specified `"+b+"` value is not valid as an HTML4 Element ID");O[b]=a[b]}else O[b]=a[b];{if("string"!=typeof a||!a)return x(O);if(s.call(O,a))return O[a]}},Q=function(){return{browser:y(g,["userAgent","platform","appName"]),flash:z(I,["bridge"]),zeroclipboard:{version:Fb.version,config:Fb.config()}}},R=function(){return!!(I.disabled||I.outdated||I.unavailable||I.deactivated)},S=function(a,b){var c,d,e,f={};if("string"==typeof a&&a)e=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)s.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Fb.on(c,a[c]);if(e&&e.length){for(c=0,d=e.length;d>c;c++)a=e[c].replace(/^on/,""),f[a]=!0,K[a]||(K[a]=[]),K[a].push(b);if(f.ready&&I.ready&&Fb.emit({type:"ready"}),f.error){var g=["disabled","outdated","unavailable","deactivated","overdue"];for(c=0,d=g.length;d>c;c++)if(I[g[c]]===!0){Fb.emit({type:"error",name:"flash-"+g[c]});break}}}return Fb},T=function(a,b){var c,d,e,f,g;if(0===arguments.length)f=q(K);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)s.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Fb.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=K[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return Fb},U=function(a){var b;return b="string"==typeof a&&a?x(K[a])||null:x(K)},V=function(a){var b,c,d;return a=db(a),a&&!jb(a)?"ready"===a.type&&I.overdue===!0?Fb.emit({type:"error",name:"flash-overdue"}):(b=w({},a),ib.call(this,b),"copy"===a.type&&(d=pb(L),c=d.data,M=d.formatMap),c):void 0},W=function(){if("boolean"!=typeof I.ready&&(I.ready=!1),!Fb.isFlashUnusable()&&null===I.bridge){var a=O.flashLoadTimeout;"number"==typeof a&&a>=0&&h(function(){"boolean"!=typeof I.deactivated&&(I.deactivated=!0),I.deactivated===!0&&Fb.emit({type:"error",name:"flash-deactivated"})},a),I.overdue=!1,nb()}},X=function(){Fb.clearData(),Fb.blur(),Fb.emit("destroy"),ob(),Fb.off()},Y=function(a,b){var c;if("object"==typeof a&&a&&"undefined"==typeof b)c=a,Fb.clearData();else{if("string"!=typeof a||!a)return;c={},c[a]=b}for(var d in c)"string"==typeof d&&d&&s.call(c,d)&&"string"==typeof c[d]&&c[d]&&(L[d]=c[d])},Z=function(a){"undefined"==typeof a?(A(L),M=null):"string"==typeof a&&s.call(L,a)&&delete L[a]},$=function(a){return"undefined"==typeof a?x(L):"string"==typeof a&&s.call(L,a)?L[a]:void 0},_=function(a){if(a&&1===a.nodeType){c&&(xb(c,O.activeClass),c!==a&&xb(c,O.hoverClass)),c=a,wb(a,O.hoverClass);var b=a.getAttribute("title")||O.title;if("string"==typeof b&&b){var d=mb(I.bridge);d&&d.setAttribute("title",b)}var e=O.forceHandCursor===!0||"pointer"===yb(a,"cursor");Cb(e),Bb()}},ab=function(){var a=mb(I.bridge);a&&(a.removeAttribute("title"),a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.top="1px"),c&&(xb(c,O.hoverClass),xb(c,O.activeClass),c=null)},bb=function(){return c||null},cb=function(a){return"string"==typeof a&&a&&/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(a)},db=function(a){var b;if("string"==typeof a&&a?(b=a,a={}):"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(b=a.type),b){!a.target&&/^(copy|aftercopy|_click)$/.test(b.toLowerCase())&&(a.target=d),w(a,{type:b.toLowerCase(),target:a.target||c||null,relatedTarget:a.relatedTarget||null,currentTarget:I&&I.bridge||null,timeStamp:a.timeStamp||p()||null});var e=N[a.type];return"error"===a.type&&a.name&&e&&(e=e[a.name]),e&&(a.message=e),"ready"===a.type&&w(a,{target:null,version:I.version}),"error"===a.type&&(/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(a.name)&&w(a,{target:null,minimumVersion:J}),/^flash-(outdated|unavailable|deactivated|overdue)$/.test(a.name)&&w(a,{version:I.version})),"copy"===a.type&&(a.clipboardData={setData:Fb.setData,clearData:Fb.clearData}),"aftercopy"===a.type&&(a=qb(a,M)),a.target&&!a.relatedTarget&&(a.relatedTarget=eb(a.target)),a=fb(a)}},eb=function(a){var b=a&&a.getAttribute&&a.getAttribute("data-clipboard-target");return b?f.getElementById(b):null},fb=function(a){if(a&&/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)){var c=a.target,d="_mouseover"===a.type&&a.relatedTarget?a.relatedTarget:b,g="_mouseout"===a.type&&a.relatedTarget?a.relatedTarget:b,h=Ab(c),i=e.screenLeft||e.screenX||0,j=e.screenTop||e.screenY||0,k=f.body.scrollLeft+f.documentElement.scrollLeft,l=f.body.scrollTop+f.documentElement.scrollTop,m=h.left+("number"==typeof a._stageX?a._stageX:0),n=h.top+("number"==typeof a._stageY?a._stageY:0),o=m-k,p=n-l,q=i+o,r=j+p,s="number"==typeof a.movementX?a.movementX:0,t="number"==typeof a.movementY?a.movementY:0;delete a._stageX,delete a._stageY,w(a,{srcElement:c,fromElement:d,toElement:g,screenX:q,screenY:r,pageX:m,pageY:n,clientX:o,clientY:p,x:o,y:p,movementX:s,movementY:t,offsetX:0,offsetY:0,layerX:0,layerY:0})}return a},gb=function(a){var b=a&&"string"==typeof a.type&&a.type||"";return!/^(?:(?:before)?copy|destroy)$/.test(b)},hb=function(a,b,c,d){d?h(function(){a.apply(b,c)},0):a.apply(b,c)},ib=function(a){if("object"==typeof a&&a&&a.type){var b=gb(a),c=K["*"]||[],d=K[a.type]||[],f=c.concat(d);if(f&&f.length){var g,h,i,j,k,l=this;for(g=0,h=f.length;h>g;g++)i=f[g],j=l,"string"==typeof i&&"function"==typeof e[i]&&(i=e[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=w({},a),hb(i,j,[k],b))}return this}},jb=function(a){var b=a.target||c||null,e="swf"===a._source;delete a._source;var f=["flash-disabled","flash-outdated","flash-unavailable","flash-deactivated","flash-overdue"];switch(a.type){case"error":-1!==f.indexOf(a.name)&&w(I,{disabled:"flash-disabled"===a.name,outdated:"flash-outdated"===a.name,unavailable:"flash-unavailable"===a.name,deactivated:"flash-deactivated"===a.name,overdue:"flash-overdue"===a.name,ready:!1});break;case"ready":var g=I.deactivated===!0;w(I,{disabled:!1,outdated:!1,unavailable:!1,deactivated:!1,overdue:g,ready:!g});break;case"beforecopy":d=b;break;case"copy":var h,i,j=a.relatedTarget;!L["text/html"]&&!L["text/plain"]&&j&&(i=j.value||j.outerHTML||j.innerHTML)&&(h=j.value||j.textContent||j.innerText)?(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",h),i!==h&&a.clipboardData.setData("text/html",i)):!L["text/plain"]&&a.target&&(h=a.target.getAttribute("data-clipboard-text"))&&(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",h));break;case"aftercopy":Fb.clearData(),b&&b!==vb()&&b.focus&&b.focus();break;case"_mouseover":Fb.focus(b),O.bubbleEvents===!0&&e&&(b&&b!==a.relatedTarget&&!B(a.relatedTarget,b)&&kb(w({},a,{type:"mouseenter",bubbles:!1,cancelable:!1})),kb(w({},a,{type:"mouseover"})));break;case"_mouseout":Fb.blur(),O.bubbleEvents===!0&&e&&(b&&b!==a.relatedTarget&&!B(a.relatedTarget,b)&&kb(w({},a,{type:"mouseleave",bubbles:!1,cancelable:!1})),kb(w({},a,{type:"mouseout"})));break;case"_mousedown":wb(b,O.activeClass),O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_mouseup":xb(b,O.activeClass),O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_click":d=null,O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_mousemove":O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}))}return/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)?!0:void 0},kb=function(a){if(a&&"string"==typeof a.type&&a){var b,c=a.target||null,d=c&&c.ownerDocument||f,g={view:d.defaultView||e,canBubble:!0,cancelable:!0,detail:"click"===a.type?1:0,button:"number"==typeof a.which?a.which-1:"number"==typeof a.button?a.button:d.createEvent?0:1},h=w(g,a);c&&d.createEvent&&c.dispatchEvent&&(h=[h.type,h.canBubble,h.cancelable,h.view,h.detail,h.screenX,h.screenY,h.clientX,h.clientY,h.ctrlKey,h.altKey,h.shiftKey,h.metaKey,h.button,h.relatedTarget],b=d.createEvent("MouseEvents"),b.initMouseEvent&&(b.initMouseEvent.apply(b,h),b._source="js",c.dispatchEvent(b)))}},lb=function(){var a=f.createElement("div");return a.id=O.containerId,a.className=O.containerClass,a.style.position="absolute",a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px",a.style.zIndex=""+Db(O.zIndex),a},mb=function(a){for(var b=a&&a.parentNode;b&&"OBJECT"===b.nodeName&&b.parentNode;)b=b.parentNode;return b||null},nb=function(){var a,b=I.bridge,c=mb(b);if(!b){var d=ub(e.location.host,O),g="never"===d?"none":"all",h=sb(O),i=O.swfPath+rb(O.swfPath,O);c=lb();var j=f.createElement("div");c.appendChild(j),f.body.appendChild(c);var k=f.createElement("div"),l="activex"===I.pluginType;k.innerHTML='',b=k.firstChild,k=null,u(b).ZeroClipboard=Fb,c.replaceChild(b,j)}return b||(b=f[O.swfObjectId],b&&(a=b.length)&&(b=b[a-1]),!b&&c&&(b=c.firstChild)),I.bridge=b||null,b},ob=function(){var a=I.bridge;if(a){var b=mb(a);b&&("activex"===I.pluginType&&"readyState"in a?(a.style.display="none",function c(){if(4===a.readyState){for(var d in a)"function"==typeof a[d]&&(a[d]=null);a.parentNode&&a.parentNode.removeChild(a),b.parentNode&&b.parentNode.removeChild(b)}else h(c,10)}()):(a.parentNode&&a.parentNode.removeChild(a),b.parentNode&&b.parentNode.removeChild(b))),I.ready=null,I.bridge=null,I.deactivated=null}},pb=function(a){var b={},c={};if("object"==typeof a&&a){for(var d in a)if(d&&s.call(a,d)&&"string"==typeof a[d]&&a[d])switch(d.toLowerCase()){case"text/plain":case"text":case"air:text":case"flash:text":b.text=a[d],c.text=d;break;case"text/html":case"html":case"air:html":case"flash:html":b.html=a[d],c.html=d;break;case"application/rtf":case"text/rtf":case"rtf":case"richtext":case"air:rtf":case"flash:rtf":b.rtf=a[d],c.rtf=d}return{data:b,formatMap:c}}},qb=function(a,b){if("object"!=typeof a||!a||"object"!=typeof b||!b)return a;var c={};for(var d in a)if(s.call(a,d)){if("success"!==d&&"data"!==d){c[d]=a[d];continue}c[d]={};var e=a[d];for(var f in e)f&&s.call(e,f)&&s.call(b,f)&&(c[d][b[f]]=e[f])}return c},rb=function(a,b){var c=null==b||b&&b.cacheBust===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+p():""},sb=function(a){var b,c,d,f,g="",h=[];if(a.trustedDomains&&("string"==typeof a.trustedDomains?f=[a.trustedDomains]:"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(f=a.trustedDomains)),f&&f.length)for(b=0,c=f.length;c>b;b++)if(s.call(f,b)&&f[b]&&"string"==typeof f[b]){if(d=tb(f[b]),!d)continue;if("*"===d){h.length=0,h.push(d);break}h.push.apply(h,[d,"//"+d,e.location.protocol+"//"+d])}return h.length&&(g+="trustedOrigins="+i(h.join(","))),a.forceEnhancedClipboard===!0&&(g+=(g?"&":"")+"forceEnhancedClipboard=true"),"string"==typeof a.swfObjectId&&a.swfObjectId&&(g+=(g?"&":"")+"swfObjectId="+i(a.swfObjectId)),g},tb=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},ub=function(){var a=function(a){var b,c,d,e=[];if("string"==typeof a&&(a=[a]),"object"!=typeof a||!a||"number"!=typeof a.length)return e;for(b=0,c=a.length;c>b;b++)if(s.call(a,b)&&(d=tb(a[b]))){if("*"===d){e.length=0,e.push("*");break}-1===e.indexOf(d)&&e.push(d)}return e};return function(b,c){var d=tb(c.swfPath);null===d&&(d=b);var e=a(c.trustedDomains),f=e.length;if(f>0){if(1===f&&"*"===e[0])return"always";if(-1!==e.indexOf(b))return 1===f&&b===d?"sameDomain":"always"}return"never"}}(),vb=function(){try{return f.activeElement}catch(a){return null}},wb=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)||a.classList.add(b),a;if(b&&"string"==typeof b){var c=(b||"").split(/\s+/);if(1===a.nodeType)if(a.className){for(var d=" "+a.className+" ",e=a.className,f=0,g=c.length;g>f;f++)d.indexOf(" "+c[f]+" ")<0&&(e+=" "+c[f]);a.className=e.replace(/^\s+|\s+$/g,"")}else a.className=b}return a},xb=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)&&a.classList.remove(b),a;if("string"==typeof b&&b){var c=b.split(/\s+/);if(1===a.nodeType&&a.className){for(var d=(" "+a.className+" ").replace(/[\n\t]/g," "),e=0,f=c.length;f>e;e++)d=d.replace(" "+c[e]+" "," ");a.className=d.replace(/^\s+|\s+$/g,"")}}return a},yb=function(a,b){var c=e.getComputedStyle(a,null).getPropertyValue(b);return"cursor"!==b||c&&"auto"!==c||"A"!==a.nodeName?c:"pointer"},zb=function(){var a,b,c,d=1;return"function"==typeof f.body.getBoundingClientRect&&(a=f.body.getBoundingClientRect(),b=a.right-a.left,c=f.body.offsetWidth,d=o(b/c*100)/100),d},Ab=function(a){var b={left:0,top:0,width:0,height:0};if(a.getBoundingClientRect){var c,d,g,h=a.getBoundingClientRect();"pageXOffset"in e&&"pageYOffset"in e?(c=e.pageXOffset,d=e.pageYOffset):(g=zb(),c=o(f.documentElement.scrollLeft/g),d=o(f.documentElement.scrollTop/g));var i=f.documentElement.clientLeft||0,j=f.documentElement.clientTop||0;b.left=h.left+c-i,b.top=h.top+d-j,b.width="width"in h?h.width:h.right-h.left,b.height="height"in h?h.height:h.bottom-h.top}return b},Bb=function(){var a;if(c&&(a=mb(I.bridge))){var b=Ab(c);w(a.style,{width:b.width+"px",height:b.height+"px",top:b.top+"px",left:b.left+"px",zIndex:""+Db(O.zIndex)})}},Cb=function(a){I.ready===!0&&(I.bridge&&"function"==typeof I.bridge.setHandCursor?I.bridge.setHandCursor(a):I.ready=!1)},Db=function(a){if(/^(?:auto|inherit)$/.test(a))return a;var b;return"number"!=typeof a||n(a)?"string"==typeof a&&(b=Db(l(a,10))):b=a,"number"==typeof b?b:"auto"},Eb=function(a){function b(a){var b=a.match(/[\d]+/g);return b.length=3,b.join(".")}function c(a){return!!a&&(a=a.toLowerCase())&&(/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(a)||"chrome.plugin"===a.slice(-13))}function d(a){a&&(i=!0,a.version&&(l=b(a.version)),!l&&a.description&&(l=b(a.description)),a.filename&&(k=c(a.filename)))}var e,f,h,i=!1,j=!1,k=!1,l="";if(g.plugins&&g.plugins.length)e=g.plugins["Shockwave Flash"],d(e),g.plugins["Shockwave Flash 2.0"]&&(i=!0,l="2.0.0.11");else if(g.mimeTypes&&g.mimeTypes.length)h=g.mimeTypes["application/x-shockwave-flash"],e=h&&h.enabledPlugin,d(e);else if("undefined"!=typeof a){j=!0;try{f=new a("ShockwaveFlash.ShockwaveFlash.7"),i=!0,l=b(f.GetVariable("$version"))}catch(n){try{f=new a("ShockwaveFlash.ShockwaveFlash.6"),i=!0,l="6.0.21"}catch(o){try{f=new a("ShockwaveFlash.ShockwaveFlash"),i=!0,l=b(f.GetVariable("$version"))}catch(p){j=!1}}}}I.disabled=i!==!0,I.outdated=l&&m(l)= 0) {\n _setTimeout(function() {\n if (typeof _flashState.deactivated !== \"boolean\") {\n _flashState.deactivated = true;\n }\n if (_flashState.deactivated === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-deactivated\"\n });\n }\n }, maxWait);\n }\n _flashState.overdue = false;\n _embedSwf();\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.destroy`.\n * @private\n */\n var _destroy = function() {\n ZeroClipboard.clearData();\n ZeroClipboard.blur();\n ZeroClipboard.emit(\"destroy\");\n _unembedSwf();\n ZeroClipboard.off();\n };\n /**\n * The underlying implementation of `ZeroClipboard.setData`.\n * @private\n */\n var _setData = function(format, data) {\n var dataObj;\n if (typeof format === \"object\" && format && typeof data === \"undefined\") {\n dataObj = format;\n ZeroClipboard.clearData();\n } else if (typeof format === \"string\" && format) {\n dataObj = {};\n dataObj[format] = data;\n } else {\n return;\n }\n for (var dataFormat in dataObj) {\n if (typeof dataFormat === \"string\" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === \"string\" && dataObj[dataFormat]) {\n _clipData[dataFormat] = dataObj[dataFormat];\n }\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.clearData`.\n * @private\n */\n var _clearData = function(format) {\n if (typeof format === \"undefined\") {\n _deleteOwnProperties(_clipData);\n _clipDataFormatMap = null;\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n delete _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.getData`.\n * @private\n */\n var _getData = function(format) {\n if (typeof format === \"undefined\") {\n return _deepCopy(_clipData);\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n return _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.\n * @private\n */\n var _focus = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.activeClass);\n if (_currentElement !== element) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n }\n }\n _currentElement = element;\n _addClass(element, _globalConfig.hoverClass);\n var newTitle = element.getAttribute(\"title\") || _globalConfig.title;\n if (typeof newTitle === \"string\" && newTitle) {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.setAttribute(\"title\", newTitle);\n }\n }\n var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, \"cursor\") === \"pointer\";\n _setHandCursor(useHandCursor);\n _reposition();\n };\n /**\n * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.\n * @private\n */\n var _blur = function() {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.removeAttribute(\"title\");\n htmlBridge.style.left = \"0px\";\n htmlBridge.style.top = \"-9999px\";\n htmlBridge.style.width = \"1px\";\n htmlBridge.style.top = \"1px\";\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n _removeClass(_currentElement, _globalConfig.activeClass);\n _currentElement = null;\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.activeElement`.\n * @private\n */\n var _activeElement = function() {\n return _currentElement || null;\n };\n /**\n * Check if a value is a valid HTML4 `ID` or `Name` token.\n * @private\n */\n var _isValidHtml4Id = function(id) {\n return typeof id === \"string\" && id && /^[A-Za-z][A-Za-z0-9_:\\-\\.]*$/.test(id);\n };\n /**\n * Create or update an `event` object, based on the `eventType`.\n * @private\n */\n var _createEvent = function(event) {\n var eventType;\n if (typeof event === \"string\" && event) {\n eventType = event;\n event = {};\n } else if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n eventType = event.type;\n }\n if (!eventType) {\n return;\n }\n if (!event.target && /^(copy|aftercopy|_click)$/.test(eventType.toLowerCase())) {\n event.target = _copyTarget;\n }\n _extend(event, {\n type: eventType.toLowerCase(),\n target: event.target || _currentElement || null,\n relatedTarget: event.relatedTarget || null,\n currentTarget: _flashState && _flashState.bridge || null,\n timeStamp: event.timeStamp || _now() || null\n });\n var msg = _eventMessages[event.type];\n if (event.type === \"error\" && event.name && msg) {\n msg = msg[event.name];\n }\n if (msg) {\n event.message = msg;\n }\n if (event.type === \"ready\") {\n _extend(event, {\n target: null,\n version: _flashState.version\n });\n }\n if (event.type === \"error\") {\n if (/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(event.name)) {\n _extend(event, {\n target: null,\n minimumVersion: _minimumFlashVersion\n });\n }\n if (/^flash-(outdated|unavailable|deactivated|overdue)$/.test(event.name)) {\n _extend(event, {\n version: _flashState.version\n });\n }\n }\n if (event.type === \"copy\") {\n event.clipboardData = {\n setData: ZeroClipboard.setData,\n clearData: ZeroClipboard.clearData\n };\n }\n if (event.type === \"aftercopy\") {\n event = _mapClipResultsFromFlash(event, _clipDataFormatMap);\n }\n if (event.target && !event.relatedTarget) {\n event.relatedTarget = _getRelatedTarget(event.target);\n }\n event = _addMouseData(event);\n return event;\n };\n /**\n * Get a relatedTarget from the target's `data-clipboard-target` attribute\n * @private\n */\n var _getRelatedTarget = function(targetEl) {\n var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute(\"data-clipboard-target\");\n return relatedTargetId ? _document.getElementById(relatedTargetId) : null;\n };\n /**\n * Add element and position data to `MouseEvent` instances\n * @private\n */\n var _addMouseData = function(event) {\n if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n var srcElement = event.target;\n var fromElement = event.type === \"_mouseover\" && event.relatedTarget ? event.relatedTarget : undefined;\n var toElement = event.type === \"_mouseout\" && event.relatedTarget ? event.relatedTarget : undefined;\n var pos = _getDOMObjectPosition(srcElement);\n var screenLeft = _window.screenLeft || _window.screenX || 0;\n var screenTop = _window.screenTop || _window.screenY || 0;\n var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;\n var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;\n var pageX = pos.left + (typeof event._stageX === \"number\" ? event._stageX : 0);\n var pageY = pos.top + (typeof event._stageY === \"number\" ? event._stageY : 0);\n var clientX = pageX - scrollLeft;\n var clientY = pageY - scrollTop;\n var screenX = screenLeft + clientX;\n var screenY = screenTop + clientY;\n var moveX = typeof event.movementX === \"number\" ? event.movementX : 0;\n var moveY = typeof event.movementY === \"number\" ? event.movementY : 0;\n delete event._stageX;\n delete event._stageY;\n _extend(event, {\n srcElement: srcElement,\n fromElement: fromElement,\n toElement: toElement,\n screenX: screenX,\n screenY: screenY,\n pageX: pageX,\n pageY: pageY,\n clientX: clientX,\n clientY: clientY,\n x: clientX,\n y: clientY,\n movementX: moveX,\n movementY: moveY,\n offsetX: 0,\n offsetY: 0,\n layerX: 0,\n layerY: 0\n });\n }\n return event;\n };\n /**\n * Determine if an event's registered handlers should be execute synchronously or asynchronously.\n *\n * @returns {boolean}\n * @private\n */\n var _shouldPerformAsync = function(event) {\n var eventType = event && typeof event.type === \"string\" && event.type || \"\";\n return !/^(?:(?:before)?copy|destroy)$/.test(eventType);\n };\n /**\n * Control if a callback should be executed asynchronously or not.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallback = function(func, context, args, async) {\n if (async) {\n _setTimeout(function() {\n func.apply(context, args);\n }, 0);\n } else {\n func.apply(context, args);\n }\n };\n /**\n * Handle the actual dispatching of events to client instances.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallbacks = function(event) {\n if (!(typeof event === \"object\" && event && event.type)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = _handlers[\"*\"] || [];\n var specificTypeHandlers = _handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n return this;\n };\n /**\n * Preprocess any special behaviors, reactions, or state changes after receiving this event.\n * Executes only once per event emitted, NOT once per client.\n * @private\n */\n var _preprocessEvent = function(event) {\n var element = event.target || _currentElement || null;\n var sourceIsSwf = event._source === \"swf\";\n delete event._source;\n var flashErrorNames = [ \"flash-disabled\", \"flash-outdated\", \"flash-unavailable\", \"flash-deactivated\", \"flash-overdue\" ];\n switch (event.type) {\n case \"error\":\n if (flashErrorNames.indexOf(event.name) !== -1) {\n _extend(_flashState, {\n disabled: event.name === \"flash-disabled\",\n outdated: event.name === \"flash-outdated\",\n unavailable: event.name === \"flash-unavailable\",\n deactivated: event.name === \"flash-deactivated\",\n overdue: event.name === \"flash-overdue\",\n ready: false\n });\n }\n break;\n\n case \"ready\":\n var wasDeactivated = _flashState.deactivated === true;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n unavailable: false,\n deactivated: false,\n overdue: wasDeactivated,\n ready: !wasDeactivated\n });\n break;\n\n case \"beforecopy\":\n _copyTarget = element;\n break;\n\n case \"copy\":\n var textContent, htmlContent, targetEl = event.relatedTarget;\n if (!(_clipData[\"text/html\"] || _clipData[\"text/plain\"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n if (htmlContent !== textContent) {\n event.clipboardData.setData(\"text/html\", htmlContent);\n }\n } else if (!_clipData[\"text/plain\"] && event.target && (textContent = event.target.getAttribute(\"data-clipboard-text\"))) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n }\n break;\n\n case \"aftercopy\":\n ZeroClipboard.clearData();\n if (element && element !== _safeActiveElement() && element.focus) {\n element.focus();\n }\n break;\n\n case \"_mouseover\":\n ZeroClipboard.focus(element);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseenter\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseover\"\n }));\n }\n break;\n\n case \"_mouseout\":\n ZeroClipboard.blur();\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseleave\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseout\"\n }));\n }\n break;\n\n case \"_mousedown\":\n _addClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mouseup\":\n _removeClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_click\":\n _copyTarget = null;\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mousemove\":\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n }\n if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n return true;\n }\n };\n /**\n * Dispatch a synthetic MouseEvent.\n *\n * @returns `undefined`\n * @private\n */\n var _fireMouseEvent = function(event) {\n if (!(event && typeof event.type === \"string\" && event)) {\n return;\n }\n var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {\n view: doc.defaultView || _window,\n canBubble: true,\n cancelable: true,\n detail: event.type === \"click\" ? 1 : 0,\n button: typeof event.which === \"number\" ? event.which - 1 : typeof event.button === \"number\" ? event.button : doc.createEvent ? 0 : 1\n }, args = _extend(defaults, event);\n if (!target) {\n return;\n }\n if (doc.createEvent && target.dispatchEvent) {\n 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 ];\n e = doc.createEvent(\"MouseEvents\");\n if (e.initMouseEvent) {\n e.initMouseEvent.apply(e, args);\n e._source = \"js\";\n target.dispatchEvent(e);\n }\n }\n };\n /**\n * Create the HTML bridge element to embed the Flash object into.\n * @private\n */\n var _createHtmlBridge = function() {\n var container = _document.createElement(\"div\");\n container.id = _globalConfig.containerId;\n container.className = _globalConfig.containerClass;\n container.style.position = \"absolute\";\n container.style.left = \"0px\";\n container.style.top = \"-9999px\";\n container.style.width = \"1px\";\n container.style.height = \"1px\";\n container.style.zIndex = \"\" + _getSafeZIndex(_globalConfig.zIndex);\n return container;\n };\n /**\n * Get the HTML element container that wraps the Flash bridge object/element.\n * @private\n */\n var _getHtmlBridge = function(flashBridge) {\n var htmlBridge = flashBridge && flashBridge.parentNode;\n while (htmlBridge && htmlBridge.nodeName === \"OBJECT\" && htmlBridge.parentNode) {\n htmlBridge = htmlBridge.parentNode;\n }\n return htmlBridge || null;\n };\n /**\n * Create the SWF object.\n *\n * @returns The SWF object reference.\n * @private\n */\n var _embedSwf = function() {\n var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);\n if (!flashBridge) {\n var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);\n var allowNetworking = allowScriptAccess === \"never\" ? \"none\" : \"all\";\n var flashvars = _vars(_globalConfig);\n var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);\n container = _createHtmlBridge();\n var divToBeReplaced = _document.createElement(\"div\");\n container.appendChild(divToBeReplaced);\n _document.body.appendChild(container);\n var tmpDiv = _document.createElement(\"div\");\n var oldIE = _flashState.pluginType === \"activex\";\n tmpDiv.innerHTML = '\";\n flashBridge = tmpDiv.firstChild;\n tmpDiv = null;\n _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;\n container.replaceChild(flashBridge, divToBeReplaced);\n }\n if (!flashBridge) {\n flashBridge = _document[_globalConfig.swfObjectId];\n if (flashBridge && (len = flashBridge.length)) {\n flashBridge = flashBridge[len - 1];\n }\n if (!flashBridge && container) {\n flashBridge = container.firstChild;\n }\n }\n _flashState.bridge = flashBridge || null;\n return flashBridge;\n };\n /**\n * Destroy the SWF object.\n * @private\n */\n var _unembedSwf = function() {\n var flashBridge = _flashState.bridge;\n if (flashBridge) {\n var htmlBridge = _getHtmlBridge(flashBridge);\n if (htmlBridge) {\n if (_flashState.pluginType === \"activex\" && \"readyState\" in flashBridge) {\n flashBridge.style.display = \"none\";\n (function removeSwfFromIE() {\n if (flashBridge.readyState === 4) {\n for (var prop in flashBridge) {\n if (typeof flashBridge[prop] === \"function\") {\n flashBridge[prop] = null;\n }\n }\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n } else {\n _setTimeout(removeSwfFromIE, 10);\n }\n })();\n } else {\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n }\n }\n _flashState.ready = null;\n _flashState.bridge = null;\n _flashState.deactivated = null;\n }\n };\n /**\n * Map the data format names of the \"clipData\" to Flash-friendly names.\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipDataToFlash = function(clipData) {\n var newClipData = {}, formatMap = {};\n if (!(typeof clipData === \"object\" && clipData)) {\n return;\n }\n for (var dataFormat in clipData) {\n if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === \"string\" && clipData[dataFormat]) {\n switch (dataFormat.toLowerCase()) {\n case \"text/plain\":\n case \"text\":\n case \"air:text\":\n case \"flash:text\":\n newClipData.text = clipData[dataFormat];\n formatMap.text = dataFormat;\n break;\n\n case \"text/html\":\n case \"html\":\n case \"air:html\":\n case \"flash:html\":\n newClipData.html = clipData[dataFormat];\n formatMap.html = dataFormat;\n break;\n\n case \"application/rtf\":\n case \"text/rtf\":\n case \"rtf\":\n case \"richtext\":\n case \"air:rtf\":\n case \"flash:rtf\":\n newClipData.rtf = clipData[dataFormat];\n formatMap.rtf = dataFormat;\n break;\n\n default:\n break;\n }\n }\n }\n return {\n data: newClipData,\n formatMap: formatMap\n };\n };\n /**\n * Map the data format names from Flash-friendly names back to their original \"clipData\" names (via a format mapping).\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipResultsFromFlash = function(clipResults, formatMap) {\n if (!(typeof clipResults === \"object\" && clipResults && typeof formatMap === \"object\" && formatMap)) {\n return clipResults;\n }\n var newResults = {};\n for (var prop in clipResults) {\n if (_hasOwn.call(clipResults, prop)) {\n if (prop !== \"success\" && prop !== \"data\") {\n newResults[prop] = clipResults[prop];\n continue;\n }\n newResults[prop] = {};\n var tmpHash = clipResults[prop];\n for (var dataFormat in tmpHash) {\n if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {\n newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];\n }\n }\n }\n }\n return newResults;\n };\n /**\n * Will look at a path, and will create a \"?noCache={time}\" or \"&noCache={time}\"\n * query param string to return. Does NOT append that string to the original path.\n * This is useful because ExternalInterface often breaks when a Flash SWF is cached.\n *\n * @returns The `noCache` query param with necessary \"?\"/\"&\" prefix.\n * @private\n */\n var _cacheBust = function(path, options) {\n var cacheBust = options == null || options && options.cacheBust === true;\n if (cacheBust) {\n return (path.indexOf(\"?\") === -1 ? \"?\" : \"&\") + \"noCache=\" + _now();\n } else {\n return \"\";\n }\n };\n /**\n * Creates a query string for the FlashVars param.\n * Does NOT include the cache-busting query param.\n *\n * @returns FlashVars query string\n * @private\n */\n var _vars = function(options) {\n var i, len, domain, domains, str = \"\", trustedOriginsExpanded = [];\n if (options.trustedDomains) {\n if (typeof options.trustedDomains === \"string\") {\n domains = [ options.trustedDomains ];\n } else if (typeof options.trustedDomains === \"object\" && \"length\" in options.trustedDomains) {\n domains = options.trustedDomains;\n }\n }\n if (domains && domains.length) {\n for (i = 0, len = domains.length; i < len; i++) {\n if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === \"string\") {\n domain = _extractDomain(domains[i]);\n if (!domain) {\n continue;\n }\n if (domain === \"*\") {\n trustedOriginsExpanded.length = 0;\n trustedOriginsExpanded.push(domain);\n break;\n }\n trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, \"//\" + domain, _window.location.protocol + \"//\" + domain ]);\n }\n }\n }\n if (trustedOriginsExpanded.length) {\n str += \"trustedOrigins=\" + _encodeURIComponent(trustedOriginsExpanded.join(\",\"));\n }\n if (options.forceEnhancedClipboard === true) {\n str += (str ? \"&\" : \"\") + \"forceEnhancedClipboard=true\";\n }\n if (typeof options.swfObjectId === \"string\" && options.swfObjectId) {\n str += (str ? \"&\" : \"\") + \"swfObjectId=\" + _encodeURIComponent(options.swfObjectId);\n }\n return str;\n };\n /**\n * Extract the domain (e.g. \"github.com\") from an origin (e.g. \"https://github.com\") or\n * URL (e.g. \"https://github.com/zeroclipboard/zeroclipboard/\").\n *\n * @returns the domain\n * @private\n */\n var _extractDomain = function(originOrUrl) {\n if (originOrUrl == null || originOrUrl === \"\") {\n return null;\n }\n originOrUrl = originOrUrl.replace(/^\\s+|\\s+$/g, \"\");\n if (originOrUrl === \"\") {\n return null;\n }\n var protocolIndex = originOrUrl.indexOf(\"//\");\n originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);\n var pathIndex = originOrUrl.indexOf(\"/\");\n originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);\n if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === \".swf\") {\n return null;\n }\n return originOrUrl || null;\n };\n /**\n * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.\n *\n * @returns The appropriate script access level.\n * @private\n */\n var _determineScriptAccess = function() {\n var _extractAllDomains = function(origins) {\n var i, len, tmp, resultsArray = [];\n if (typeof origins === \"string\") {\n origins = [ origins ];\n }\n if (!(typeof origins === \"object\" && origins && typeof origins.length === \"number\")) {\n return resultsArray;\n }\n for (i = 0, len = origins.length; i < len; i++) {\n if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {\n if (tmp === \"*\") {\n resultsArray.length = 0;\n resultsArray.push(\"*\");\n break;\n }\n if (resultsArray.indexOf(tmp) === -1) {\n resultsArray.push(tmp);\n }\n }\n }\n return resultsArray;\n };\n return function(currentDomain, configOptions) {\n var swfDomain = _extractDomain(configOptions.swfPath);\n if (swfDomain === null) {\n swfDomain = currentDomain;\n }\n var trustedDomains = _extractAllDomains(configOptions.trustedDomains);\n var len = trustedDomains.length;\n if (len > 0) {\n if (len === 1 && trustedDomains[0] === \"*\") {\n return \"always\";\n }\n if (trustedDomains.indexOf(currentDomain) !== -1) {\n if (len === 1 && currentDomain === swfDomain) {\n return \"sameDomain\";\n }\n return \"always\";\n }\n }\n return \"never\";\n };\n }();\n /**\n * Get the currently active/focused DOM element.\n *\n * @returns the currently active/focused element, or `null`\n * @private\n */\n var _safeActiveElement = function() {\n try {\n return _document.activeElement;\n } catch (err) {\n return null;\n }\n };\n /**\n * Add a class to an element, if it doesn't already have it.\n *\n * @returns The element, with its new class added.\n * @private\n */\n var _addClass = function(element, value) {\n if (!element || element.nodeType !== 1) {\n return element;\n }\n if (element.classList) {\n if (!element.classList.contains(value)) {\n element.classList.add(value);\n }\n return element;\n }\n if (value && typeof value === \"string\") {\n var classNames = (value || \"\").split(/\\s+/);\n if (element.nodeType === 1) {\n if (!element.className) {\n element.className = value;\n } else {\n var className = \" \" + element.className + \" \", setClass = element.className;\n for (var c = 0, cl = classNames.length; c < cl; c++) {\n if (className.indexOf(\" \" + classNames[c] + \" \") < 0) {\n setClass += \" \" + classNames[c];\n }\n }\n element.className = setClass.replace(/^\\s+|\\s+$/g, \"\");\n }\n }\n }\n return element;\n };\n /**\n * Remove a class from an element, if it has it.\n *\n * @returns The element, with its class removed.\n * @private\n */\n var _removeClass = function(element, value) {\n if (!element || element.nodeType !== 1) {\n return element;\n }\n if (element.classList) {\n if (element.classList.contains(value)) {\n element.classList.remove(value);\n }\n return element;\n }\n if (typeof value === \"string\" && value) {\n var classNames = value.split(/\\s+/);\n if (element.nodeType === 1 && element.className) {\n var className = (\" \" + element.className + \" \").replace(/[\\n\\t]/g, \" \");\n for (var c = 0, cl = classNames.length; c < cl; c++) {\n className = className.replace(\" \" + classNames[c] + \" \", \" \");\n }\n element.className = className.replace(/^\\s+|\\s+$/g, \"\");\n }\n }\n return element;\n };\n /**\n * Attempt to interpret the element's CSS styling. If `prop` is `\"cursor\"`,\n * then we assume that it should be a hand (\"pointer\") cursor if the element\n * is an anchor element (\"a\" tag).\n *\n * @returns The computed style property.\n * @private\n */\n var _getStyle = function(el, prop) {\n var value = _window.getComputedStyle(el, null).getPropertyValue(prop);\n if (prop === \"cursor\") {\n if (!value || value === \"auto\") {\n if (el.nodeName === \"A\") {\n return \"pointer\";\n }\n }\n }\n return value;\n };\n /**\n * Get the zoom factor of the browser. Always returns `1.0`, except at\n * non-default zoom levels in IE<8 and some older versions of WebKit.\n *\n * @returns Floating unit percentage of the zoom factor (e.g. 150% = `1.5`).\n * @private\n */\n var _getZoomFactor = function() {\n var rect, physicalWidth, logicalWidth, zoomFactor = 1;\n if (typeof _document.body.getBoundingClientRect === \"function\") {\n rect = _document.body.getBoundingClientRect();\n physicalWidth = rect.right - rect.left;\n logicalWidth = _document.body.offsetWidth;\n zoomFactor = _round(physicalWidth / logicalWidth * 100) / 100;\n }\n return zoomFactor;\n };\n /**\n * Get the DOM positioning info of an element.\n *\n * @returns Object containing the element's position, width, and height.\n * @private\n */\n var _getDOMObjectPosition = function(obj) {\n var info = {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n };\n if (obj.getBoundingClientRect) {\n var rect = obj.getBoundingClientRect();\n var pageXOffset, pageYOffset, zoomFactor;\n if (\"pageXOffset\" in _window && \"pageYOffset\" in _window) {\n pageXOffset = _window.pageXOffset;\n pageYOffset = _window.pageYOffset;\n } else {\n zoomFactor = _getZoomFactor();\n pageXOffset = _round(_document.documentElement.scrollLeft / zoomFactor);\n pageYOffset = _round(_document.documentElement.scrollTop / zoomFactor);\n }\n var leftBorderWidth = _document.documentElement.clientLeft || 0;\n var topBorderWidth = _document.documentElement.clientTop || 0;\n info.left = rect.left + pageXOffset - leftBorderWidth;\n info.top = rect.top + pageYOffset - topBorderWidth;\n info.width = \"width\" in rect ? rect.width : rect.right - rect.left;\n info.height = \"height\" in rect ? rect.height : rect.bottom - rect.top;\n }\n return info;\n };\n /**\n * Reposition the Flash object to cover the currently activated element.\n *\n * @returns `undefined`\n * @private\n */\n var _reposition = function() {\n var htmlBridge;\n if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {\n var pos = _getDOMObjectPosition(_currentElement);\n _extend(htmlBridge.style, {\n width: pos.width + \"px\",\n height: pos.height + \"px\",\n top: pos.top + \"px\",\n left: pos.left + \"px\",\n zIndex: \"\" + _getSafeZIndex(_globalConfig.zIndex)\n });\n }\n };\n /**\n * Sends a signal to the Flash object to display the hand cursor if `true`.\n *\n * @returns `undefined`\n * @private\n */\n var _setHandCursor = function(enabled) {\n if (_flashState.ready === true) {\n if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === \"function\") {\n _flashState.bridge.setHandCursor(enabled);\n } else {\n _flashState.ready = false;\n }\n }\n };\n /**\n * Get a safe value for `zIndex`\n *\n * @returns an integer, or \"auto\"\n * @private\n */\n var _getSafeZIndex = function(val) {\n if (/^(?:auto|inherit)$/.test(val)) {\n return val;\n }\n var zIndex;\n if (typeof val === \"number\" && !_isNaN(val)) {\n zIndex = val;\n } else if (typeof val === \"string\") {\n zIndex = _getSafeZIndex(_parseInt(val, 10));\n }\n return typeof zIndex === \"number\" ? zIndex : \"auto\";\n };\n /**\n * Detect the Flash Player status, version, and plugin type.\n *\n * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}\n * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}\n *\n * @returns `undefined`\n * @private\n */\n var _detectFlashSupport = function(ActiveXObject) {\n var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = \"\";\n /**\n * Derived from Apple's suggested sniffer.\n * @param {String} desc e.g. \"Shockwave Flash 7.0 r61\"\n * @returns {String} \"7.0.61\"\n * @private\n */\n function parseFlashVersion(desc) {\n var matches = desc.match(/[\\d]+/g);\n matches.length = 3;\n return matches.join(\".\");\n }\n function isPepperFlash(flashPlayerFileName) {\n return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\\.dll|libpepflashplayer\\.so|pepperflashplayer\\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === \"chrome.plugin\");\n }\n function inspectPlugin(plugin) {\n if (plugin) {\n hasFlash = true;\n if (plugin.version) {\n flashVersion = parseFlashVersion(plugin.version);\n }\n if (!flashVersion && plugin.description) {\n flashVersion = parseFlashVersion(plugin.description);\n }\n if (plugin.filename) {\n isPPAPI = isPepperFlash(plugin.filename);\n }\n }\n }\n if (_navigator.plugins && _navigator.plugins.length) {\n plugin = _navigator.plugins[\"Shockwave Flash\"];\n inspectPlugin(plugin);\n if (_navigator.plugins[\"Shockwave Flash 2.0\"]) {\n hasFlash = true;\n flashVersion = \"2.0.0.11\";\n }\n } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {\n mimeType = _navigator.mimeTypes[\"application/x-shockwave-flash\"];\n plugin = mimeType && mimeType.enabledPlugin;\n inspectPlugin(plugin);\n } else if (typeof ActiveXObject !== \"undefined\") {\n isActiveX = true;\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.7\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e1) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.6\");\n hasFlash = true;\n flashVersion = \"6.0.21\";\n } catch (e2) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e3) {\n isActiveX = false;\n }\n }\n }\n }\n _flashState.disabled = hasFlash !== true;\n _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);\n _flashState.version = flashVersion || \"0.0.0\";\n _flashState.pluginType = isPPAPI ? \"pepper\" : isActiveX ? \"activex\" : hasFlash ? \"netscape\" : \"unknown\";\n };\n /**\n * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.\n */\n _detectFlashSupport(_ActiveXObject);\n /**\n * A shell constructor for `ZeroClipboard` client instances.\n *\n * @constructor\n */\n var ZeroClipboard = function() {\n if (!(this instanceof ZeroClipboard)) {\n return new ZeroClipboard();\n }\n if (typeof ZeroClipboard._createClient === \"function\") {\n ZeroClipboard._createClient.apply(this, _args(arguments));\n }\n };\n /**\n * The ZeroClipboard library's version number.\n *\n * @static\n * @readonly\n * @property {string}\n */\n _defineProperty(ZeroClipboard, \"version\", {\n value: \"2.1.6\",\n writable: false,\n configurable: true,\n enumerable: true\n });\n /**\n * Update or get a copy of the ZeroClipboard global configuration.\n * Returns a copy of the current/updated configuration.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.config = function() {\n return _config.apply(this, _args(arguments));\n };\n /**\n * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.state = function() {\n return _state.apply(this, _args(arguments));\n };\n /**\n * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.\n *\n * @returns Boolean\n * @static\n */\n ZeroClipboard.isFlashUnusable = function() {\n return _isFlashUnusable.apply(this, _args(arguments));\n };\n /**\n * Register an event listener.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.on = function() {\n return _on.apply(this, _args(arguments));\n };\n /**\n * Unregister an event listener.\n * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all listeners for every event type.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.off = function() {\n return _off.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType`.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.handlers = function() {\n return _listeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n * @static\n */\n ZeroClipboard.emit = function() {\n return _emit.apply(this, _args(arguments));\n };\n /**\n * Create and embed the Flash object.\n *\n * @returns The Flash object\n * @static\n */\n ZeroClipboard.create = function() {\n return _create.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything, including the embedded Flash object.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.destroy = function() {\n return _destroy.apply(this, _args(arguments));\n };\n /**\n * Set the pending data for clipboard injection.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.setData = function() {\n return _setData.apply(this, _args(arguments));\n };\n /**\n * Clear the pending data for clipboard injection.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.clearData = function() {\n return _clearData.apply(this, _args(arguments));\n };\n /**\n * Get a copy of the pending data for clipboard injection.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n * @static\n */\n ZeroClipboard.getData = function() {\n return _getData.apply(this, _args(arguments));\n };\n /**\n * Sets the current HTML object that the Flash object should overlay. This will put the global\n * Flash object on top of the current element; depending on the setup, this may also set the\n * pending clipboard text data as well as the Flash object's wrapping element's title attribute\n * based on the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.focus = ZeroClipboard.activate = function() {\n return _focus.apply(this, _args(arguments));\n };\n /**\n * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on\n * the setup, this may also unset the Flash object's wrapping element's title attribute based on\n * the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.blur = ZeroClipboard.deactivate = function() {\n return _blur.apply(this, _args(arguments));\n };\n /**\n * Returns the currently focused/\"activated\" HTML element that the Flash object is wrapping.\n *\n * @returns `HTMLElement` or `null`\n * @static\n */\n ZeroClipboard.activeElement = function() {\n return _activeElement.apply(this, _args(arguments));\n };\n if (typeof define === \"function\" && define.amd) {\n define(function() {\n return ZeroClipboard;\n });\n } else if (typeof module === \"object\" && module && typeof module.exports === \"object\" && module.exports) {\n module.exports = ZeroClipboard;\n } else {\n window.ZeroClipboard = ZeroClipboard;\n }\n})(function() {\n return this || window;\n}());"]}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.js b/showcase/js/zeroclipboard/dist/ZeroClipboard.js
new file mode 100755
index 0000000..5778b0c
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/ZeroClipboard.js
@@ -0,0 +1,2314 @@
+/*!
+ * ZeroClipboard
+ * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
+ * Copyright (c) 2014 Jon Rohan, James M. Greene
+ * Licensed MIT
+ * http://zeroclipboard.org/
+ * v2.1.6
+ */
+(function(window, undefined) {
+ "use strict";
+ /**
+ * Store references to critically important global functions that may be
+ * overridden on certain web pages.
+ */
+ var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _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, _round = _window.Math.round, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() {
+ var unwrapper = function(el) {
+ return el;
+ };
+ if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") {
+ try {
+ var div = _document.createElement("div");
+ var unwrappedDiv = _window.unwrap(div);
+ if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {
+ unwrapper = _window.unwrap;
+ }
+ } catch (e) {}
+ }
+ return unwrapper;
+ }();
+ /**
+ * Convert an `arguments` object into an Array.
+ *
+ * @returns The arguments as an Array
+ * @private
+ */
+ var _args = function(argumentsObj) {
+ return _slice.call(argumentsObj, 0);
+ };
+ /**
+ * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.
+ *
+ * @returns The target object, augmented
+ * @private
+ */
+ var _extend = function() {
+ var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {};
+ for (i = 1, len = args.length; i < len; i++) {
+ if ((arg = args[i]) != null) {
+ for (prop in arg) {
+ if (_hasOwn.call(arg, prop)) {
+ src = target[prop];
+ copy = arg[prop];
+ if (target !== copy && copy !== undefined) {
+ target[prop] = copy;
+ }
+ }
+ }
+ }
+ }
+ return target;
+ };
+ /**
+ * Return a deep copy of the source object or array.
+ *
+ * @returns Object or Array
+ * @private
+ */
+ var _deepCopy = function(source) {
+ var copy, i, len, prop;
+ if (typeof source !== "object" || source == null) {
+ copy = source;
+ } else if (typeof source.length === "number") {
+ copy = [];
+ for (i = 0, len = source.length; i < len; i++) {
+ if (_hasOwn.call(source, i)) {
+ copy[i] = _deepCopy(source[i]);
+ }
+ }
+ } else {
+ copy = {};
+ for (prop in source) {
+ if (_hasOwn.call(source, prop)) {
+ copy[prop] = _deepCopy(source[prop]);
+ }
+ }
+ }
+ return copy;
+ };
+ /**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.
+ * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to
+ * be kept.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+ var _pick = function(obj, keys) {
+ var newObj = {};
+ for (var i = 0, len = keys.length; i < len; i++) {
+ if (keys[i] in obj) {
+ newObj[keys[i]] = obj[keys[i]];
+ }
+ }
+ return newObj;
+ };
+ /**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.
+ * The inverse of `_pick`.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+ var _omit = function(obj, keys) {
+ var newObj = {};
+ for (var prop in obj) {
+ if (keys.indexOf(prop) === -1) {
+ newObj[prop] = obj[prop];
+ }
+ }
+ return newObj;
+ };
+ /**
+ * Remove all owned, enumerable properties from an object.
+ *
+ * @returns The original object without its owned, enumerable properties.
+ * @private
+ */
+ var _deleteOwnProperties = function(obj) {
+ if (obj) {
+ for (var prop in obj) {
+ if (_hasOwn.call(obj, prop)) {
+ delete obj[prop];
+ }
+ }
+ }
+ return obj;
+ };
+ /**
+ * Determine if an element is contained within another element.
+ *
+ * @returns Boolean
+ * @private
+ */
+ var _containedBy = function(el, ancestorEl) {
+ 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)) {
+ do {
+ if (el === ancestorEl) {
+ return true;
+ }
+ el = el.parentNode;
+ } while (el);
+ }
+ return false;
+ };
+ /**
+ * Get the URL path's parent directory.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getDirPathOfUrl = function(url) {
+ var dir;
+ if (typeof url === "string" && url) {
+ dir = url.split("#")[0].split("?")[0];
+ dir = url.slice(0, url.lastIndexOf("/") + 1);
+ }
+ return dir;
+ };
+ /**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrlFromErrorStack = function(stack) {
+ var url, matches;
+ if (typeof stack === "string" && stack) {
+ matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ } else {
+ matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ }
+ }
+ }
+ return url;
+ };
+ /**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrlFromError = function() {
+ var url, err;
+ try {
+ throw new _Error();
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);
+ }
+ return url;
+ };
+ /**
+ * Get the current script's URL.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getCurrentScriptUrl = function() {
+ var jsPath, scripts, i;
+ if (_document.currentScript && (jsPath = _document.currentScript.src)) {
+ return jsPath;
+ }
+ scripts = _document.getElementsByTagName("script");
+ if (scripts.length === 1) {
+ return scripts[0].src || undefined;
+ }
+ if ("readyState" in scripts[0]) {
+ for (i = scripts.length; i--; ) {
+ if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) {
+ return jsPath;
+ }
+ }
+ }
+ if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) {
+ return jsPath;
+ }
+ if (jsPath = _getCurrentScriptUrlFromError()) {
+ return jsPath;
+ }
+ return undefined;
+ };
+ /**
+ * Get the unanimous parent directory of ALL script tags.
+ * If any script tags are either (a) inline or (b) from differing parent
+ * directories, this method must return `undefined`.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+ var _getUnanimousScriptParentDir = function() {
+ var i, jsDir, jsPath, scripts = _document.getElementsByTagName("script");
+ for (i = scripts.length; i--; ) {
+ if (!(jsPath = scripts[i].src)) {
+ jsDir = null;
+ break;
+ }
+ jsPath = _getDirPathOfUrl(jsPath);
+ if (jsDir == null) {
+ jsDir = jsPath;
+ } else if (jsDir !== jsPath) {
+ jsDir = null;
+ break;
+ }
+ }
+ return jsDir || undefined;
+ };
+ /**
+ * Get the presumed location of the "ZeroClipboard.swf" file, based on the location
+ * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.).
+ *
+ * @returns String
+ * @private
+ */
+ var _getDefaultSwfPath = function() {
+ var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || "";
+ return jsDir + "ZeroClipboard.swf";
+ };
+ /**
+ * Keep track of the state of the Flash object.
+ * @private
+ */
+ var _flashState = {
+ bridge: null,
+ version: "0.0.0",
+ pluginType: "unknown",
+ disabled: null,
+ outdated: null,
+ unavailable: null,
+ deactivated: null,
+ overdue: null,
+ ready: null
+ };
+ /**
+ * The minimum Flash Player version required to use ZeroClipboard completely.
+ * @readonly
+ * @private
+ */
+ var _minimumFlashVersion = "11.0.0";
+ /**
+ * Keep track of all event listener registrations.
+ * @private
+ */
+ var _handlers = {};
+ /**
+ * Keep track of the currently activated element.
+ * @private
+ */
+ var _currentElement;
+ /**
+ * Keep track of the element that was activated when a `copy` process started.
+ * @private
+ */
+ var _copyTarget;
+ /**
+ * Keep track of data for the pending clipboard transaction.
+ * @private
+ */
+ var _clipData = {};
+ /**
+ * Keep track of data formats for the pending clipboard transaction.
+ * @private
+ */
+ var _clipDataFormatMap = null;
+ /**
+ * The `message` store for events
+ * @private
+ */
+ var _eventMessages = {
+ ready: "Flash communication is established",
+ error: {
+ "flash-disabled": "Flash is disabled or not installed",
+ "flash-outdated": "Flash is too outdated to support ZeroClipboard",
+ "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript",
+ "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate",
+ "flash-overdue": "Flash communication was established but NOT within the acceptable time limit"
+ }
+ };
+ /**
+ * ZeroClipboard configuration defaults for the Core module.
+ * @private
+ */
+ var _globalConfig = {
+ swfPath: _getDefaultSwfPath(),
+ trustedDomains: window.location.host ? [ window.location.host ] : [],
+ cacheBust: true,
+ forceEnhancedClipboard: false,
+ flashLoadTimeout: 3e4,
+ autoActivate: true,
+ bubbleEvents: true,
+ containerId: "global-zeroclipboard-html-bridge",
+ containerClass: "global-zeroclipboard-container",
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+ hoverClass: "zeroclipboard-is-hover",
+ activeClass: "zeroclipboard-is-active",
+ forceHandCursor: false,
+ title: null,
+ zIndex: 999999999
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.config`.
+ * @private
+ */
+ var _config = function(options) {
+ if (typeof options === "object" && options !== null) {
+ for (var prop in options) {
+ if (_hasOwn.call(options, prop)) {
+ if (/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(prop)) {
+ _globalConfig[prop] = options[prop];
+ } else if (_flashState.bridge == null) {
+ if (prop === "containerId" || prop === "swfObjectId") {
+ if (_isValidHtml4Id(options[prop])) {
+ _globalConfig[prop] = options[prop];
+ } else {
+ throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID");
+ }
+ } else {
+ _globalConfig[prop] = options[prop];
+ }
+ }
+ }
+ }
+ }
+ if (typeof options === "string" && options) {
+ if (_hasOwn.call(_globalConfig, options)) {
+ return _globalConfig[options];
+ }
+ return;
+ }
+ return _deepCopy(_globalConfig);
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.state`.
+ * @private
+ */
+ var _state = function() {
+ return {
+ browser: _pick(_navigator, [ "userAgent", "platform", "appName" ]),
+ flash: _omit(_flashState, [ "bridge" ]),
+ zeroclipboard: {
+ version: ZeroClipboard.version,
+ config: ZeroClipboard.config()
+ }
+ };
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.isFlashUnusable`.
+ * @private
+ */
+ var _isFlashUnusable = function() {
+ return !!(_flashState.disabled || _flashState.outdated || _flashState.unavailable || _flashState.deactivated);
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.on`.
+ * @private
+ */
+ var _on = function(eventType, listener) {
+ var i, len, events, added = {};
+ if (typeof eventType === "string" && eventType) {
+ events = eventType.toLowerCase().split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.on(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].replace(/^on/, "");
+ added[eventType] = true;
+ if (!_handlers[eventType]) {
+ _handlers[eventType] = [];
+ }
+ _handlers[eventType].push(listener);
+ }
+ if (added.ready && _flashState.ready) {
+ ZeroClipboard.emit({
+ type: "ready"
+ });
+ }
+ if (added.error) {
+ var errorTypes = [ "disabled", "outdated", "unavailable", "deactivated", "overdue" ];
+ for (i = 0, len = errorTypes.length; i < len; i++) {
+ if (_flashState[errorTypes[i]] === true) {
+ ZeroClipboard.emit({
+ type: "error",
+ name: "flash-" + errorTypes[i]
+ });
+ break;
+ }
+ }
+ }
+ }
+ return ZeroClipboard;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.off`.
+ * @private
+ */
+ var _off = function(eventType, listener) {
+ var i, len, foundIndex, events, perEventHandlers;
+ if (arguments.length === 0) {
+ events = _keys(_handlers);
+ } else if (typeof eventType === "string" && eventType) {
+ events = eventType.split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.off(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].toLowerCase().replace(/^on/, "");
+ perEventHandlers = _handlers[eventType];
+ if (perEventHandlers && perEventHandlers.length) {
+ if (listener) {
+ foundIndex = perEventHandlers.indexOf(listener);
+ while (foundIndex !== -1) {
+ perEventHandlers.splice(foundIndex, 1);
+ foundIndex = perEventHandlers.indexOf(listener, foundIndex);
+ }
+ } else {
+ perEventHandlers.length = 0;
+ }
+ }
+ }
+ }
+ return ZeroClipboard;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.handlers`.
+ * @private
+ */
+ var _listeners = function(eventType) {
+ var copy;
+ if (typeof eventType === "string" && eventType) {
+ copy = _deepCopy(_handlers[eventType]) || null;
+ } else {
+ copy = _deepCopy(_handlers);
+ }
+ return copy;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.emit`.
+ * @private
+ */
+ var _emit = function(event) {
+ var eventCopy, returnVal, tmp;
+ event = _createEvent(event);
+ if (!event) {
+ return;
+ }
+ if (_preprocessEvent(event)) {
+ return;
+ }
+ if (event.type === "ready" && _flashState.overdue === true) {
+ return ZeroClipboard.emit({
+ type: "error",
+ name: "flash-overdue"
+ });
+ }
+ eventCopy = _extend({}, event);
+ _dispatchCallbacks.call(this, eventCopy);
+ if (event.type === "copy") {
+ tmp = _mapClipDataToFlash(_clipData);
+ returnVal = tmp.data;
+ _clipDataFormatMap = tmp.formatMap;
+ }
+ return returnVal;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.create`.
+ * @private
+ */
+ var _create = function() {
+ if (typeof _flashState.ready !== "boolean") {
+ _flashState.ready = false;
+ }
+ if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {
+ var maxWait = _globalConfig.flashLoadTimeout;
+ if (typeof maxWait === "number" && maxWait >= 0) {
+ _setTimeout(function() {
+ if (typeof _flashState.deactivated !== "boolean") {
+ _flashState.deactivated = true;
+ }
+ if (_flashState.deactivated === true) {
+ ZeroClipboard.emit({
+ type: "error",
+ name: "flash-deactivated"
+ });
+ }
+ }, maxWait);
+ }
+ _flashState.overdue = false;
+ _embedSwf();
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.destroy`.
+ * @private
+ */
+ var _destroy = function() {
+ ZeroClipboard.clearData();
+ ZeroClipboard.blur();
+ ZeroClipboard.emit("destroy");
+ _unembedSwf();
+ ZeroClipboard.off();
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.setData`.
+ * @private
+ */
+ var _setData = function(format, data) {
+ var dataObj;
+ if (typeof format === "object" && format && typeof data === "undefined") {
+ dataObj = format;
+ ZeroClipboard.clearData();
+ } else if (typeof format === "string" && format) {
+ dataObj = {};
+ dataObj[format] = data;
+ } else {
+ return;
+ }
+ for (var dataFormat in dataObj) {
+ if (typeof dataFormat === "string" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]) {
+ _clipData[dataFormat] = dataObj[dataFormat];
+ }
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.clearData`.
+ * @private
+ */
+ var _clearData = function(format) {
+ if (typeof format === "undefined") {
+ _deleteOwnProperties(_clipData);
+ _clipDataFormatMap = null;
+ } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ delete _clipData[format];
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.getData`.
+ * @private
+ */
+ var _getData = function(format) {
+ if (typeof format === "undefined") {
+ return _deepCopy(_clipData);
+ } else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ return _clipData[format];
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.
+ * @private
+ */
+ var _focus = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.activeClass);
+ if (_currentElement !== element) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ }
+ }
+ _currentElement = element;
+ _addClass(element, _globalConfig.hoverClass);
+ var newTitle = element.getAttribute("title") || _globalConfig.title;
+ if (typeof newTitle === "string" && newTitle) {
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.setAttribute("title", newTitle);
+ }
+ }
+ var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer";
+ _setHandCursor(useHandCursor);
+ _reposition();
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.
+ * @private
+ */
+ var _blur = function() {
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.removeAttribute("title");
+ htmlBridge.style.left = "0px";
+ htmlBridge.style.top = "-9999px";
+ htmlBridge.style.width = "1px";
+ htmlBridge.style.top = "1px";
+ }
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ _removeClass(_currentElement, _globalConfig.activeClass);
+ _currentElement = null;
+ }
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.activeElement`.
+ * @private
+ */
+ var _activeElement = function() {
+ return _currentElement || null;
+ };
+ /**
+ * Check if a value is a valid HTML4 `ID` or `Name` token.
+ * @private
+ */
+ var _isValidHtml4Id = function(id) {
+ return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id);
+ };
+ /**
+ * Create or update an `event` object, based on the `eventType`.
+ * @private
+ */
+ var _createEvent = function(event) {
+ var eventType;
+ if (typeof event === "string" && event) {
+ eventType = event;
+ event = {};
+ } else if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
+ eventType = event.type;
+ }
+ if (!eventType) {
+ return;
+ }
+ if (!event.target && /^(copy|aftercopy|_click)$/.test(eventType.toLowerCase())) {
+ event.target = _copyTarget;
+ }
+ _extend(event, {
+ type: eventType.toLowerCase(),
+ target: event.target || _currentElement || null,
+ relatedTarget: event.relatedTarget || null,
+ currentTarget: _flashState && _flashState.bridge || null,
+ timeStamp: event.timeStamp || _now() || null
+ });
+ var msg = _eventMessages[event.type];
+ if (event.type === "error" && event.name && msg) {
+ msg = msg[event.name];
+ }
+ if (msg) {
+ event.message = msg;
+ }
+ if (event.type === "ready") {
+ _extend(event, {
+ target: null,
+ version: _flashState.version
+ });
+ }
+ if (event.type === "error") {
+ if (/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ target: null,
+ minimumVersion: _minimumFlashVersion
+ });
+ }
+ if (/^flash-(outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ version: _flashState.version
+ });
+ }
+ }
+ if (event.type === "copy") {
+ event.clipboardData = {
+ setData: ZeroClipboard.setData,
+ clearData: ZeroClipboard.clearData
+ };
+ }
+ if (event.type === "aftercopy") {
+ event = _mapClipResultsFromFlash(event, _clipDataFormatMap);
+ }
+ if (event.target && !event.relatedTarget) {
+ event.relatedTarget = _getRelatedTarget(event.target);
+ }
+ event = _addMouseData(event);
+ return event;
+ };
+ /**
+ * Get a relatedTarget from the target's `data-clipboard-target` attribute
+ * @private
+ */
+ var _getRelatedTarget = function(targetEl) {
+ var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target");
+ return relatedTargetId ? _document.getElementById(relatedTargetId) : null;
+ };
+ /**
+ * Add element and position data to `MouseEvent` instances
+ * @private
+ */
+ var _addMouseData = function(event) {
+ if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ var srcElement = event.target;
+ var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined;
+ var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined;
+ var pos = _getDOMObjectPosition(srcElement);
+ var screenLeft = _window.screenLeft || _window.screenX || 0;
+ var screenTop = _window.screenTop || _window.screenY || 0;
+ var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;
+ var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;
+ var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0);
+ var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0);
+ var clientX = pageX - scrollLeft;
+ var clientY = pageY - scrollTop;
+ var screenX = screenLeft + clientX;
+ var screenY = screenTop + clientY;
+ var moveX = typeof event.movementX === "number" ? event.movementX : 0;
+ var moveY = typeof event.movementY === "number" ? event.movementY : 0;
+ delete event._stageX;
+ delete event._stageY;
+ _extend(event, {
+ srcElement: srcElement,
+ fromElement: fromElement,
+ toElement: toElement,
+ screenX: screenX,
+ screenY: screenY,
+ pageX: pageX,
+ pageY: pageY,
+ clientX: clientX,
+ clientY: clientY,
+ x: clientX,
+ y: clientY,
+ movementX: moveX,
+ movementY: moveY,
+ offsetX: 0,
+ offsetY: 0,
+ layerX: 0,
+ layerY: 0
+ });
+ }
+ return event;
+ };
+ /**
+ * Determine if an event's registered handlers should be execute synchronously or asynchronously.
+ *
+ * @returns {boolean}
+ * @private
+ */
+ var _shouldPerformAsync = function(event) {
+ var eventType = event && typeof event.type === "string" && event.type || "";
+ return !/^(?:(?:before)?copy|destroy)$/.test(eventType);
+ };
+ /**
+ * Control if a callback should be executed asynchronously or not.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _dispatchCallback = function(func, context, args, async) {
+ if (async) {
+ _setTimeout(function() {
+ func.apply(context, args);
+ }, 0);
+ } else {
+ func.apply(context, args);
+ }
+ };
+ /**
+ * Handle the actual dispatching of events to client instances.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _dispatchCallbacks = function(event) {
+ if (!(typeof event === "object" && event && event.type)) {
+ return;
+ }
+ var async = _shouldPerformAsync(event);
+ var wildcardTypeHandlers = _handlers["*"] || [];
+ var specificTypeHandlers = _handlers[event.type] || [];
+ var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
+ if (handlers && handlers.length) {
+ var i, len, func, context, eventCopy, originalContext = this;
+ for (i = 0, len = handlers.length; i < len; i++) {
+ func = handlers[i];
+ context = originalContext;
+ if (typeof func === "string" && typeof _window[func] === "function") {
+ func = _window[func];
+ }
+ if (typeof func === "object" && func && typeof func.handleEvent === "function") {
+ context = func;
+ func = func.handleEvent;
+ }
+ if (typeof func === "function") {
+ eventCopy = _extend({}, event);
+ _dispatchCallback(func, context, [ eventCopy ], async);
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Preprocess any special behaviors, reactions, or state changes after receiving this event.
+ * Executes only once per event emitted, NOT once per client.
+ * @private
+ */
+ var _preprocessEvent = function(event) {
+ var element = event.target || _currentElement || null;
+ var sourceIsSwf = event._source === "swf";
+ delete event._source;
+ var flashErrorNames = [ "flash-disabled", "flash-outdated", "flash-unavailable", "flash-deactivated", "flash-overdue" ];
+ switch (event.type) {
+ case "error":
+ if (flashErrorNames.indexOf(event.name) !== -1) {
+ _extend(_flashState, {
+ disabled: event.name === "flash-disabled",
+ outdated: event.name === "flash-outdated",
+ unavailable: event.name === "flash-unavailable",
+ deactivated: event.name === "flash-deactivated",
+ overdue: event.name === "flash-overdue",
+ ready: false
+ });
+ }
+ break;
+
+ case "ready":
+ var wasDeactivated = _flashState.deactivated === true;
+ _extend(_flashState, {
+ disabled: false,
+ outdated: false,
+ unavailable: false,
+ deactivated: false,
+ overdue: wasDeactivated,
+ ready: !wasDeactivated
+ });
+ break;
+
+ case "beforecopy":
+ _copyTarget = element;
+ break;
+
+ case "copy":
+ var textContent, htmlContent, targetEl = event.relatedTarget;
+ if (!(_clipData["text/html"] || _clipData["text/plain"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ if (htmlContent !== textContent) {
+ event.clipboardData.setData("text/html", htmlContent);
+ }
+ } else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ }
+ break;
+
+ case "aftercopy":
+ ZeroClipboard.clearData();
+ if (element && element !== _safeActiveElement() && element.focus) {
+ element.focus();
+ }
+ break;
+
+ case "_mouseover":
+ ZeroClipboard.focus(element);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseenter",
+ bubbles: false,
+ cancelable: false
+ }));
+ }
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseover"
+ }));
+ }
+ break;
+
+ case "_mouseout":
+ ZeroClipboard.blur();
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseleave",
+ bubbles: false,
+ cancelable: false
+ }));
+ }
+ _fireMouseEvent(_extend({}, event, {
+ type: "mouseout"
+ }));
+ }
+ break;
+
+ case "_mousedown":
+ _addClass(element, _globalConfig.activeClass);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_mouseup":
+ _removeClass(element, _globalConfig.activeClass);
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_click":
+ _copyTarget = null;
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+
+ case "_mousemove":
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, {
+ type: event.type.slice(1)
+ }));
+ }
+ break;
+ }
+ if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ return true;
+ }
+ };
+ /**
+ * Dispatch a synthetic MouseEvent.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _fireMouseEvent = function(event) {
+ if (!(event && typeof event.type === "string" && event)) {
+ return;
+ }
+ var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {
+ view: doc.defaultView || _window,
+ canBubble: true,
+ cancelable: true,
+ detail: event.type === "click" ? 1 : 0,
+ button: typeof event.which === "number" ? event.which - 1 : typeof event.button === "number" ? event.button : doc.createEvent ? 0 : 1
+ }, args = _extend(defaults, event);
+ if (!target) {
+ return;
+ }
+ if (doc.createEvent && target.dispatchEvent) {
+ 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 ];
+ e = doc.createEvent("MouseEvents");
+ if (e.initMouseEvent) {
+ e.initMouseEvent.apply(e, args);
+ e._source = "js";
+ target.dispatchEvent(e);
+ }
+ }
+ };
+ /**
+ * Create the HTML bridge element to embed the Flash object into.
+ * @private
+ */
+ var _createHtmlBridge = function() {
+ var container = _document.createElement("div");
+ container.id = _globalConfig.containerId;
+ container.className = _globalConfig.containerClass;
+ container.style.position = "absolute";
+ container.style.left = "0px";
+ container.style.top = "-9999px";
+ container.style.width = "1px";
+ container.style.height = "1px";
+ container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex);
+ return container;
+ };
+ /**
+ * Get the HTML element container that wraps the Flash bridge object/element.
+ * @private
+ */
+ var _getHtmlBridge = function(flashBridge) {
+ var htmlBridge = flashBridge && flashBridge.parentNode;
+ while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) {
+ htmlBridge = htmlBridge.parentNode;
+ }
+ return htmlBridge || null;
+ };
+ /**
+ * Create the SWF object.
+ *
+ * @returns The SWF object reference.
+ * @private
+ */
+ var _embedSwf = function() {
+ var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);
+ if (!flashBridge) {
+ var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);
+ var allowNetworking = allowScriptAccess === "never" ? "none" : "all";
+ var flashvars = _vars(_globalConfig);
+ var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);
+ container = _createHtmlBridge();
+ var divToBeReplaced = _document.createElement("div");
+ container.appendChild(divToBeReplaced);
+ _document.body.appendChild(container);
+ var tmpDiv = _document.createElement("div");
+ var oldIE = _flashState.pluginType === "activex";
+ tmpDiv.innerHTML = '";
+ flashBridge = tmpDiv.firstChild;
+ tmpDiv = null;
+ _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;
+ container.replaceChild(flashBridge, divToBeReplaced);
+ }
+ if (!flashBridge) {
+ flashBridge = _document[_globalConfig.swfObjectId];
+ if (flashBridge && (len = flashBridge.length)) {
+ flashBridge = flashBridge[len - 1];
+ }
+ if (!flashBridge && container) {
+ flashBridge = container.firstChild;
+ }
+ }
+ _flashState.bridge = flashBridge || null;
+ return flashBridge;
+ };
+ /**
+ * Destroy the SWF object.
+ * @private
+ */
+ var _unembedSwf = function() {
+ var flashBridge = _flashState.bridge;
+ if (flashBridge) {
+ var htmlBridge = _getHtmlBridge(flashBridge);
+ if (htmlBridge) {
+ if (_flashState.pluginType === "activex" && "readyState" in flashBridge) {
+ flashBridge.style.display = "none";
+ (function removeSwfFromIE() {
+ if (flashBridge.readyState === 4) {
+ for (var prop in flashBridge) {
+ if (typeof flashBridge[prop] === "function") {
+ flashBridge[prop] = null;
+ }
+ }
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ } else {
+ _setTimeout(removeSwfFromIE, 10);
+ }
+ })();
+ } else {
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ }
+ }
+ _flashState.ready = null;
+ _flashState.bridge = null;
+ _flashState.deactivated = null;
+ }
+ };
+ /**
+ * Map the data format names of the "clipData" to Flash-friendly names.
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+ var _mapClipDataToFlash = function(clipData) {
+ var newClipData = {}, formatMap = {};
+ if (!(typeof clipData === "object" && clipData)) {
+ return;
+ }
+ for (var dataFormat in clipData) {
+ if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) {
+ switch (dataFormat.toLowerCase()) {
+ case "text/plain":
+ case "text":
+ case "air:text":
+ case "flash:text":
+ newClipData.text = clipData[dataFormat];
+ formatMap.text = dataFormat;
+ break;
+
+ case "text/html":
+ case "html":
+ case "air:html":
+ case "flash:html":
+ newClipData.html = clipData[dataFormat];
+ formatMap.html = dataFormat;
+ break;
+
+ case "application/rtf":
+ case "text/rtf":
+ case "rtf":
+ case "richtext":
+ case "air:rtf":
+ case "flash:rtf":
+ newClipData.rtf = clipData[dataFormat];
+ formatMap.rtf = dataFormat;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return {
+ data: newClipData,
+ formatMap: formatMap
+ };
+ };
+ /**
+ * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping).
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+ var _mapClipResultsFromFlash = function(clipResults, formatMap) {
+ if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) {
+ return clipResults;
+ }
+ var newResults = {};
+ for (var prop in clipResults) {
+ if (_hasOwn.call(clipResults, prop)) {
+ if (prop !== "success" && prop !== "data") {
+ newResults[prop] = clipResults[prop];
+ continue;
+ }
+ newResults[prop] = {};
+ var tmpHash = clipResults[prop];
+ for (var dataFormat in tmpHash) {
+ if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {
+ newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];
+ }
+ }
+ }
+ }
+ return newResults;
+ };
+ /**
+ * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}"
+ * query param string to return. Does NOT append that string to the original path.
+ * This is useful because ExternalInterface often breaks when a Flash SWF is cached.
+ *
+ * @returns The `noCache` query param with necessary "?"/"&" prefix.
+ * @private
+ */
+ var _cacheBust = function(path, options) {
+ var cacheBust = options == null || options && options.cacheBust === true;
+ if (cacheBust) {
+ return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now();
+ } else {
+ return "";
+ }
+ };
+ /**
+ * Creates a query string for the FlashVars param.
+ * Does NOT include the cache-busting query param.
+ *
+ * @returns FlashVars query string
+ * @private
+ */
+ var _vars = function(options) {
+ var i, len, domain, domains, str = "", trustedOriginsExpanded = [];
+ if (options.trustedDomains) {
+ if (typeof options.trustedDomains === "string") {
+ domains = [ options.trustedDomains ];
+ } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) {
+ domains = options.trustedDomains;
+ }
+ }
+ if (domains && domains.length) {
+ for (i = 0, len = domains.length; i < len; i++) {
+ if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") {
+ domain = _extractDomain(domains[i]);
+ if (!domain) {
+ continue;
+ }
+ if (domain === "*") {
+ trustedOriginsExpanded.length = 0;
+ trustedOriginsExpanded.push(domain);
+ break;
+ }
+ trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, "//" + domain, _window.location.protocol + "//" + domain ]);
+ }
+ }
+ }
+ if (trustedOriginsExpanded.length) {
+ str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(","));
+ }
+ if (options.forceEnhancedClipboard === true) {
+ str += (str ? "&" : "") + "forceEnhancedClipboard=true";
+ }
+ if (typeof options.swfObjectId === "string" && options.swfObjectId) {
+ str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId);
+ }
+ return str;
+ };
+ /**
+ * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or
+ * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/").
+ *
+ * @returns the domain
+ * @private
+ */
+ var _extractDomain = function(originOrUrl) {
+ if (originOrUrl == null || originOrUrl === "") {
+ return null;
+ }
+ originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, "");
+ if (originOrUrl === "") {
+ return null;
+ }
+ var protocolIndex = originOrUrl.indexOf("//");
+ originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);
+ var pathIndex = originOrUrl.indexOf("/");
+ originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);
+ if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") {
+ return null;
+ }
+ return originOrUrl || null;
+ };
+ /**
+ * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.
+ *
+ * @returns The appropriate script access level.
+ * @private
+ */
+ var _determineScriptAccess = function() {
+ var _extractAllDomains = function(origins) {
+ var i, len, tmp, resultsArray = [];
+ if (typeof origins === "string") {
+ origins = [ origins ];
+ }
+ if (!(typeof origins === "object" && origins && typeof origins.length === "number")) {
+ return resultsArray;
+ }
+ for (i = 0, len = origins.length; i < len; i++) {
+ if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {
+ if (tmp === "*") {
+ resultsArray.length = 0;
+ resultsArray.push("*");
+ break;
+ }
+ if (resultsArray.indexOf(tmp) === -1) {
+ resultsArray.push(tmp);
+ }
+ }
+ }
+ return resultsArray;
+ };
+ return function(currentDomain, configOptions) {
+ var swfDomain = _extractDomain(configOptions.swfPath);
+ if (swfDomain === null) {
+ swfDomain = currentDomain;
+ }
+ var trustedDomains = _extractAllDomains(configOptions.trustedDomains);
+ var len = trustedDomains.length;
+ if (len > 0) {
+ if (len === 1 && trustedDomains[0] === "*") {
+ return "always";
+ }
+ if (trustedDomains.indexOf(currentDomain) !== -1) {
+ if (len === 1 && currentDomain === swfDomain) {
+ return "sameDomain";
+ }
+ return "always";
+ }
+ }
+ return "never";
+ };
+ }();
+ /**
+ * Get the currently active/focused DOM element.
+ *
+ * @returns the currently active/focused element, or `null`
+ * @private
+ */
+ var _safeActiveElement = function() {
+ try {
+ return _document.activeElement;
+ } catch (err) {
+ return null;
+ }
+ };
+ /**
+ * Add a class to an element, if it doesn't already have it.
+ *
+ * @returns The element, with its new class added.
+ * @private
+ */
+ var _addClass = function(element, value) {
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+ if (element.classList) {
+ if (!element.classList.contains(value)) {
+ element.classList.add(value);
+ }
+ return element;
+ }
+ if (value && typeof value === "string") {
+ var classNames = (value || "").split(/\s+/);
+ if (element.nodeType === 1) {
+ if (!element.className) {
+ element.className = value;
+ } else {
+ var className = " " + element.className + " ", setClass = element.className;
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ if (className.indexOf(" " + classNames[c] + " ") < 0) {
+ setClass += " " + classNames[c];
+ }
+ }
+ element.className = setClass.replace(/^\s+|\s+$/g, "");
+ }
+ }
+ }
+ return element;
+ };
+ /**
+ * Remove a class from an element, if it has it.
+ *
+ * @returns The element, with its class removed.
+ * @private
+ */
+ var _removeClass = function(element, value) {
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+ if (element.classList) {
+ if (element.classList.contains(value)) {
+ element.classList.remove(value);
+ }
+ return element;
+ }
+ if (typeof value === "string" && value) {
+ var classNames = value.split(/\s+/);
+ if (element.nodeType === 1 && element.className) {
+ var className = (" " + element.className + " ").replace(/[\n\t]/g, " ");
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ element.className = className.replace(/^\s+|\s+$/g, "");
+ }
+ }
+ return element;
+ };
+ /**
+ * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`,
+ * then we assume that it should be a hand ("pointer") cursor if the element
+ * is an anchor element ("a" tag).
+ *
+ * @returns The computed style property.
+ * @private
+ */
+ var _getStyle = function(el, prop) {
+ var value = _window.getComputedStyle(el, null).getPropertyValue(prop);
+ if (prop === "cursor") {
+ if (!value || value === "auto") {
+ if (el.nodeName === "A") {
+ return "pointer";
+ }
+ }
+ }
+ return value;
+ };
+ /**
+ * Get the zoom factor of the browser. Always returns `1.0`, except at
+ * non-default zoom levels in IE<8 and some older versions of WebKit.
+ *
+ * @returns Floating unit percentage of the zoom factor (e.g. 150% = `1.5`).
+ * @private
+ */
+ var _getZoomFactor = function() {
+ var rect, physicalWidth, logicalWidth, zoomFactor = 1;
+ if (typeof _document.body.getBoundingClientRect === "function") {
+ rect = _document.body.getBoundingClientRect();
+ physicalWidth = rect.right - rect.left;
+ logicalWidth = _document.body.offsetWidth;
+ zoomFactor = _round(physicalWidth / logicalWidth * 100) / 100;
+ }
+ return zoomFactor;
+ };
+ /**
+ * Get the DOM positioning info of an element.
+ *
+ * @returns Object containing the element's position, width, and height.
+ * @private
+ */
+ var _getDOMObjectPosition = function(obj) {
+ var info = {
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0
+ };
+ if (obj.getBoundingClientRect) {
+ var rect = obj.getBoundingClientRect();
+ var pageXOffset, pageYOffset, zoomFactor;
+ if ("pageXOffset" in _window && "pageYOffset" in _window) {
+ pageXOffset = _window.pageXOffset;
+ pageYOffset = _window.pageYOffset;
+ } else {
+ zoomFactor = _getZoomFactor();
+ pageXOffset = _round(_document.documentElement.scrollLeft / zoomFactor);
+ pageYOffset = _round(_document.documentElement.scrollTop / zoomFactor);
+ }
+ var leftBorderWidth = _document.documentElement.clientLeft || 0;
+ var topBorderWidth = _document.documentElement.clientTop || 0;
+ info.left = rect.left + pageXOffset - leftBorderWidth;
+ info.top = rect.top + pageYOffset - topBorderWidth;
+ info.width = "width" in rect ? rect.width : rect.right - rect.left;
+ info.height = "height" in rect ? rect.height : rect.bottom - rect.top;
+ }
+ return info;
+ };
+ /**
+ * Reposition the Flash object to cover the currently activated element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _reposition = function() {
+ var htmlBridge;
+ if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {
+ var pos = _getDOMObjectPosition(_currentElement);
+ _extend(htmlBridge.style, {
+ width: pos.width + "px",
+ height: pos.height + "px",
+ top: pos.top + "px",
+ left: pos.left + "px",
+ zIndex: "" + _getSafeZIndex(_globalConfig.zIndex)
+ });
+ }
+ };
+ /**
+ * Sends a signal to the Flash object to display the hand cursor if `true`.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _setHandCursor = function(enabled) {
+ if (_flashState.ready === true) {
+ if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") {
+ _flashState.bridge.setHandCursor(enabled);
+ } else {
+ _flashState.ready = false;
+ }
+ }
+ };
+ /**
+ * Get a safe value for `zIndex`
+ *
+ * @returns an integer, or "auto"
+ * @private
+ */
+ var _getSafeZIndex = function(val) {
+ if (/^(?:auto|inherit)$/.test(val)) {
+ return val;
+ }
+ var zIndex;
+ if (typeof val === "number" && !_isNaN(val)) {
+ zIndex = val;
+ } else if (typeof val === "string") {
+ zIndex = _getSafeZIndex(_parseInt(val, 10));
+ }
+ return typeof zIndex === "number" ? zIndex : "auto";
+ };
+ /**
+ * Detect the Flash Player status, version, and plugin type.
+ *
+ * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}
+ * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _detectFlashSupport = function(ActiveXObject) {
+ var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = "";
+ /**
+ * Derived from Apple's suggested sniffer.
+ * @param {String} desc e.g. "Shockwave Flash 7.0 r61"
+ * @returns {String} "7.0.61"
+ * @private
+ */
+ function parseFlashVersion(desc) {
+ var matches = desc.match(/[\d]+/g);
+ matches.length = 3;
+ return matches.join(".");
+ }
+ function isPepperFlash(flashPlayerFileName) {
+ return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === "chrome.plugin");
+ }
+ function inspectPlugin(plugin) {
+ if (plugin) {
+ hasFlash = true;
+ if (plugin.version) {
+ flashVersion = parseFlashVersion(plugin.version);
+ }
+ if (!flashVersion && plugin.description) {
+ flashVersion = parseFlashVersion(plugin.description);
+ }
+ if (plugin.filename) {
+ isPPAPI = isPepperFlash(plugin.filename);
+ }
+ }
+ }
+ if (_navigator.plugins && _navigator.plugins.length) {
+ plugin = _navigator.plugins["Shockwave Flash"];
+ inspectPlugin(plugin);
+ if (_navigator.plugins["Shockwave Flash 2.0"]) {
+ hasFlash = true;
+ flashVersion = "2.0.0.11";
+ }
+ } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {
+ mimeType = _navigator.mimeTypes["application/x-shockwave-flash"];
+ plugin = mimeType && mimeType.enabledPlugin;
+ inspectPlugin(plugin);
+ } else if (typeof ActiveXObject !== "undefined") {
+ isActiveX = true;
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ } catch (e1) {
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+ hasFlash = true;
+ flashVersion = "6.0.21";
+ } catch (e2) {
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ } catch (e3) {
+ isActiveX = false;
+ }
+ }
+ }
+ }
+ _flashState.disabled = hasFlash !== true;
+ _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);
+ _flashState.version = flashVersion || "0.0.0";
+ _flashState.pluginType = isPPAPI ? "pepper" : isActiveX ? "activex" : hasFlash ? "netscape" : "unknown";
+ };
+ /**
+ * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.
+ */
+ _detectFlashSupport(_ActiveXObject);
+ /**
+ * A shell constructor for `ZeroClipboard` client instances.
+ *
+ * @constructor
+ */
+ var ZeroClipboard = function() {
+ if (!(this instanceof ZeroClipboard)) {
+ return new ZeroClipboard();
+ }
+ if (typeof ZeroClipboard._createClient === "function") {
+ ZeroClipboard._createClient.apply(this, _args(arguments));
+ }
+ };
+ /**
+ * The ZeroClipboard library's version number.
+ *
+ * @static
+ * @readonly
+ * @property {string}
+ */
+ _defineProperty(ZeroClipboard, "version", {
+ value: "2.1.6",
+ writable: false,
+ configurable: true,
+ enumerable: true
+ });
+ /**
+ * Update or get a copy of the ZeroClipboard global configuration.
+ * Returns a copy of the current/updated configuration.
+ *
+ * @returns Object
+ * @static
+ */
+ ZeroClipboard.config = function() {
+ return _config.apply(this, _args(arguments));
+ };
+ /**
+ * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
+ *
+ * @returns Object
+ * @static
+ */
+ ZeroClipboard.state = function() {
+ return _state.apply(this, _args(arguments));
+ };
+ /**
+ * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.
+ *
+ * @returns Boolean
+ * @static
+ */
+ ZeroClipboard.isFlashUnusable = function() {
+ return _isFlashUnusable.apply(this, _args(arguments));
+ };
+ /**
+ * Register an event listener.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ ZeroClipboard.on = function() {
+ return _on.apply(this, _args(arguments));
+ };
+ /**
+ * Unregister an event listener.
+ * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.
+ * If no `eventType` is provided, it will unregister all listeners for every event type.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ ZeroClipboard.off = function() {
+ return _off.apply(this, _args(arguments));
+ };
+ /**
+ * Retrieve event listeners for an `eventType`.
+ * If no `eventType` is provided, it will retrieve all listeners for every event type.
+ *
+ * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
+ */
+ ZeroClipboard.handlers = function() {
+ return _listeners.apply(this, _args(arguments));
+ };
+ /**
+ * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.
+ *
+ * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
+ * @static
+ */
+ ZeroClipboard.emit = function() {
+ return _emit.apply(this, _args(arguments));
+ };
+ /**
+ * Create and embed the Flash object.
+ *
+ * @returns The Flash object
+ * @static
+ */
+ ZeroClipboard.create = function() {
+ return _create.apply(this, _args(arguments));
+ };
+ /**
+ * Self-destruct and clean up everything, including the embedded Flash object.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.destroy = function() {
+ return _destroy.apply(this, _args(arguments));
+ };
+ /**
+ * Set the pending data for clipboard injection.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.setData = function() {
+ return _setData.apply(this, _args(arguments));
+ };
+ /**
+ * Clear the pending data for clipboard injection.
+ * If no `format` is provided, all pending data formats will be cleared.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.clearData = function() {
+ return _clearData.apply(this, _args(arguments));
+ };
+ /**
+ * Get a copy of the pending data for clipboard injection.
+ * If no `format` is provided, a copy of ALL pending data formats will be returned.
+ *
+ * @returns `String` or `Object`
+ * @static
+ */
+ ZeroClipboard.getData = function() {
+ return _getData.apply(this, _args(arguments));
+ };
+ /**
+ * Sets the current HTML object that the Flash object should overlay. This will put the global
+ * Flash object on top of the current element; depending on the setup, this may also set the
+ * pending clipboard text data as well as the Flash object's wrapping element's title attribute
+ * based on the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.focus = ZeroClipboard.activate = function() {
+ return _focus.apply(this, _args(arguments));
+ };
+ /**
+ * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on
+ * the setup, this may also unset the Flash object's wrapping element's title attribute based on
+ * the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ ZeroClipboard.blur = ZeroClipboard.deactivate = function() {
+ return _blur.apply(this, _args(arguments));
+ };
+ /**
+ * Returns the currently focused/"activated" HTML element that the Flash object is wrapping.
+ *
+ * @returns `HTMLElement` or `null`
+ * @static
+ */
+ ZeroClipboard.activeElement = function() {
+ return _activeElement.apply(this, _args(arguments));
+ };
+ /**
+ * Keep track of the ZeroClipboard client instance counter.
+ */
+ var _clientIdCounter = 0;
+ /**
+ * Keep track of the state of the client instances.
+ *
+ * Entry structure:
+ * _clientMeta[client.id] = {
+ * instance: client,
+ * elements: [],
+ * handlers: {}
+ * };
+ */
+ var _clientMeta = {};
+ /**
+ * Keep track of the ZeroClipboard clipped elements counter.
+ */
+ var _elementIdCounter = 0;
+ /**
+ * Keep track of the state of the clipped element relationships to clients.
+ *
+ * Entry structure:
+ * _elementMeta[element.zcClippingId] = [client1.id, client2.id];
+ */
+ var _elementMeta = {};
+ /**
+ * Keep track of the state of the mouse event handlers for clipped elements.
+ *
+ * Entry structure:
+ * _mouseHandlers[element.zcClippingId] = {
+ * mouseover: function(event) {},
+ * mouseout: function(event) {},
+ * mouseenter: function(event) {},
+ * mouseleave: function(event) {},
+ * mousemove: function(event) {}
+ * };
+ */
+ var _mouseHandlers = {};
+ /**
+ * Extending the ZeroClipboard configuration defaults for the Client module.
+ */
+ _extend(_globalConfig, {
+ autoActivate: true
+ });
+ /**
+ * The real constructor for `ZeroClipboard` client instances.
+ * @private
+ */
+ var _clientConstructor = function(elements) {
+ var client = this;
+ client.id = "" + _clientIdCounter++;
+ _clientMeta[client.id] = {
+ instance: client,
+ elements: [],
+ handlers: {}
+ };
+ if (elements) {
+ client.clip(elements);
+ }
+ ZeroClipboard.on("*", function(event) {
+ return client.emit(event);
+ });
+ ZeroClipboard.on("destroy", function() {
+ client.destroy();
+ });
+ ZeroClipboard.create();
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.on`.
+ * @private
+ */
+ var _clientOn = function(eventType, listener) {
+ var i, len, events, added = {}, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+ if (typeof eventType === "string" && eventType) {
+ events = eventType.toLowerCase().split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ this.on(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].replace(/^on/, "");
+ added[eventType] = true;
+ if (!handlers[eventType]) {
+ handlers[eventType] = [];
+ }
+ handlers[eventType].push(listener);
+ }
+ if (added.ready && _flashState.ready) {
+ this.emit({
+ type: "ready",
+ client: this
+ });
+ }
+ if (added.error) {
+ var errorTypes = [ "disabled", "outdated", "unavailable", "deactivated", "overdue" ];
+ for (i = 0, len = errorTypes.length; i < len; i++) {
+ if (_flashState[errorTypes[i]]) {
+ this.emit({
+ type: "error",
+ name: "flash-" + errorTypes[i],
+ client: this
+ });
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.off`.
+ * @private
+ */
+ var _clientOff = function(eventType, listener) {
+ var i, len, foundIndex, events, perEventHandlers, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+ if (arguments.length === 0) {
+ events = _keys(handlers);
+ } else if (typeof eventType === "string" && eventType) {
+ events = eventType.split(/\s+/);
+ } else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ this.off(i, eventType[i]);
+ }
+ }
+ }
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].toLowerCase().replace(/^on/, "");
+ perEventHandlers = handlers[eventType];
+ if (perEventHandlers && perEventHandlers.length) {
+ if (listener) {
+ foundIndex = perEventHandlers.indexOf(listener);
+ while (foundIndex !== -1) {
+ perEventHandlers.splice(foundIndex, 1);
+ foundIndex = perEventHandlers.indexOf(listener, foundIndex);
+ }
+ } else {
+ perEventHandlers.length = 0;
+ }
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.
+ * @private
+ */
+ var _clientListeners = function(eventType) {
+ var copy = null, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+ if (handlers) {
+ if (typeof eventType === "string" && eventType) {
+ copy = handlers[eventType] ? handlers[eventType].slice(0) : [];
+ } else {
+ copy = _deepCopy(handlers);
+ }
+ }
+ return copy;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.
+ * @private
+ */
+ var _clientEmit = function(event) {
+ if (_clientShouldEmit.call(this, event)) {
+ if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
+ event = _extend({}, event);
+ }
+ var eventCopy = _extend({}, _createEvent(event), {
+ client: this
+ });
+ _clientDispatchCallbacks.call(this, eventCopy);
+ }
+ return this;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.
+ * @private
+ */
+ var _clientClip = function(elements) {
+ elements = _prepClip(elements);
+ for (var i = 0; i < elements.length; i++) {
+ if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
+ if (!elements[i].zcClippingId) {
+ elements[i].zcClippingId = "zcClippingId_" + _elementIdCounter++;
+ _elementMeta[elements[i].zcClippingId] = [ this.id ];
+ if (_globalConfig.autoActivate === true) {
+ _addMouseHandlers(elements[i]);
+ }
+ } else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {
+ _elementMeta[elements[i].zcClippingId].push(this.id);
+ }
+ var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;
+ if (clippedElements.indexOf(elements[i]) === -1) {
+ clippedElements.push(elements[i]);
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.
+ * @private
+ */
+ var _clientUnclip = function(elements) {
+ var meta = _clientMeta[this.id];
+ if (!meta) {
+ return this;
+ }
+ var clippedElements = meta.elements;
+ var arrayIndex;
+ if (typeof elements === "undefined") {
+ elements = clippedElements.slice(0);
+ } else {
+ elements = _prepClip(elements);
+ }
+ for (var i = elements.length; i--; ) {
+ if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
+ arrayIndex = 0;
+ while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {
+ clippedElements.splice(arrayIndex, 1);
+ }
+ var clientIds = _elementMeta[elements[i].zcClippingId];
+ if (clientIds) {
+ arrayIndex = 0;
+ while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {
+ clientIds.splice(arrayIndex, 1);
+ }
+ if (clientIds.length === 0) {
+ if (_globalConfig.autoActivate === true) {
+ _removeMouseHandlers(elements[i]);
+ }
+ delete elements[i].zcClippingId;
+ }
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.
+ * @private
+ */
+ var _clientElements = function() {
+ var meta = _clientMeta[this.id];
+ return meta && meta.elements ? meta.elements.slice(0) : [];
+ };
+ /**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.
+ * @private
+ */
+ var _clientDestroy = function() {
+ this.unclip();
+ this.off();
+ delete _clientMeta[this.id];
+ };
+ /**
+ * Inspect an Event to see if the Client (`this`) should honor it for emission.
+ * @private
+ */
+ var _clientShouldEmit = function(event) {
+ if (!(event && event.type)) {
+ return false;
+ }
+ if (event.client && event.client !== this) {
+ return false;
+ }
+ var clippedEls = _clientMeta[this.id] && _clientMeta[this.id].elements;
+ var hasClippedEls = !!clippedEls && clippedEls.length > 0;
+ var goodTarget = !event.target || hasClippedEls && clippedEls.indexOf(event.target) !== -1;
+ var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;
+ var goodClient = event.client && event.client === this;
+ if (!(goodTarget || goodRelTarget || goodClient)) {
+ return false;
+ }
+ return true;
+ };
+ /**
+ * Handle the actual dispatching of events to a client instance.
+ *
+ * @returns `this`
+ * @private
+ */
+ var _clientDispatchCallbacks = function(event) {
+ if (!(typeof event === "object" && event && event.type)) {
+ return;
+ }
+ var async = _shouldPerformAsync(event);
+ var wildcardTypeHandlers = _clientMeta[this.id] && _clientMeta[this.id].handlers["*"] || [];
+ var specificTypeHandlers = _clientMeta[this.id] && _clientMeta[this.id].handlers[event.type] || [];
+ var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
+ if (handlers && handlers.length) {
+ var i, len, func, context, eventCopy, originalContext = this;
+ for (i = 0, len = handlers.length; i < len; i++) {
+ func = handlers[i];
+ context = originalContext;
+ if (typeof func === "string" && typeof _window[func] === "function") {
+ func = _window[func];
+ }
+ if (typeof func === "object" && func && typeof func.handleEvent === "function") {
+ context = func;
+ func = func.handleEvent;
+ }
+ if (typeof func === "function") {
+ eventCopy = _extend({}, event);
+ _dispatchCallback(func, context, [ eventCopy ], async);
+ }
+ }
+ }
+ return this;
+ };
+ /**
+ * Prepares the elements for clipping/unclipping.
+ *
+ * @returns An Array of elements.
+ * @private
+ */
+ var _prepClip = function(elements) {
+ if (typeof elements === "string") {
+ elements = [];
+ }
+ return typeof elements.length !== "number" ? [ elements ] : elements;
+ };
+ /**
+ * Add a `mouseover` handler function for a clipped element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _addMouseHandlers = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+ var _suppressMouseEvents = function(event) {
+ if (!(event || (event = _window.event))) {
+ return;
+ }
+ if (event._source !== "js") {
+ event.stopImmediatePropagation();
+ event.preventDefault();
+ }
+ delete event._source;
+ };
+ var _elementMouseOver = function(event) {
+ if (!(event || (event = _window.event))) {
+ return;
+ }
+ _suppressMouseEvents(event);
+ ZeroClipboard.focus(element);
+ };
+ element.addEventListener("mouseover", _elementMouseOver, false);
+ element.addEventListener("mouseout", _suppressMouseEvents, false);
+ element.addEventListener("mouseenter", _suppressMouseEvents, false);
+ element.addEventListener("mouseleave", _suppressMouseEvents, false);
+ element.addEventListener("mousemove", _suppressMouseEvents, false);
+ _mouseHandlers[element.zcClippingId] = {
+ mouseover: _elementMouseOver,
+ mouseout: _suppressMouseEvents,
+ mouseenter: _suppressMouseEvents,
+ mouseleave: _suppressMouseEvents,
+ mousemove: _suppressMouseEvents
+ };
+ };
+ /**
+ * Remove a `mouseover` handler function for a clipped element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+ var _removeMouseHandlers = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+ var mouseHandlers = _mouseHandlers[element.zcClippingId];
+ if (!(typeof mouseHandlers === "object" && mouseHandlers)) {
+ return;
+ }
+ var key, val, mouseEvents = [ "move", "leave", "enter", "out", "over" ];
+ for (var i = 0, len = mouseEvents.length; i < len; i++) {
+ key = "mouse" + mouseEvents[i];
+ val = mouseHandlers[key];
+ if (typeof val === "function") {
+ element.removeEventListener(key, val, false);
+ }
+ }
+ delete _mouseHandlers[element.zcClippingId];
+ };
+ /**
+ * Creates a new ZeroClipboard client instance.
+ * Optionally, auto-`clip` an element or collection of elements.
+ *
+ * @constructor
+ */
+ ZeroClipboard._createClient = function() {
+ _clientConstructor.apply(this, _args(arguments));
+ };
+ /**
+ * Register an event listener to the client.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.on = function() {
+ return _clientOn.apply(this, _args(arguments));
+ };
+ /**
+ * Unregister an event handler from the client.
+ * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`.
+ * If no `eventType` is provided, it will unregister all handlers for every event type.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.off = function() {
+ return _clientOff.apply(this, _args(arguments));
+ };
+ /**
+ * Retrieve event listeners for an `eventType` from the client.
+ * If no `eventType` is provided, it will retrieve all listeners for every event type.
+ *
+ * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
+ */
+ ZeroClipboard.prototype.handlers = function() {
+ return _clientListeners.apply(this, _args(arguments));
+ };
+ /**
+ * Event emission receiver from the Flash object for this client's registered JavaScript event listeners.
+ *
+ * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
+ */
+ ZeroClipboard.prototype.emit = function() {
+ return _clientEmit.apply(this, _args(arguments));
+ };
+ /**
+ * Register clipboard actions for new element(s) to the client.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.clip = function() {
+ return _clientClip.apply(this, _args(arguments));
+ };
+ /**
+ * Unregister the clipboard actions of previously registered element(s) on the page.
+ * If no elements are provided, ALL registered elements will be unregistered.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.unclip = function() {
+ return _clientUnclip.apply(this, _args(arguments));
+ };
+ /**
+ * Get all of the elements to which this client is clipped.
+ *
+ * @returns array of clipped elements
+ */
+ ZeroClipboard.prototype.elements = function() {
+ return _clientElements.apply(this, _args(arguments));
+ };
+ /**
+ * Self-destruct and clean up everything for a single client.
+ * This will NOT destroy the embedded Flash object.
+ *
+ * @returns `undefined`
+ */
+ ZeroClipboard.prototype.destroy = function() {
+ return _clientDestroy.apply(this, _args(arguments));
+ };
+ /**
+ * Stores the pending plain text to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.setText = function(text) {
+ ZeroClipboard.setData("text/plain", text);
+ return this;
+ };
+ /**
+ * Stores the pending HTML text to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.setHtml = function(html) {
+ ZeroClipboard.setData("text/html", html);
+ return this;
+ };
+ /**
+ * Stores the pending rich text (RTF) to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.setRichText = function(richText) {
+ ZeroClipboard.setData("application/rtf", richText);
+ return this;
+ };
+ /**
+ * Stores the pending data to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.setData = function() {
+ ZeroClipboard.setData.apply(this, _args(arguments));
+ return this;
+ };
+ /**
+ * Clears the pending data to inject into the clipboard.
+ * If no `format` is provided, all pending data formats will be cleared.
+ *
+ * @returns `this`
+ */
+ ZeroClipboard.prototype.clearData = function() {
+ ZeroClipboard.clearData.apply(this, _args(arguments));
+ return this;
+ };
+ /**
+ * Gets a copy of the pending data to inject into the clipboard.
+ * If no `format` is provided, a copy of ALL pending data formats will be returned.
+ *
+ * @returns `String` or `Object`
+ */
+ ZeroClipboard.prototype.getData = function() {
+ return ZeroClipboard.getData.apply(this, _args(arguments));
+ };
+ if (typeof define === "function" && define.amd) {
+ define(function() {
+ return ZeroClipboard;
+ });
+ } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) {
+ module.exports = ZeroClipboard;
+ } else {
+ window.ZeroClipboard = ZeroClipboard;
+ }
+})(function() {
+ return this || window;
+}());
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.min.js b/showcase/js/zeroclipboard/dist/ZeroClipboard.min.js
new file mode 100755
index 0000000..fea2a1d
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/ZeroClipboard.min.js
@@ -0,0 +1,10 @@
+/*!
+ * ZeroClipboard
+ * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.
+ * Copyright (c) 2014 Jon Rohan, James M. Greene
+ * Licensed MIT
+ * http://zeroclipboard.org/
+ * v2.1.6
+ */
+!function(a,b){"use strict";var c,d,e=a,f=e.document,g=e.navigator,h=e.setTimeout,i=e.encodeURIComponent,j=e.ActiveXObject,k=e.Error,l=e.Number.parseInt||e.parseInt,m=e.Number.parseFloat||e.parseFloat,n=e.Number.isNaN||e.isNaN,o=e.Math.round,p=e.Date.now,q=e.Object.keys,r=e.Object.defineProperty,s=e.Object.prototype.hasOwnProperty,t=e.Array.prototype.slice,u=function(){var a=function(a){return a};if("function"==typeof e.wrap&&"function"==typeof e.unwrap)try{var b=f.createElement("div"),c=e.unwrap(b);1===b.nodeType&&c&&1===c.nodeType&&(a=e.unwrap)}catch(d){}return a}(),v=function(a){return t.call(a,0)},w=function(){var a,c,d,e,f,g,h=v(arguments),i=h[0]||{};for(a=1,c=h.length;c>a;a++)if(null!=(d=h[a]))for(e in d)s.call(d,e)&&(f=i[e],g=d[e],i!==g&&g!==b&&(i[e]=g));return i},x=function(a){var b,c,d,e;if("object"!=typeof a||null==a)b=a;else if("number"==typeof a.length)for(b=[],c=0,d=a.length;d>c;c++)s.call(a,c)&&(b[c]=x(a[c]));else{b={};for(e in a)s.call(a,e)&&(b[e]=x(a[e]))}return b},y=function(a,b){for(var c={},d=0,e=b.length;e>d;d++)b[d]in a&&(c[b[d]]=a[b[d]]);return c},z=function(a,b){var c={};for(var d in a)-1===b.indexOf(d)&&(c[d]=a[d]);return c},A=function(a){if(a)for(var b in a)s.call(a,b)&&delete a[b];return a},B=function(a,b){if(a&&1===a.nodeType&&a.ownerDocument&&b&&(1===b.nodeType&&b.ownerDocument&&b.ownerDocument===a.ownerDocument||9===b.nodeType&&!b.ownerDocument&&b===a.ownerDocument))do{if(a===b)return!0;a=a.parentNode}while(a);return!1},C=function(a){var b;return"string"==typeof a&&a&&(b=a.split("#")[0].split("?")[0],b=a.slice(0,a.lastIndexOf("/")+1)),b},D=function(a){var b,c;return"string"==typeof a&&a&&(c=a.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]?b=c[1]:(c=a.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/),c&&c[1]&&(b=c[1]))),b},E=function(){var a,b;try{throw new k}catch(c){b=c}return b&&(a=b.sourceURL||b.fileName||D(b.stack)),a},F=function(){var a,c,d;if(f.currentScript&&(a=f.currentScript.src))return a;if(c=f.getElementsByTagName("script"),1===c.length)return c[0].src||b;if("readyState"in c[0])for(d=c.length;d--;)if("interactive"===c[d].readyState&&(a=c[d].src))return a;return"loading"===f.readyState&&(a=c[c.length-1].src)?a:(a=E())?a:b},G=function(){var a,c,d,e=f.getElementsByTagName("script");for(a=e.length;a--;){if(!(d=e[a].src)){c=null;break}if(d=C(d),null==c)c=d;else if(c!==d){c=null;break}}return c||b},H=function(){var a=C(F())||G()||"";return a+"ZeroClipboard.swf"},I={bridge:null,version:"0.0.0",pluginType:"unknown",disabled:null,outdated:null,unavailable:null,deactivated:null,overdue:null,ready:null},J="11.0.0",K={},L={},M=null,N={ready:"Flash communication is established",error:{"flash-disabled":"Flash is disabled or not installed","flash-outdated":"Flash is too outdated to support ZeroClipboard","flash-unavailable":"Flash is unable to communicate bidirectionally with JavaScript","flash-deactivated":"Flash is too outdated for your browser and/or is configured as click-to-activate","flash-overdue":"Flash communication was established but NOT within the acceptable time limit"}},O={swfPath:H(),trustedDomains:a.location.host?[a.location.host]:[],cacheBust:!0,forceEnhancedClipboard:!1,flashLoadTimeout:3e4,autoActivate:!0,bubbleEvents:!0,containerId:"global-zeroclipboard-html-bridge",containerClass:"global-zeroclipboard-container",swfObjectId:"global-zeroclipboard-flash-bridge",hoverClass:"zeroclipboard-is-hover",activeClass:"zeroclipboard-is-active",forceHandCursor:!1,title:null,zIndex:999999999},P=function(a){if("object"==typeof a&&null!==a)for(var b in a)if(s.call(a,b))if(/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(b))O[b]=a[b];else if(null==I.bridge)if("containerId"===b||"swfObjectId"===b){if(!cb(a[b]))throw new Error("The specified `"+b+"` value is not valid as an HTML4 Element ID");O[b]=a[b]}else O[b]=a[b];{if("string"!=typeof a||!a)return x(O);if(s.call(O,a))return O[a]}},Q=function(){return{browser:y(g,["userAgent","platform","appName"]),flash:z(I,["bridge"]),zeroclipboard:{version:Fb.version,config:Fb.config()}}},R=function(){return!!(I.disabled||I.outdated||I.unavailable||I.deactivated)},S=function(a,b){var c,d,e,f={};if("string"==typeof a&&a)e=a.toLowerCase().split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)s.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Fb.on(c,a[c]);if(e&&e.length){for(c=0,d=e.length;d>c;c++)a=e[c].replace(/^on/,""),f[a]=!0,K[a]||(K[a]=[]),K[a].push(b);if(f.ready&&I.ready&&Fb.emit({type:"ready"}),f.error){var g=["disabled","outdated","unavailable","deactivated","overdue"];for(c=0,d=g.length;d>c;c++)if(I[g[c]]===!0){Fb.emit({type:"error",name:"flash-"+g[c]});break}}}return Fb},T=function(a,b){var c,d,e,f,g;if(0===arguments.length)f=q(K);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)s.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&Fb.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=K[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return Fb},U=function(a){var b;return b="string"==typeof a&&a?x(K[a])||null:x(K)},V=function(a){var b,c,d;return a=db(a),a&&!jb(a)?"ready"===a.type&&I.overdue===!0?Fb.emit({type:"error",name:"flash-overdue"}):(b=w({},a),ib.call(this,b),"copy"===a.type&&(d=pb(L),c=d.data,M=d.formatMap),c):void 0},W=function(){if("boolean"!=typeof I.ready&&(I.ready=!1),!Fb.isFlashUnusable()&&null===I.bridge){var a=O.flashLoadTimeout;"number"==typeof a&&a>=0&&h(function(){"boolean"!=typeof I.deactivated&&(I.deactivated=!0),I.deactivated===!0&&Fb.emit({type:"error",name:"flash-deactivated"})},a),I.overdue=!1,nb()}},X=function(){Fb.clearData(),Fb.blur(),Fb.emit("destroy"),ob(),Fb.off()},Y=function(a,b){var c;if("object"==typeof a&&a&&"undefined"==typeof b)c=a,Fb.clearData();else{if("string"!=typeof a||!a)return;c={},c[a]=b}for(var d in c)"string"==typeof d&&d&&s.call(c,d)&&"string"==typeof c[d]&&c[d]&&(L[d]=c[d])},Z=function(a){"undefined"==typeof a?(A(L),M=null):"string"==typeof a&&s.call(L,a)&&delete L[a]},$=function(a){return"undefined"==typeof a?x(L):"string"==typeof a&&s.call(L,a)?L[a]:void 0},_=function(a){if(a&&1===a.nodeType){c&&(xb(c,O.activeClass),c!==a&&xb(c,O.hoverClass)),c=a,wb(a,O.hoverClass);var b=a.getAttribute("title")||O.title;if("string"==typeof b&&b){var d=mb(I.bridge);d&&d.setAttribute("title",b)}var e=O.forceHandCursor===!0||"pointer"===yb(a,"cursor");Cb(e),Bb()}},ab=function(){var a=mb(I.bridge);a&&(a.removeAttribute("title"),a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.top="1px"),c&&(xb(c,O.hoverClass),xb(c,O.activeClass),c=null)},bb=function(){return c||null},cb=function(a){return"string"==typeof a&&a&&/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(a)},db=function(a){var b;if("string"==typeof a&&a?(b=a,a={}):"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(b=a.type),b){!a.target&&/^(copy|aftercopy|_click)$/.test(b.toLowerCase())&&(a.target=d),w(a,{type:b.toLowerCase(),target:a.target||c||null,relatedTarget:a.relatedTarget||null,currentTarget:I&&I.bridge||null,timeStamp:a.timeStamp||p()||null});var e=N[a.type];return"error"===a.type&&a.name&&e&&(e=e[a.name]),e&&(a.message=e),"ready"===a.type&&w(a,{target:null,version:I.version}),"error"===a.type&&(/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(a.name)&&w(a,{target:null,minimumVersion:J}),/^flash-(outdated|unavailable|deactivated|overdue)$/.test(a.name)&&w(a,{version:I.version})),"copy"===a.type&&(a.clipboardData={setData:Fb.setData,clearData:Fb.clearData}),"aftercopy"===a.type&&(a=qb(a,M)),a.target&&!a.relatedTarget&&(a.relatedTarget=eb(a.target)),a=fb(a)}},eb=function(a){var b=a&&a.getAttribute&&a.getAttribute("data-clipboard-target");return b?f.getElementById(b):null},fb=function(a){if(a&&/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)){var c=a.target,d="_mouseover"===a.type&&a.relatedTarget?a.relatedTarget:b,g="_mouseout"===a.type&&a.relatedTarget?a.relatedTarget:b,h=Ab(c),i=e.screenLeft||e.screenX||0,j=e.screenTop||e.screenY||0,k=f.body.scrollLeft+f.documentElement.scrollLeft,l=f.body.scrollTop+f.documentElement.scrollTop,m=h.left+("number"==typeof a._stageX?a._stageX:0),n=h.top+("number"==typeof a._stageY?a._stageY:0),o=m-k,p=n-l,q=i+o,r=j+p,s="number"==typeof a.movementX?a.movementX:0,t="number"==typeof a.movementY?a.movementY:0;delete a._stageX,delete a._stageY,w(a,{srcElement:c,fromElement:d,toElement:g,screenX:q,screenY:r,pageX:m,pageY:n,clientX:o,clientY:p,x:o,y:p,movementX:s,movementY:t,offsetX:0,offsetY:0,layerX:0,layerY:0})}return a},gb=function(a){var b=a&&"string"==typeof a.type&&a.type||"";return!/^(?:(?:before)?copy|destroy)$/.test(b)},hb=function(a,b,c,d){d?h(function(){a.apply(b,c)},0):a.apply(b,c)},ib=function(a){if("object"==typeof a&&a&&a.type){var b=gb(a),c=K["*"]||[],d=K[a.type]||[],f=c.concat(d);if(f&&f.length){var g,h,i,j,k,l=this;for(g=0,h=f.length;h>g;g++)i=f[g],j=l,"string"==typeof i&&"function"==typeof e[i]&&(i=e[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=w({},a),hb(i,j,[k],b))}return this}},jb=function(a){var b=a.target||c||null,e="swf"===a._source;delete a._source;var f=["flash-disabled","flash-outdated","flash-unavailable","flash-deactivated","flash-overdue"];switch(a.type){case"error":-1!==f.indexOf(a.name)&&w(I,{disabled:"flash-disabled"===a.name,outdated:"flash-outdated"===a.name,unavailable:"flash-unavailable"===a.name,deactivated:"flash-deactivated"===a.name,overdue:"flash-overdue"===a.name,ready:!1});break;case"ready":var g=I.deactivated===!0;w(I,{disabled:!1,outdated:!1,unavailable:!1,deactivated:!1,overdue:g,ready:!g});break;case"beforecopy":d=b;break;case"copy":var h,i,j=a.relatedTarget;!L["text/html"]&&!L["text/plain"]&&j&&(i=j.value||j.outerHTML||j.innerHTML)&&(h=j.value||j.textContent||j.innerText)?(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",h),i!==h&&a.clipboardData.setData("text/html",i)):!L["text/plain"]&&a.target&&(h=a.target.getAttribute("data-clipboard-text"))&&(a.clipboardData.clearData(),a.clipboardData.setData("text/plain",h));break;case"aftercopy":Fb.clearData(),b&&b!==vb()&&b.focus&&b.focus();break;case"_mouseover":Fb.focus(b),O.bubbleEvents===!0&&e&&(b&&b!==a.relatedTarget&&!B(a.relatedTarget,b)&&kb(w({},a,{type:"mouseenter",bubbles:!1,cancelable:!1})),kb(w({},a,{type:"mouseover"})));break;case"_mouseout":Fb.blur(),O.bubbleEvents===!0&&e&&(b&&b!==a.relatedTarget&&!B(a.relatedTarget,b)&&kb(w({},a,{type:"mouseleave",bubbles:!1,cancelable:!1})),kb(w({},a,{type:"mouseout"})));break;case"_mousedown":wb(b,O.activeClass),O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_mouseup":xb(b,O.activeClass),O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_click":d=null,O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}));break;case"_mousemove":O.bubbleEvents===!0&&e&&kb(w({},a,{type:a.type.slice(1)}))}return/^_(?:click|mouse(?:over|out|down|up|move))$/.test(a.type)?!0:void 0},kb=function(a){if(a&&"string"==typeof a.type&&a){var b,c=a.target||null,d=c&&c.ownerDocument||f,g={view:d.defaultView||e,canBubble:!0,cancelable:!0,detail:"click"===a.type?1:0,button:"number"==typeof a.which?a.which-1:"number"==typeof a.button?a.button:d.createEvent?0:1},h=w(g,a);c&&d.createEvent&&c.dispatchEvent&&(h=[h.type,h.canBubble,h.cancelable,h.view,h.detail,h.screenX,h.screenY,h.clientX,h.clientY,h.ctrlKey,h.altKey,h.shiftKey,h.metaKey,h.button,h.relatedTarget],b=d.createEvent("MouseEvents"),b.initMouseEvent&&(b.initMouseEvent.apply(b,h),b._source="js",c.dispatchEvent(b)))}},lb=function(){var a=f.createElement("div");return a.id=O.containerId,a.className=O.containerClass,a.style.position="absolute",a.style.left="0px",a.style.top="-9999px",a.style.width="1px",a.style.height="1px",a.style.zIndex=""+Db(O.zIndex),a},mb=function(a){for(var b=a&&a.parentNode;b&&"OBJECT"===b.nodeName&&b.parentNode;)b=b.parentNode;return b||null},nb=function(){var a,b=I.bridge,c=mb(b);if(!b){var d=ub(e.location.host,O),g="never"===d?"none":"all",h=sb(O),i=O.swfPath+rb(O.swfPath,O);c=lb();var j=f.createElement("div");c.appendChild(j),f.body.appendChild(c);var k=f.createElement("div"),l="activex"===I.pluginType;k.innerHTML='',b=k.firstChild,k=null,u(b).ZeroClipboard=Fb,c.replaceChild(b,j)}return b||(b=f[O.swfObjectId],b&&(a=b.length)&&(b=b[a-1]),!b&&c&&(b=c.firstChild)),I.bridge=b||null,b},ob=function(){var a=I.bridge;if(a){var b=mb(a);b&&("activex"===I.pluginType&&"readyState"in a?(a.style.display="none",function c(){if(4===a.readyState){for(var d in a)"function"==typeof a[d]&&(a[d]=null);a.parentNode&&a.parentNode.removeChild(a),b.parentNode&&b.parentNode.removeChild(b)}else h(c,10)}()):(a.parentNode&&a.parentNode.removeChild(a),b.parentNode&&b.parentNode.removeChild(b))),I.ready=null,I.bridge=null,I.deactivated=null}},pb=function(a){var b={},c={};if("object"==typeof a&&a){for(var d in a)if(d&&s.call(a,d)&&"string"==typeof a[d]&&a[d])switch(d.toLowerCase()){case"text/plain":case"text":case"air:text":case"flash:text":b.text=a[d],c.text=d;break;case"text/html":case"html":case"air:html":case"flash:html":b.html=a[d],c.html=d;break;case"application/rtf":case"text/rtf":case"rtf":case"richtext":case"air:rtf":case"flash:rtf":b.rtf=a[d],c.rtf=d}return{data:b,formatMap:c}}},qb=function(a,b){if("object"!=typeof a||!a||"object"!=typeof b||!b)return a;var c={};for(var d in a)if(s.call(a,d)){if("success"!==d&&"data"!==d){c[d]=a[d];continue}c[d]={};var e=a[d];for(var f in e)f&&s.call(e,f)&&s.call(b,f)&&(c[d][b[f]]=e[f])}return c},rb=function(a,b){var c=null==b||b&&b.cacheBust===!0;return c?(-1===a.indexOf("?")?"?":"&")+"noCache="+p():""},sb=function(a){var b,c,d,f,g="",h=[];if(a.trustedDomains&&("string"==typeof a.trustedDomains?f=[a.trustedDomains]:"object"==typeof a.trustedDomains&&"length"in a.trustedDomains&&(f=a.trustedDomains)),f&&f.length)for(b=0,c=f.length;c>b;b++)if(s.call(f,b)&&f[b]&&"string"==typeof f[b]){if(d=tb(f[b]),!d)continue;if("*"===d){h.length=0,h.push(d);break}h.push.apply(h,[d,"//"+d,e.location.protocol+"//"+d])}return h.length&&(g+="trustedOrigins="+i(h.join(","))),a.forceEnhancedClipboard===!0&&(g+=(g?"&":"")+"forceEnhancedClipboard=true"),"string"==typeof a.swfObjectId&&a.swfObjectId&&(g+=(g?"&":"")+"swfObjectId="+i(a.swfObjectId)),g},tb=function(a){if(null==a||""===a)return null;if(a=a.replace(/^\s+|\s+$/g,""),""===a)return null;var b=a.indexOf("//");a=-1===b?a:a.slice(b+2);var c=a.indexOf("/");return a=-1===c?a:-1===b||0===c?null:a.slice(0,c),a&&".swf"===a.slice(-4).toLowerCase()?null:a||null},ub=function(){var a=function(a){var b,c,d,e=[];if("string"==typeof a&&(a=[a]),"object"!=typeof a||!a||"number"!=typeof a.length)return e;for(b=0,c=a.length;c>b;b++)if(s.call(a,b)&&(d=tb(a[b]))){if("*"===d){e.length=0,e.push("*");break}-1===e.indexOf(d)&&e.push(d)}return e};return function(b,c){var d=tb(c.swfPath);null===d&&(d=b);var e=a(c.trustedDomains),f=e.length;if(f>0){if(1===f&&"*"===e[0])return"always";if(-1!==e.indexOf(b))return 1===f&&b===d?"sameDomain":"always"}return"never"}}(),vb=function(){try{return f.activeElement}catch(a){return null}},wb=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)||a.classList.add(b),a;if(b&&"string"==typeof b){var c=(b||"").split(/\s+/);if(1===a.nodeType)if(a.className){for(var d=" "+a.className+" ",e=a.className,f=0,g=c.length;g>f;f++)d.indexOf(" "+c[f]+" ")<0&&(e+=" "+c[f]);a.className=e.replace(/^\s+|\s+$/g,"")}else a.className=b}return a},xb=function(a,b){if(!a||1!==a.nodeType)return a;if(a.classList)return a.classList.contains(b)&&a.classList.remove(b),a;if("string"==typeof b&&b){var c=b.split(/\s+/);if(1===a.nodeType&&a.className){for(var d=(" "+a.className+" ").replace(/[\n\t]/g," "),e=0,f=c.length;f>e;e++)d=d.replace(" "+c[e]+" "," ");a.className=d.replace(/^\s+|\s+$/g,"")}}return a},yb=function(a,b){var c=e.getComputedStyle(a,null).getPropertyValue(b);return"cursor"!==b||c&&"auto"!==c||"A"!==a.nodeName?c:"pointer"},zb=function(){var a,b,c,d=1;return"function"==typeof f.body.getBoundingClientRect&&(a=f.body.getBoundingClientRect(),b=a.right-a.left,c=f.body.offsetWidth,d=o(b/c*100)/100),d},Ab=function(a){var b={left:0,top:0,width:0,height:0};if(a.getBoundingClientRect){var c,d,g,h=a.getBoundingClientRect();"pageXOffset"in e&&"pageYOffset"in e?(c=e.pageXOffset,d=e.pageYOffset):(g=zb(),c=o(f.documentElement.scrollLeft/g),d=o(f.documentElement.scrollTop/g));var i=f.documentElement.clientLeft||0,j=f.documentElement.clientTop||0;b.left=h.left+c-i,b.top=h.top+d-j,b.width="width"in h?h.width:h.right-h.left,b.height="height"in h?h.height:h.bottom-h.top}return b},Bb=function(){var a;if(c&&(a=mb(I.bridge))){var b=Ab(c);w(a.style,{width:b.width+"px",height:b.height+"px",top:b.top+"px",left:b.left+"px",zIndex:""+Db(O.zIndex)})}},Cb=function(a){I.ready===!0&&(I.bridge&&"function"==typeof I.bridge.setHandCursor?I.bridge.setHandCursor(a):I.ready=!1)},Db=function(a){if(/^(?:auto|inherit)$/.test(a))return a;var b;return"number"!=typeof a||n(a)?"string"==typeof a&&(b=Db(l(a,10))):b=a,"number"==typeof b?b:"auto"},Eb=function(a){function b(a){var b=a.match(/[\d]+/g);return b.length=3,b.join(".")}function c(a){return!!a&&(a=a.toLowerCase())&&(/^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(a)||"chrome.plugin"===a.slice(-13))}function d(a){a&&(i=!0,a.version&&(l=b(a.version)),!l&&a.description&&(l=b(a.description)),a.filename&&(k=c(a.filename)))}var e,f,h,i=!1,j=!1,k=!1,l="";if(g.plugins&&g.plugins.length)e=g.plugins["Shockwave Flash"],d(e),g.plugins["Shockwave Flash 2.0"]&&(i=!0,l="2.0.0.11");else if(g.mimeTypes&&g.mimeTypes.length)h=g.mimeTypes["application/x-shockwave-flash"],e=h&&h.enabledPlugin,d(e);else if("undefined"!=typeof a){j=!0;try{f=new a("ShockwaveFlash.ShockwaveFlash.7"),i=!0,l=b(f.GetVariable("$version"))}catch(n){try{f=new a("ShockwaveFlash.ShockwaveFlash.6"),i=!0,l="6.0.21"}catch(o){try{f=new a("ShockwaveFlash.ShockwaveFlash"),i=!0,l=b(f.GetVariable("$version"))}catch(p){j=!1}}}}I.disabled=i!==!0,I.outdated=l&&m(l)c;c++)a=e[c].replace(/^on/,""),f[a]=!0,g[a]||(g[a]=[]),g[a].push(b);if(f.ready&&I.ready&&this.emit({type:"ready",client:this}),f.error){var h=["disabled","outdated","unavailable","deactivated","overdue"];for(c=0,d=h.length;d>c;c++)if(I[h[c]]){this.emit({type:"error",name:"flash-"+h[c],client:this});break}}}return this},Nb=function(a,b){var c,d,e,f,g,h=Hb[this.id]&&Hb[this.id].handlers;if(0===arguments.length)f=q(h);else if("string"==typeof a&&a)f=a.split(/\s+/);else if("object"==typeof a&&a&&"undefined"==typeof b)for(c in a)s.call(a,c)&&"string"==typeof c&&c&&"function"==typeof a[c]&&this.off(c,a[c]);if(f&&f.length)for(c=0,d=f.length;d>c;c++)if(a=f[c].toLowerCase().replace(/^on/,""),g=h[a],g&&g.length)if(b)for(e=g.indexOf(b);-1!==e;)g.splice(e,1),e=g.indexOf(b,e);else g.length=0;return this},Ob=function(a){var b=null,c=Hb[this.id]&&Hb[this.id].handlers;return c&&(b="string"==typeof a&&a?c[a]?c[a].slice(0):[]:x(c)),b},Pb=function(a){if(Ub.call(this,a)){"object"==typeof a&&a&&"string"==typeof a.type&&a.type&&(a=w({},a));var b=w({},db(a),{client:this});Vb.call(this,b)}return this},Qb=function(a){a=Wb(a);for(var b=0;b0,d=!a.target||c&&-1!==b.indexOf(a.target),e=a.relatedTarget&&c&&-1!==b.indexOf(a.relatedTarget),f=a.client&&a.client===this;return d||e||f?!0:!1},Vb=function(a){if("object"==typeof a&&a&&a.type){var b=gb(a),c=Hb[this.id]&&Hb[this.id].handlers["*"]||[],d=Hb[this.id]&&Hb[this.id].handlers[a.type]||[],f=c.concat(d);if(f&&f.length){var g,h,i,j,k,l=this;for(g=0,h=f.length;h>g;g++)i=f[g],j=l,"string"==typeof i&&"function"==typeof e[i]&&(i=e[i]),"object"==typeof i&&i&&"function"==typeof i.handleEvent&&(j=i,i=i.handleEvent),"function"==typeof i&&(k=w({},a),hb(i,j,[k],b))}return this}},Wb=function(a){return"string"==typeof a&&(a=[]),"number"!=typeof a.length?[a]:a},Xb=function(a){if(a&&1===a.nodeType){var b=function(a){(a||(a=e.event))&&("js"!==a._source&&(a.stopImmediatePropagation(),a.preventDefault()),delete a._source)},c=function(c){(c||(c=e.event))&&(b(c),Fb.focus(a))};a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",b,!1),a.addEventListener("mouseenter",b,!1),a.addEventListener("mouseleave",b,!1),a.addEventListener("mousemove",b,!1),Kb[a.zcClippingId]={mouseover:c,mouseout:b,mouseenter:b,mouseleave:b,mousemove:b}}},Yb=function(a){if(a&&1===a.nodeType){var b=Kb[a.zcClippingId];if("object"==typeof b&&b){for(var c,d,e=["move","leave","enter","out","over"],f=0,g=e.length;g>f;f++)c="mouse"+e[f],d=b[c],"function"==typeof d&&a.removeEventListener(c,d,!1);delete Kb[a.zcClippingId]}}};Fb._createClient=function(){Lb.apply(this,v(arguments))},Fb.prototype.on=function(){return Mb.apply(this,v(arguments))},Fb.prototype.off=function(){return Nb.apply(this,v(arguments))},Fb.prototype.handlers=function(){return Ob.apply(this,v(arguments))},Fb.prototype.emit=function(){return Pb.apply(this,v(arguments))},Fb.prototype.clip=function(){return Qb.apply(this,v(arguments))},Fb.prototype.unclip=function(){return Rb.apply(this,v(arguments))},Fb.prototype.elements=function(){return Sb.apply(this,v(arguments))},Fb.prototype.destroy=function(){return Tb.apply(this,v(arguments))},Fb.prototype.setText=function(a){return Fb.setData("text/plain",a),this},Fb.prototype.setHtml=function(a){return Fb.setData("text/html",a),this},Fb.prototype.setRichText=function(a){return Fb.setData("application/rtf",a),this},Fb.prototype.setData=function(){return Fb.setData.apply(this,v(arguments)),this},Fb.prototype.clearData=function(){return Fb.clearData.apply(this,v(arguments)),this},Fb.prototype.getData=function(){return Fb.getData.apply(this,v(arguments))},"function"==typeof define&&define.amd?define(function(){return Fb}):"object"==typeof module&&module&&"object"==typeof module.exports&&module.exports?module.exports=Fb:a.ZeroClipboard=Fb}(function(){return this||window}());
+//# sourceMappingURL=ZeroClipboard.min.map
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.min.map b/showcase/js/zeroclipboard/dist/ZeroClipboard.min.map
new file mode 100755
index 0000000..854758f
--- /dev/null
+++ b/showcase/js/zeroclipboard/dist/ZeroClipboard.min.map
@@ -0,0 +1 @@
+{"version":3,"file":"ZeroClipboard.min.js","sources":["ZeroClipboard.js"],"names":["window","undefined","_currentElement","_copyTarget","_window","_document","document","_navigator","navigator","_setTimeout","setTimeout","_encodeURIComponent","encodeURIComponent","_ActiveXObject","ActiveXObject","_Error","Error","_parseInt","Number","parseInt","_parseFloat","parseFloat","_isNaN","isNaN","_round","Math","round","_now","Date","now","_keys","Object","keys","_defineProperty","defineProperty","_hasOwn","prototype","hasOwnProperty","_slice","Array","slice","_unwrap","unwrapper","el","wrap","unwrap","div","createElement","unwrappedDiv","nodeType","e","_args","argumentsObj","call","_extend","i","len","arg","prop","src","copy","args","arguments","target","length","_deepCopy","source","_pick","obj","newObj","_omit","indexOf","_deleteOwnProperties","_containedBy","ancestorEl","ownerDocument","parentNode","_getDirPathOfUrl","url","dir","split","lastIndexOf","_getCurrentScriptUrlFromErrorStack","stack","matches","match","_getCurrentScriptUrlFromError","err","sourceURL","fileName","_getCurrentScriptUrl","jsPath","scripts","currentScript","getElementsByTagName","readyState","_getUnanimousScriptParentDir","jsDir","_getDefaultSwfPath","_flashState","bridge","version","pluginType","disabled","outdated","unavailable","deactivated","overdue","ready","_minimumFlashVersion","_handlers","_clipData","_clipDataFormatMap","_eventMessages","error","flash-disabled","flash-outdated","flash-unavailable","flash-deactivated","flash-overdue","_globalConfig","swfPath","trustedDomains","location","host","cacheBust","forceEnhancedClipboard","flashLoadTimeout","autoActivate","bubbleEvents","containerId","containerClass","swfObjectId","hoverClass","activeClass","forceHandCursor","title","zIndex","_config","options","test","_isValidHtml4Id","_state","browser","flash","zeroclipboard","ZeroClipboard","config","_isFlashUnusable","_on","eventType","listener","events","added","toLowerCase","on","replace","push","emit","type","errorTypes","name","_off","foundIndex","perEventHandlers","off","splice","_listeners","_emit","event","eventCopy","returnVal","tmp","_createEvent","_preprocessEvent","_dispatchCallbacks","this","_mapClipDataToFlash","data","formatMap","_create","isFlashUnusable","maxWait","_embedSwf","_destroy","clearData","blur","_unembedSwf","_setData","format","dataObj","dataFormat","_clearData","_getData","_focus","element","_removeClass","_addClass","newTitle","getAttribute","htmlBridge","_getHtmlBridge","setAttribute","useHandCursor","_getStyle","_setHandCursor","_reposition","_blur","removeAttribute","style","left","top","width","_activeElement","id","relatedTarget","currentTarget","timeStamp","msg","message","minimumVersion","clipboardData","setData","_mapClipResultsFromFlash","_getRelatedTarget","_addMouseData","targetEl","relatedTargetId","getElementById","srcElement","fromElement","toElement","pos","_getDOMObjectPosition","screenLeft","screenX","screenTop","screenY","scrollLeft","body","documentElement","scrollTop","pageX","_stageX","pageY","_stageY","clientX","clientY","moveX","movementX","moveY","movementY","x","y","offsetX","offsetY","layerX","layerY","_shouldPerformAsync","_dispatchCallback","func","context","async","apply","wildcardTypeHandlers","specificTypeHandlers","handlers","concat","originalContext","handleEvent","sourceIsSwf","_source","flashErrorNames","wasDeactivated","textContent","htmlContent","value","outerHTML","innerHTML","innerText","_safeActiveElement","focus","_fireMouseEvent","bubbles","cancelable","doc","defaults","view","defaultView","canBubble","detail","button","which","createEvent","dispatchEvent","ctrlKey","altKey","shiftKey","metaKey","initMouseEvent","_createHtmlBridge","container","className","position","height","_getSafeZIndex","flashBridge","nodeName","allowScriptAccess","_determineScriptAccess","allowNetworking","flashvars","_vars","swfUrl","_cacheBust","divToBeReplaced","appendChild","tmpDiv","oldIE","firstChild","replaceChild","display","removeSwfFromIE","removeChild","clipData","newClipData","text","html","rtf","clipResults","newResults","tmpHash","path","domain","domains","str","trustedOriginsExpanded","_extractDomain","protocol","join","originOrUrl","protocolIndex","pathIndex","_extractAllDomains","origins","resultsArray","currentDomain","configOptions","swfDomain","activeElement","classList","contains","add","classNames","setClass","c","cl","remove","getComputedStyle","getPropertyValue","_getZoomFactor","rect","physicalWidth","logicalWidth","zoomFactor","getBoundingClientRect","right","offsetWidth","info","pageXOffset","pageYOffset","leftBorderWidth","clientLeft","topBorderWidth","clientTop","bottom","enabled","setHandCursor","val","_detectFlashSupport","parseFlashVersion","desc","isPepperFlash","flashPlayerFileName","inspectPlugin","plugin","hasFlash","flashVersion","description","filename","isPPAPI","ax","mimeType","isActiveX","plugins","mimeTypes","enabledPlugin","GetVariable","e1","e2","e3","_createClient","writable","configurable","enumerable","state","create","destroy","getData","activate","deactivate","_clientIdCounter","_clientMeta","_elementIdCounter","_elementMeta","_mouseHandlers","_clientConstructor","elements","client","instance","clip","_clientOn","_clientOff","_clientListeners","_clientEmit","_clientShouldEmit","_clientDispatchCallbacks","_clientClip","_prepClip","zcClippingId","_addMouseHandlers","clippedElements","_clientUnclip","meta","arrayIndex","clientIds","_removeMouseHandlers","_clientElements","_clientDestroy","unclip","clippedEls","hasClippedEls","goodTarget","goodRelTarget","goodClient","_suppressMouseEvents","stopImmediatePropagation","preventDefault","_elementMouseOver","addEventListener","mouseover","mouseout","mouseenter","mouseleave","mousemove","mouseHandlers","key","mouseEvents","removeEventListener","setText","setHtml","setRichText","richText","define","amd","module","exports"],"mappings":"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","sourcesContent":["/*!\n * ZeroClipboard\n * The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.\n * Copyright (c) 2014 Jon Rohan, James M. Greene\n * Licensed MIT\n * http://zeroclipboard.org/\n * v2.1.6\n */\n(function(window, undefined) {\n \"use strict\";\n /**\n * Store references to critically important global functions that may be\n * overridden on certain web pages.\n */\n var _window = window, _document = _window.document, _navigator = _window.navigator, _setTimeout = _window.setTimeout, _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, _round = _window.Math.round, _now = _window.Date.now, _keys = _window.Object.keys, _defineProperty = _window.Object.defineProperty, _hasOwn = _window.Object.prototype.hasOwnProperty, _slice = _window.Array.prototype.slice, _unwrap = function() {\n var unwrapper = function(el) {\n return el;\n };\n if (typeof _window.wrap === \"function\" && typeof _window.unwrap === \"function\") {\n try {\n var div = _document.createElement(\"div\");\n var unwrappedDiv = _window.unwrap(div);\n if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {\n unwrapper = _window.unwrap;\n }\n } catch (e) {}\n }\n return unwrapper;\n }();\n /**\n * Convert an `arguments` object into an Array.\n *\n * @returns The arguments as an Array\n * @private\n */\n var _args = function(argumentsObj) {\n return _slice.call(argumentsObj, 0);\n };\n /**\n * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.\n *\n * @returns The target object, augmented\n * @private\n */\n var _extend = function() {\n var i, len, arg, prop, src, copy, args = _args(arguments), target = args[0] || {};\n for (i = 1, len = args.length; i < len; i++) {\n if ((arg = args[i]) != null) {\n for (prop in arg) {\n if (_hasOwn.call(arg, prop)) {\n src = target[prop];\n copy = arg[prop];\n if (target !== copy && copy !== undefined) {\n target[prop] = copy;\n }\n }\n }\n }\n }\n return target;\n };\n /**\n * Return a deep copy of the source object or array.\n *\n * @returns Object or Array\n * @private\n */\n var _deepCopy = function(source) {\n var copy, i, len, prop;\n if (typeof source !== \"object\" || source == null) {\n copy = source;\n } else if (typeof source.length === \"number\") {\n copy = [];\n for (i = 0, len = source.length; i < len; i++) {\n if (_hasOwn.call(source, i)) {\n copy[i] = _deepCopy(source[i]);\n }\n }\n } else {\n copy = {};\n for (prop in source) {\n if (_hasOwn.call(source, prop)) {\n copy[prop] = _deepCopy(source[prop]);\n }\n }\n }\n return copy;\n };\n /**\n * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.\n * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to\n * be kept.\n *\n * @returns A new filtered object.\n * @private\n */\n var _pick = function(obj, keys) {\n var newObj = {};\n for (var i = 0, len = keys.length; i < len; i++) {\n if (keys[i] in obj) {\n newObj[keys[i]] = obj[keys[i]];\n }\n }\n return newObj;\n };\n /**\n * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.\n * The inverse of `_pick`.\n *\n * @returns A new filtered object.\n * @private\n */\n var _omit = function(obj, keys) {\n var newObj = {};\n for (var prop in obj) {\n if (keys.indexOf(prop) === -1) {\n newObj[prop] = obj[prop];\n }\n }\n return newObj;\n };\n /**\n * Remove all owned, enumerable properties from an object.\n *\n * @returns The original object without its owned, enumerable properties.\n * @private\n */\n var _deleteOwnProperties = function(obj) {\n if (obj) {\n for (var prop in obj) {\n if (_hasOwn.call(obj, prop)) {\n delete obj[prop];\n }\n }\n }\n return obj;\n };\n /**\n * Determine if an element is contained within another element.\n *\n * @returns Boolean\n * @private\n */\n var _containedBy = function(el, ancestorEl) {\n 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)) {\n do {\n if (el === ancestorEl) {\n return true;\n }\n el = el.parentNode;\n } while (el);\n }\n return false;\n };\n /**\n * Get the URL path's parent directory.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getDirPathOfUrl = function(url) {\n var dir;\n if (typeof url === \"string\" && url) {\n dir = url.split(\"#\")[0].split(\"?\")[0];\n dir = url.slice(0, url.lastIndexOf(\"/\") + 1);\n }\n return dir;\n };\n /**\n * Get the current script's URL by throwing an `Error` and analyzing it.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrlFromErrorStack = function(stack) {\n var url, matches;\n if (typeof stack === \"string\" && stack) {\n matches = stack.match(/^(?:|[^:@]*@|.+\\)@(?=http[s]?|file)|.+?\\s+(?: at |@)(?:[^:\\(]+ )*[\\(]?)((?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n if (matches && matches[1]) {\n url = matches[1];\n } else {\n matches = stack.match(/\\)@((?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n if (matches && matches[1]) {\n url = matches[1];\n }\n }\n }\n return url;\n };\n /**\n * Get the current script's URL by throwing an `Error` and analyzing it.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrlFromError = function() {\n var url, err;\n try {\n throw new _Error();\n } catch (e) {\n err = e;\n }\n if (err) {\n url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);\n }\n return url;\n };\n /**\n * Get the current script's URL.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getCurrentScriptUrl = function() {\n var jsPath, scripts, i;\n if (_document.currentScript && (jsPath = _document.currentScript.src)) {\n return jsPath;\n }\n scripts = _document.getElementsByTagName(\"script\");\n if (scripts.length === 1) {\n return scripts[0].src || undefined;\n }\n if (\"readyState\" in scripts[0]) {\n for (i = scripts.length; i--; ) {\n if (scripts[i].readyState === \"interactive\" && (jsPath = scripts[i].src)) {\n return jsPath;\n }\n }\n }\n if (_document.readyState === \"loading\" && (jsPath = scripts[scripts.length - 1].src)) {\n return jsPath;\n }\n if (jsPath = _getCurrentScriptUrlFromError()) {\n return jsPath;\n }\n return undefined;\n };\n /**\n * Get the unanimous parent directory of ALL script tags.\n * If any script tags are either (a) inline or (b) from differing parent\n * directories, this method must return `undefined`.\n *\n * @returns String or `undefined`\n * @private\n */\n var _getUnanimousScriptParentDir = function() {\n var i, jsDir, jsPath, scripts = _document.getElementsByTagName(\"script\");\n for (i = scripts.length; i--; ) {\n if (!(jsPath = scripts[i].src)) {\n jsDir = null;\n break;\n }\n jsPath = _getDirPathOfUrl(jsPath);\n if (jsDir == null) {\n jsDir = jsPath;\n } else if (jsDir !== jsPath) {\n jsDir = null;\n break;\n }\n }\n return jsDir || undefined;\n };\n /**\n * Get the presumed location of the \"ZeroClipboard.swf\" file, based on the location\n * of the executing JavaScript file (e.g. \"ZeroClipboard.js\", etc.).\n *\n * @returns String\n * @private\n */\n var _getDefaultSwfPath = function() {\n var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || \"\";\n return jsDir + \"ZeroClipboard.swf\";\n };\n /**\n * Keep track of the state of the Flash object.\n * @private\n */\n var _flashState = {\n bridge: null,\n version: \"0.0.0\",\n pluginType: \"unknown\",\n disabled: null,\n outdated: null,\n unavailable: null,\n deactivated: null,\n overdue: null,\n ready: null\n };\n /**\n * The minimum Flash Player version required to use ZeroClipboard completely.\n * @readonly\n * @private\n */\n var _minimumFlashVersion = \"11.0.0\";\n /**\n * Keep track of all event listener registrations.\n * @private\n */\n var _handlers = {};\n /**\n * Keep track of the currently activated element.\n * @private\n */\n var _currentElement;\n /**\n * Keep track of the element that was activated when a `copy` process started.\n * @private\n */\n var _copyTarget;\n /**\n * Keep track of data for the pending clipboard transaction.\n * @private\n */\n var _clipData = {};\n /**\n * Keep track of data formats for the pending clipboard transaction.\n * @private\n */\n var _clipDataFormatMap = null;\n /**\n * The `message` store for events\n * @private\n */\n var _eventMessages = {\n ready: \"Flash communication is established\",\n error: {\n \"flash-disabled\": \"Flash is disabled or not installed\",\n \"flash-outdated\": \"Flash is too outdated to support ZeroClipboard\",\n \"flash-unavailable\": \"Flash is unable to communicate bidirectionally with JavaScript\",\n \"flash-deactivated\": \"Flash is too outdated for your browser and/or is configured as click-to-activate\",\n \"flash-overdue\": \"Flash communication was established but NOT within the acceptable time limit\"\n }\n };\n /**\n * ZeroClipboard configuration defaults for the Core module.\n * @private\n */\n var _globalConfig = {\n swfPath: _getDefaultSwfPath(),\n trustedDomains: window.location.host ? [ window.location.host ] : [],\n cacheBust: true,\n forceEnhancedClipboard: false,\n flashLoadTimeout: 3e4,\n autoActivate: true,\n bubbleEvents: true,\n containerId: \"global-zeroclipboard-html-bridge\",\n containerClass: \"global-zeroclipboard-container\",\n swfObjectId: \"global-zeroclipboard-flash-bridge\",\n hoverClass: \"zeroclipboard-is-hover\",\n activeClass: \"zeroclipboard-is-active\",\n forceHandCursor: false,\n title: null,\n zIndex: 999999999\n };\n /**\n * The underlying implementation of `ZeroClipboard.config`.\n * @private\n */\n var _config = function(options) {\n if (typeof options === \"object\" && options !== null) {\n for (var prop in options) {\n if (_hasOwn.call(options, prop)) {\n if (/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(prop)) {\n _globalConfig[prop] = options[prop];\n } else if (_flashState.bridge == null) {\n if (prop === \"containerId\" || prop === \"swfObjectId\") {\n if (_isValidHtml4Id(options[prop])) {\n _globalConfig[prop] = options[prop];\n } else {\n throw new Error(\"The specified `\" + prop + \"` value is not valid as an HTML4 Element ID\");\n }\n } else {\n _globalConfig[prop] = options[prop];\n }\n }\n }\n }\n }\n if (typeof options === \"string\" && options) {\n if (_hasOwn.call(_globalConfig, options)) {\n return _globalConfig[options];\n }\n return;\n }\n return _deepCopy(_globalConfig);\n };\n /**\n * The underlying implementation of `ZeroClipboard.state`.\n * @private\n */\n var _state = function() {\n return {\n browser: _pick(_navigator, [ \"userAgent\", \"platform\", \"appName\" ]),\n flash: _omit(_flashState, [ \"bridge\" ]),\n zeroclipboard: {\n version: ZeroClipboard.version,\n config: ZeroClipboard.config()\n }\n };\n };\n /**\n * The underlying implementation of `ZeroClipboard.isFlashUnusable`.\n * @private\n */\n var _isFlashUnusable = function() {\n return !!(_flashState.disabled || _flashState.outdated || _flashState.unavailable || _flashState.deactivated);\n };\n /**\n * The underlying implementation of `ZeroClipboard.on`.\n * @private\n */\n var _on = function(eventType, listener) {\n var i, len, events, added = {};\n if (typeof eventType === \"string\" && eventType) {\n events = eventType.toLowerCase().split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n ZeroClipboard.on(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].replace(/^on/, \"\");\n added[eventType] = true;\n if (!_handlers[eventType]) {\n _handlers[eventType] = [];\n }\n _handlers[eventType].push(listener);\n }\n if (added.ready && _flashState.ready) {\n ZeroClipboard.emit({\n type: \"ready\"\n });\n }\n if (added.error) {\n var errorTypes = [ \"disabled\", \"outdated\", \"unavailable\", \"deactivated\", \"overdue\" ];\n for (i = 0, len = errorTypes.length; i < len; i++) {\n if (_flashState[errorTypes[i]] === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-\" + errorTypes[i]\n });\n break;\n }\n }\n }\n }\n return ZeroClipboard;\n };\n /**\n * The underlying implementation of `ZeroClipboard.off`.\n * @private\n */\n var _off = function(eventType, listener) {\n var i, len, foundIndex, events, perEventHandlers;\n if (arguments.length === 0) {\n events = _keys(_handlers);\n } else if (typeof eventType === \"string\" && eventType) {\n events = eventType.split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n ZeroClipboard.off(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].toLowerCase().replace(/^on/, \"\");\n perEventHandlers = _handlers[eventType];\n if (perEventHandlers && perEventHandlers.length) {\n if (listener) {\n foundIndex = perEventHandlers.indexOf(listener);\n while (foundIndex !== -1) {\n perEventHandlers.splice(foundIndex, 1);\n foundIndex = perEventHandlers.indexOf(listener, foundIndex);\n }\n } else {\n perEventHandlers.length = 0;\n }\n }\n }\n }\n return ZeroClipboard;\n };\n /**\n * The underlying implementation of `ZeroClipboard.handlers`.\n * @private\n */\n var _listeners = function(eventType) {\n var copy;\n if (typeof eventType === \"string\" && eventType) {\n copy = _deepCopy(_handlers[eventType]) || null;\n } else {\n copy = _deepCopy(_handlers);\n }\n return copy;\n };\n /**\n * The underlying implementation of `ZeroClipboard.emit`.\n * @private\n */\n var _emit = function(event) {\n var eventCopy, returnVal, tmp;\n event = _createEvent(event);\n if (!event) {\n return;\n }\n if (_preprocessEvent(event)) {\n return;\n }\n if (event.type === \"ready\" && _flashState.overdue === true) {\n return ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-overdue\"\n });\n }\n eventCopy = _extend({}, event);\n _dispatchCallbacks.call(this, eventCopy);\n if (event.type === \"copy\") {\n tmp = _mapClipDataToFlash(_clipData);\n returnVal = tmp.data;\n _clipDataFormatMap = tmp.formatMap;\n }\n return returnVal;\n };\n /**\n * The underlying implementation of `ZeroClipboard.create`.\n * @private\n */\n var _create = function() {\n if (typeof _flashState.ready !== \"boolean\") {\n _flashState.ready = false;\n }\n if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {\n var maxWait = _globalConfig.flashLoadTimeout;\n if (typeof maxWait === \"number\" && maxWait >= 0) {\n _setTimeout(function() {\n if (typeof _flashState.deactivated !== \"boolean\") {\n _flashState.deactivated = true;\n }\n if (_flashState.deactivated === true) {\n ZeroClipboard.emit({\n type: \"error\",\n name: \"flash-deactivated\"\n });\n }\n }, maxWait);\n }\n _flashState.overdue = false;\n _embedSwf();\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.destroy`.\n * @private\n */\n var _destroy = function() {\n ZeroClipboard.clearData();\n ZeroClipboard.blur();\n ZeroClipboard.emit(\"destroy\");\n _unembedSwf();\n ZeroClipboard.off();\n };\n /**\n * The underlying implementation of `ZeroClipboard.setData`.\n * @private\n */\n var _setData = function(format, data) {\n var dataObj;\n if (typeof format === \"object\" && format && typeof data === \"undefined\") {\n dataObj = format;\n ZeroClipboard.clearData();\n } else if (typeof format === \"string\" && format) {\n dataObj = {};\n dataObj[format] = data;\n } else {\n return;\n }\n for (var dataFormat in dataObj) {\n if (typeof dataFormat === \"string\" && dataFormat && _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === \"string\" && dataObj[dataFormat]) {\n _clipData[dataFormat] = dataObj[dataFormat];\n }\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.clearData`.\n * @private\n */\n var _clearData = function(format) {\n if (typeof format === \"undefined\") {\n _deleteOwnProperties(_clipData);\n _clipDataFormatMap = null;\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n delete _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.getData`.\n * @private\n */\n var _getData = function(format) {\n if (typeof format === \"undefined\") {\n return _deepCopy(_clipData);\n } else if (typeof format === \"string\" && _hasOwn.call(_clipData, format)) {\n return _clipData[format];\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.\n * @private\n */\n var _focus = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.activeClass);\n if (_currentElement !== element) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n }\n }\n _currentElement = element;\n _addClass(element, _globalConfig.hoverClass);\n var newTitle = element.getAttribute(\"title\") || _globalConfig.title;\n if (typeof newTitle === \"string\" && newTitle) {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.setAttribute(\"title\", newTitle);\n }\n }\n var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, \"cursor\") === \"pointer\";\n _setHandCursor(useHandCursor);\n _reposition();\n };\n /**\n * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.\n * @private\n */\n var _blur = function() {\n var htmlBridge = _getHtmlBridge(_flashState.bridge);\n if (htmlBridge) {\n htmlBridge.removeAttribute(\"title\");\n htmlBridge.style.left = \"0px\";\n htmlBridge.style.top = \"-9999px\";\n htmlBridge.style.width = \"1px\";\n htmlBridge.style.top = \"1px\";\n }\n if (_currentElement) {\n _removeClass(_currentElement, _globalConfig.hoverClass);\n _removeClass(_currentElement, _globalConfig.activeClass);\n _currentElement = null;\n }\n };\n /**\n * The underlying implementation of `ZeroClipboard.activeElement`.\n * @private\n */\n var _activeElement = function() {\n return _currentElement || null;\n };\n /**\n * Check if a value is a valid HTML4 `ID` or `Name` token.\n * @private\n */\n var _isValidHtml4Id = function(id) {\n return typeof id === \"string\" && id && /^[A-Za-z][A-Za-z0-9_:\\-\\.]*$/.test(id);\n };\n /**\n * Create or update an `event` object, based on the `eventType`.\n * @private\n */\n var _createEvent = function(event) {\n var eventType;\n if (typeof event === \"string\" && event) {\n eventType = event;\n event = {};\n } else if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n eventType = event.type;\n }\n if (!eventType) {\n return;\n }\n if (!event.target && /^(copy|aftercopy|_click)$/.test(eventType.toLowerCase())) {\n event.target = _copyTarget;\n }\n _extend(event, {\n type: eventType.toLowerCase(),\n target: event.target || _currentElement || null,\n relatedTarget: event.relatedTarget || null,\n currentTarget: _flashState && _flashState.bridge || null,\n timeStamp: event.timeStamp || _now() || null\n });\n var msg = _eventMessages[event.type];\n if (event.type === \"error\" && event.name && msg) {\n msg = msg[event.name];\n }\n if (msg) {\n event.message = msg;\n }\n if (event.type === \"ready\") {\n _extend(event, {\n target: null,\n version: _flashState.version\n });\n }\n if (event.type === \"error\") {\n if (/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(event.name)) {\n _extend(event, {\n target: null,\n minimumVersion: _minimumFlashVersion\n });\n }\n if (/^flash-(outdated|unavailable|deactivated|overdue)$/.test(event.name)) {\n _extend(event, {\n version: _flashState.version\n });\n }\n }\n if (event.type === \"copy\") {\n event.clipboardData = {\n setData: ZeroClipboard.setData,\n clearData: ZeroClipboard.clearData\n };\n }\n if (event.type === \"aftercopy\") {\n event = _mapClipResultsFromFlash(event, _clipDataFormatMap);\n }\n if (event.target && !event.relatedTarget) {\n event.relatedTarget = _getRelatedTarget(event.target);\n }\n event = _addMouseData(event);\n return event;\n };\n /**\n * Get a relatedTarget from the target's `data-clipboard-target` attribute\n * @private\n */\n var _getRelatedTarget = function(targetEl) {\n var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute(\"data-clipboard-target\");\n return relatedTargetId ? _document.getElementById(relatedTargetId) : null;\n };\n /**\n * Add element and position data to `MouseEvent` instances\n * @private\n */\n var _addMouseData = function(event) {\n if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n var srcElement = event.target;\n var fromElement = event.type === \"_mouseover\" && event.relatedTarget ? event.relatedTarget : undefined;\n var toElement = event.type === \"_mouseout\" && event.relatedTarget ? event.relatedTarget : undefined;\n var pos = _getDOMObjectPosition(srcElement);\n var screenLeft = _window.screenLeft || _window.screenX || 0;\n var screenTop = _window.screenTop || _window.screenY || 0;\n var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;\n var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;\n var pageX = pos.left + (typeof event._stageX === \"number\" ? event._stageX : 0);\n var pageY = pos.top + (typeof event._stageY === \"number\" ? event._stageY : 0);\n var clientX = pageX - scrollLeft;\n var clientY = pageY - scrollTop;\n var screenX = screenLeft + clientX;\n var screenY = screenTop + clientY;\n var moveX = typeof event.movementX === \"number\" ? event.movementX : 0;\n var moveY = typeof event.movementY === \"number\" ? event.movementY : 0;\n delete event._stageX;\n delete event._stageY;\n _extend(event, {\n srcElement: srcElement,\n fromElement: fromElement,\n toElement: toElement,\n screenX: screenX,\n screenY: screenY,\n pageX: pageX,\n pageY: pageY,\n clientX: clientX,\n clientY: clientY,\n x: clientX,\n y: clientY,\n movementX: moveX,\n movementY: moveY,\n offsetX: 0,\n offsetY: 0,\n layerX: 0,\n layerY: 0\n });\n }\n return event;\n };\n /**\n * Determine if an event's registered handlers should be execute synchronously or asynchronously.\n *\n * @returns {boolean}\n * @private\n */\n var _shouldPerformAsync = function(event) {\n var eventType = event && typeof event.type === \"string\" && event.type || \"\";\n return !/^(?:(?:before)?copy|destroy)$/.test(eventType);\n };\n /**\n * Control if a callback should be executed asynchronously or not.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallback = function(func, context, args, async) {\n if (async) {\n _setTimeout(function() {\n func.apply(context, args);\n }, 0);\n } else {\n func.apply(context, args);\n }\n };\n /**\n * Handle the actual dispatching of events to client instances.\n *\n * @returns `undefined`\n * @private\n */\n var _dispatchCallbacks = function(event) {\n if (!(typeof event === \"object\" && event && event.type)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = _handlers[\"*\"] || [];\n var specificTypeHandlers = _handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n return this;\n };\n /**\n * Preprocess any special behaviors, reactions, or state changes after receiving this event.\n * Executes only once per event emitted, NOT once per client.\n * @private\n */\n var _preprocessEvent = function(event) {\n var element = event.target || _currentElement || null;\n var sourceIsSwf = event._source === \"swf\";\n delete event._source;\n var flashErrorNames = [ \"flash-disabled\", \"flash-outdated\", \"flash-unavailable\", \"flash-deactivated\", \"flash-overdue\" ];\n switch (event.type) {\n case \"error\":\n if (flashErrorNames.indexOf(event.name) !== -1) {\n _extend(_flashState, {\n disabled: event.name === \"flash-disabled\",\n outdated: event.name === \"flash-outdated\",\n unavailable: event.name === \"flash-unavailable\",\n deactivated: event.name === \"flash-deactivated\",\n overdue: event.name === \"flash-overdue\",\n ready: false\n });\n }\n break;\n\n case \"ready\":\n var wasDeactivated = _flashState.deactivated === true;\n _extend(_flashState, {\n disabled: false,\n outdated: false,\n unavailable: false,\n deactivated: false,\n overdue: wasDeactivated,\n ready: !wasDeactivated\n });\n break;\n\n case \"beforecopy\":\n _copyTarget = element;\n break;\n\n case \"copy\":\n var textContent, htmlContent, targetEl = event.relatedTarget;\n if (!(_clipData[\"text/html\"] || _clipData[\"text/plain\"]) && targetEl && (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) && (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n if (htmlContent !== textContent) {\n event.clipboardData.setData(\"text/html\", htmlContent);\n }\n } else if (!_clipData[\"text/plain\"] && event.target && (textContent = event.target.getAttribute(\"data-clipboard-text\"))) {\n event.clipboardData.clearData();\n event.clipboardData.setData(\"text/plain\", textContent);\n }\n break;\n\n case \"aftercopy\":\n ZeroClipboard.clearData();\n if (element && element !== _safeActiveElement() && element.focus) {\n element.focus();\n }\n break;\n\n case \"_mouseover\":\n ZeroClipboard.focus(element);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseenter\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseover\"\n }));\n }\n break;\n\n case \"_mouseout\":\n ZeroClipboard.blur();\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n if (element && element !== event.relatedTarget && !_containedBy(event.relatedTarget, element)) {\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseleave\",\n bubbles: false,\n cancelable: false\n }));\n }\n _fireMouseEvent(_extend({}, event, {\n type: \"mouseout\"\n }));\n }\n break;\n\n case \"_mousedown\":\n _addClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mouseup\":\n _removeClass(element, _globalConfig.activeClass);\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_click\":\n _copyTarget = null;\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n\n case \"_mousemove\":\n if (_globalConfig.bubbleEvents === true && sourceIsSwf) {\n _fireMouseEvent(_extend({}, event, {\n type: event.type.slice(1)\n }));\n }\n break;\n }\n if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {\n return true;\n }\n };\n /**\n * Dispatch a synthetic MouseEvent.\n *\n * @returns `undefined`\n * @private\n */\n var _fireMouseEvent = function(event) {\n if (!(event && typeof event.type === \"string\" && event)) {\n return;\n }\n var e, target = event.target || null, doc = target && target.ownerDocument || _document, defaults = {\n view: doc.defaultView || _window,\n canBubble: true,\n cancelable: true,\n detail: event.type === \"click\" ? 1 : 0,\n button: typeof event.which === \"number\" ? event.which - 1 : typeof event.button === \"number\" ? event.button : doc.createEvent ? 0 : 1\n }, args = _extend(defaults, event);\n if (!target) {\n return;\n }\n if (doc.createEvent && target.dispatchEvent) {\n 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 ];\n e = doc.createEvent(\"MouseEvents\");\n if (e.initMouseEvent) {\n e.initMouseEvent.apply(e, args);\n e._source = \"js\";\n target.dispatchEvent(e);\n }\n }\n };\n /**\n * Create the HTML bridge element to embed the Flash object into.\n * @private\n */\n var _createHtmlBridge = function() {\n var container = _document.createElement(\"div\");\n container.id = _globalConfig.containerId;\n container.className = _globalConfig.containerClass;\n container.style.position = \"absolute\";\n container.style.left = \"0px\";\n container.style.top = \"-9999px\";\n container.style.width = \"1px\";\n container.style.height = \"1px\";\n container.style.zIndex = \"\" + _getSafeZIndex(_globalConfig.zIndex);\n return container;\n };\n /**\n * Get the HTML element container that wraps the Flash bridge object/element.\n * @private\n */\n var _getHtmlBridge = function(flashBridge) {\n var htmlBridge = flashBridge && flashBridge.parentNode;\n while (htmlBridge && htmlBridge.nodeName === \"OBJECT\" && htmlBridge.parentNode) {\n htmlBridge = htmlBridge.parentNode;\n }\n return htmlBridge || null;\n };\n /**\n * Create the SWF object.\n *\n * @returns The SWF object reference.\n * @private\n */\n var _embedSwf = function() {\n var len, flashBridge = _flashState.bridge, container = _getHtmlBridge(flashBridge);\n if (!flashBridge) {\n var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);\n var allowNetworking = allowScriptAccess === \"never\" ? \"none\" : \"all\";\n var flashvars = _vars(_globalConfig);\n var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);\n container = _createHtmlBridge();\n var divToBeReplaced = _document.createElement(\"div\");\n container.appendChild(divToBeReplaced);\n _document.body.appendChild(container);\n var tmpDiv = _document.createElement(\"div\");\n var oldIE = _flashState.pluginType === \"activex\";\n tmpDiv.innerHTML = '\";\n flashBridge = tmpDiv.firstChild;\n tmpDiv = null;\n _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;\n container.replaceChild(flashBridge, divToBeReplaced);\n }\n if (!flashBridge) {\n flashBridge = _document[_globalConfig.swfObjectId];\n if (flashBridge && (len = flashBridge.length)) {\n flashBridge = flashBridge[len - 1];\n }\n if (!flashBridge && container) {\n flashBridge = container.firstChild;\n }\n }\n _flashState.bridge = flashBridge || null;\n return flashBridge;\n };\n /**\n * Destroy the SWF object.\n * @private\n */\n var _unembedSwf = function() {\n var flashBridge = _flashState.bridge;\n if (flashBridge) {\n var htmlBridge = _getHtmlBridge(flashBridge);\n if (htmlBridge) {\n if (_flashState.pluginType === \"activex\" && \"readyState\" in flashBridge) {\n flashBridge.style.display = \"none\";\n (function removeSwfFromIE() {\n if (flashBridge.readyState === 4) {\n for (var prop in flashBridge) {\n if (typeof flashBridge[prop] === \"function\") {\n flashBridge[prop] = null;\n }\n }\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n } else {\n _setTimeout(removeSwfFromIE, 10);\n }\n })();\n } else {\n if (flashBridge.parentNode) {\n flashBridge.parentNode.removeChild(flashBridge);\n }\n if (htmlBridge.parentNode) {\n htmlBridge.parentNode.removeChild(htmlBridge);\n }\n }\n }\n _flashState.ready = null;\n _flashState.bridge = null;\n _flashState.deactivated = null;\n }\n };\n /**\n * Map the data format names of the \"clipData\" to Flash-friendly names.\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipDataToFlash = function(clipData) {\n var newClipData = {}, formatMap = {};\n if (!(typeof clipData === \"object\" && clipData)) {\n return;\n }\n for (var dataFormat in clipData) {\n if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === \"string\" && clipData[dataFormat]) {\n switch (dataFormat.toLowerCase()) {\n case \"text/plain\":\n case \"text\":\n case \"air:text\":\n case \"flash:text\":\n newClipData.text = clipData[dataFormat];\n formatMap.text = dataFormat;\n break;\n\n case \"text/html\":\n case \"html\":\n case \"air:html\":\n case \"flash:html\":\n newClipData.html = clipData[dataFormat];\n formatMap.html = dataFormat;\n break;\n\n case \"application/rtf\":\n case \"text/rtf\":\n case \"rtf\":\n case \"richtext\":\n case \"air:rtf\":\n case \"flash:rtf\":\n newClipData.rtf = clipData[dataFormat];\n formatMap.rtf = dataFormat;\n break;\n\n default:\n break;\n }\n }\n }\n return {\n data: newClipData,\n formatMap: formatMap\n };\n };\n /**\n * Map the data format names from Flash-friendly names back to their original \"clipData\" names (via a format mapping).\n *\n * @returns A new transformed object.\n * @private\n */\n var _mapClipResultsFromFlash = function(clipResults, formatMap) {\n if (!(typeof clipResults === \"object\" && clipResults && typeof formatMap === \"object\" && formatMap)) {\n return clipResults;\n }\n var newResults = {};\n for (var prop in clipResults) {\n if (_hasOwn.call(clipResults, prop)) {\n if (prop !== \"success\" && prop !== \"data\") {\n newResults[prop] = clipResults[prop];\n continue;\n }\n newResults[prop] = {};\n var tmpHash = clipResults[prop];\n for (var dataFormat in tmpHash) {\n if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {\n newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];\n }\n }\n }\n }\n return newResults;\n };\n /**\n * Will look at a path, and will create a \"?noCache={time}\" or \"&noCache={time}\"\n * query param string to return. Does NOT append that string to the original path.\n * This is useful because ExternalInterface often breaks when a Flash SWF is cached.\n *\n * @returns The `noCache` query param with necessary \"?\"/\"&\" prefix.\n * @private\n */\n var _cacheBust = function(path, options) {\n var cacheBust = options == null || options && options.cacheBust === true;\n if (cacheBust) {\n return (path.indexOf(\"?\") === -1 ? \"?\" : \"&\") + \"noCache=\" + _now();\n } else {\n return \"\";\n }\n };\n /**\n * Creates a query string for the FlashVars param.\n * Does NOT include the cache-busting query param.\n *\n * @returns FlashVars query string\n * @private\n */\n var _vars = function(options) {\n var i, len, domain, domains, str = \"\", trustedOriginsExpanded = [];\n if (options.trustedDomains) {\n if (typeof options.trustedDomains === \"string\") {\n domains = [ options.trustedDomains ];\n } else if (typeof options.trustedDomains === \"object\" && \"length\" in options.trustedDomains) {\n domains = options.trustedDomains;\n }\n }\n if (domains && domains.length) {\n for (i = 0, len = domains.length; i < len; i++) {\n if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === \"string\") {\n domain = _extractDomain(domains[i]);\n if (!domain) {\n continue;\n }\n if (domain === \"*\") {\n trustedOriginsExpanded.length = 0;\n trustedOriginsExpanded.push(domain);\n break;\n }\n trustedOriginsExpanded.push.apply(trustedOriginsExpanded, [ domain, \"//\" + domain, _window.location.protocol + \"//\" + domain ]);\n }\n }\n }\n if (trustedOriginsExpanded.length) {\n str += \"trustedOrigins=\" + _encodeURIComponent(trustedOriginsExpanded.join(\",\"));\n }\n if (options.forceEnhancedClipboard === true) {\n str += (str ? \"&\" : \"\") + \"forceEnhancedClipboard=true\";\n }\n if (typeof options.swfObjectId === \"string\" && options.swfObjectId) {\n str += (str ? \"&\" : \"\") + \"swfObjectId=\" + _encodeURIComponent(options.swfObjectId);\n }\n return str;\n };\n /**\n * Extract the domain (e.g. \"github.com\") from an origin (e.g. \"https://github.com\") or\n * URL (e.g. \"https://github.com/zeroclipboard/zeroclipboard/\").\n *\n * @returns the domain\n * @private\n */\n var _extractDomain = function(originOrUrl) {\n if (originOrUrl == null || originOrUrl === \"\") {\n return null;\n }\n originOrUrl = originOrUrl.replace(/^\\s+|\\s+$/g, \"\");\n if (originOrUrl === \"\") {\n return null;\n }\n var protocolIndex = originOrUrl.indexOf(\"//\");\n originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);\n var pathIndex = originOrUrl.indexOf(\"/\");\n originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);\n if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === \".swf\") {\n return null;\n }\n return originOrUrl || null;\n };\n /**\n * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.\n *\n * @returns The appropriate script access level.\n * @private\n */\n var _determineScriptAccess = function() {\n var _extractAllDomains = function(origins) {\n var i, len, tmp, resultsArray = [];\n if (typeof origins === \"string\") {\n origins = [ origins ];\n }\n if (!(typeof origins === \"object\" && origins && typeof origins.length === \"number\")) {\n return resultsArray;\n }\n for (i = 0, len = origins.length; i < len; i++) {\n if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {\n if (tmp === \"*\") {\n resultsArray.length = 0;\n resultsArray.push(\"*\");\n break;\n }\n if (resultsArray.indexOf(tmp) === -1) {\n resultsArray.push(tmp);\n }\n }\n }\n return resultsArray;\n };\n return function(currentDomain, configOptions) {\n var swfDomain = _extractDomain(configOptions.swfPath);\n if (swfDomain === null) {\n swfDomain = currentDomain;\n }\n var trustedDomains = _extractAllDomains(configOptions.trustedDomains);\n var len = trustedDomains.length;\n if (len > 0) {\n if (len === 1 && trustedDomains[0] === \"*\") {\n return \"always\";\n }\n if (trustedDomains.indexOf(currentDomain) !== -1) {\n if (len === 1 && currentDomain === swfDomain) {\n return \"sameDomain\";\n }\n return \"always\";\n }\n }\n return \"never\";\n };\n }();\n /**\n * Get the currently active/focused DOM element.\n *\n * @returns the currently active/focused element, or `null`\n * @private\n */\n var _safeActiveElement = function() {\n try {\n return _document.activeElement;\n } catch (err) {\n return null;\n }\n };\n /**\n * Add a class to an element, if it doesn't already have it.\n *\n * @returns The element, with its new class added.\n * @private\n */\n var _addClass = function(element, value) {\n if (!element || element.nodeType !== 1) {\n return element;\n }\n if (element.classList) {\n if (!element.classList.contains(value)) {\n element.classList.add(value);\n }\n return element;\n }\n if (value && typeof value === \"string\") {\n var classNames = (value || \"\").split(/\\s+/);\n if (element.nodeType === 1) {\n if (!element.className) {\n element.className = value;\n } else {\n var className = \" \" + element.className + \" \", setClass = element.className;\n for (var c = 0, cl = classNames.length; c < cl; c++) {\n if (className.indexOf(\" \" + classNames[c] + \" \") < 0) {\n setClass += \" \" + classNames[c];\n }\n }\n element.className = setClass.replace(/^\\s+|\\s+$/g, \"\");\n }\n }\n }\n return element;\n };\n /**\n * Remove a class from an element, if it has it.\n *\n * @returns The element, with its class removed.\n * @private\n */\n var _removeClass = function(element, value) {\n if (!element || element.nodeType !== 1) {\n return element;\n }\n if (element.classList) {\n if (element.classList.contains(value)) {\n element.classList.remove(value);\n }\n return element;\n }\n if (typeof value === \"string\" && value) {\n var classNames = value.split(/\\s+/);\n if (element.nodeType === 1 && element.className) {\n var className = (\" \" + element.className + \" \").replace(/[\\n\\t]/g, \" \");\n for (var c = 0, cl = classNames.length; c < cl; c++) {\n className = className.replace(\" \" + classNames[c] + \" \", \" \");\n }\n element.className = className.replace(/^\\s+|\\s+$/g, \"\");\n }\n }\n return element;\n };\n /**\n * Attempt to interpret the element's CSS styling. If `prop` is `\"cursor\"`,\n * then we assume that it should be a hand (\"pointer\") cursor if the element\n * is an anchor element (\"a\" tag).\n *\n * @returns The computed style property.\n * @private\n */\n var _getStyle = function(el, prop) {\n var value = _window.getComputedStyle(el, null).getPropertyValue(prop);\n if (prop === \"cursor\") {\n if (!value || value === \"auto\") {\n if (el.nodeName === \"A\") {\n return \"pointer\";\n }\n }\n }\n return value;\n };\n /**\n * Get the zoom factor of the browser. Always returns `1.0`, except at\n * non-default zoom levels in IE<8 and some older versions of WebKit.\n *\n * @returns Floating unit percentage of the zoom factor (e.g. 150% = `1.5`).\n * @private\n */\n var _getZoomFactor = function() {\n var rect, physicalWidth, logicalWidth, zoomFactor = 1;\n if (typeof _document.body.getBoundingClientRect === \"function\") {\n rect = _document.body.getBoundingClientRect();\n physicalWidth = rect.right - rect.left;\n logicalWidth = _document.body.offsetWidth;\n zoomFactor = _round(physicalWidth / logicalWidth * 100) / 100;\n }\n return zoomFactor;\n };\n /**\n * Get the DOM positioning info of an element.\n *\n * @returns Object containing the element's position, width, and height.\n * @private\n */\n var _getDOMObjectPosition = function(obj) {\n var info = {\n left: 0,\n top: 0,\n width: 0,\n height: 0\n };\n if (obj.getBoundingClientRect) {\n var rect = obj.getBoundingClientRect();\n var pageXOffset, pageYOffset, zoomFactor;\n if (\"pageXOffset\" in _window && \"pageYOffset\" in _window) {\n pageXOffset = _window.pageXOffset;\n pageYOffset = _window.pageYOffset;\n } else {\n zoomFactor = _getZoomFactor();\n pageXOffset = _round(_document.documentElement.scrollLeft / zoomFactor);\n pageYOffset = _round(_document.documentElement.scrollTop / zoomFactor);\n }\n var leftBorderWidth = _document.documentElement.clientLeft || 0;\n var topBorderWidth = _document.documentElement.clientTop || 0;\n info.left = rect.left + pageXOffset - leftBorderWidth;\n info.top = rect.top + pageYOffset - topBorderWidth;\n info.width = \"width\" in rect ? rect.width : rect.right - rect.left;\n info.height = \"height\" in rect ? rect.height : rect.bottom - rect.top;\n }\n return info;\n };\n /**\n * Reposition the Flash object to cover the currently activated element.\n *\n * @returns `undefined`\n * @private\n */\n var _reposition = function() {\n var htmlBridge;\n if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {\n var pos = _getDOMObjectPosition(_currentElement);\n _extend(htmlBridge.style, {\n width: pos.width + \"px\",\n height: pos.height + \"px\",\n top: pos.top + \"px\",\n left: pos.left + \"px\",\n zIndex: \"\" + _getSafeZIndex(_globalConfig.zIndex)\n });\n }\n };\n /**\n * Sends a signal to the Flash object to display the hand cursor if `true`.\n *\n * @returns `undefined`\n * @private\n */\n var _setHandCursor = function(enabled) {\n if (_flashState.ready === true) {\n if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === \"function\") {\n _flashState.bridge.setHandCursor(enabled);\n } else {\n _flashState.ready = false;\n }\n }\n };\n /**\n * Get a safe value for `zIndex`\n *\n * @returns an integer, or \"auto\"\n * @private\n */\n var _getSafeZIndex = function(val) {\n if (/^(?:auto|inherit)$/.test(val)) {\n return val;\n }\n var zIndex;\n if (typeof val === \"number\" && !_isNaN(val)) {\n zIndex = val;\n } else if (typeof val === \"string\") {\n zIndex = _getSafeZIndex(_parseInt(val, 10));\n }\n return typeof zIndex === \"number\" ? zIndex : \"auto\";\n };\n /**\n * Detect the Flash Player status, version, and plugin type.\n *\n * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}\n * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}\n *\n * @returns `undefined`\n * @private\n */\n var _detectFlashSupport = function(ActiveXObject) {\n var plugin, ax, mimeType, hasFlash = false, isActiveX = false, isPPAPI = false, flashVersion = \"\";\n /**\n * Derived from Apple's suggested sniffer.\n * @param {String} desc e.g. \"Shockwave Flash 7.0 r61\"\n * @returns {String} \"7.0.61\"\n * @private\n */\n function parseFlashVersion(desc) {\n var matches = desc.match(/[\\d]+/g);\n matches.length = 3;\n return matches.join(\".\");\n }\n function isPepperFlash(flashPlayerFileName) {\n return !!flashPlayerFileName && (flashPlayerFileName = flashPlayerFileName.toLowerCase()) && (/^(pepflashplayer\\.dll|libpepflashplayer\\.so|pepperflashplayer\\.plugin)$/.test(flashPlayerFileName) || flashPlayerFileName.slice(-13) === \"chrome.plugin\");\n }\n function inspectPlugin(plugin) {\n if (plugin) {\n hasFlash = true;\n if (plugin.version) {\n flashVersion = parseFlashVersion(plugin.version);\n }\n if (!flashVersion && plugin.description) {\n flashVersion = parseFlashVersion(plugin.description);\n }\n if (plugin.filename) {\n isPPAPI = isPepperFlash(plugin.filename);\n }\n }\n }\n if (_navigator.plugins && _navigator.plugins.length) {\n plugin = _navigator.plugins[\"Shockwave Flash\"];\n inspectPlugin(plugin);\n if (_navigator.plugins[\"Shockwave Flash 2.0\"]) {\n hasFlash = true;\n flashVersion = \"2.0.0.11\";\n }\n } else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {\n mimeType = _navigator.mimeTypes[\"application/x-shockwave-flash\"];\n plugin = mimeType && mimeType.enabledPlugin;\n inspectPlugin(plugin);\n } else if (typeof ActiveXObject !== \"undefined\") {\n isActiveX = true;\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.7\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e1) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash.6\");\n hasFlash = true;\n flashVersion = \"6.0.21\";\n } catch (e2) {\n try {\n ax = new ActiveXObject(\"ShockwaveFlash.ShockwaveFlash\");\n hasFlash = true;\n flashVersion = parseFlashVersion(ax.GetVariable(\"$version\"));\n } catch (e3) {\n isActiveX = false;\n }\n }\n }\n }\n _flashState.disabled = hasFlash !== true;\n _flashState.outdated = flashVersion && _parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion);\n _flashState.version = flashVersion || \"0.0.0\";\n _flashState.pluginType = isPPAPI ? \"pepper\" : isActiveX ? \"activex\" : hasFlash ? \"netscape\" : \"unknown\";\n };\n /**\n * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.\n */\n _detectFlashSupport(_ActiveXObject);\n /**\n * A shell constructor for `ZeroClipboard` client instances.\n *\n * @constructor\n */\n var ZeroClipboard = function() {\n if (!(this instanceof ZeroClipboard)) {\n return new ZeroClipboard();\n }\n if (typeof ZeroClipboard._createClient === \"function\") {\n ZeroClipboard._createClient.apply(this, _args(arguments));\n }\n };\n /**\n * The ZeroClipboard library's version number.\n *\n * @static\n * @readonly\n * @property {string}\n */\n _defineProperty(ZeroClipboard, \"version\", {\n value: \"2.1.6\",\n writable: false,\n configurable: true,\n enumerable: true\n });\n /**\n * Update or get a copy of the ZeroClipboard global configuration.\n * Returns a copy of the current/updated configuration.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.config = function() {\n return _config.apply(this, _args(arguments));\n };\n /**\n * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.\n *\n * @returns Object\n * @static\n */\n ZeroClipboard.state = function() {\n return _state.apply(this, _args(arguments));\n };\n /**\n * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.\n *\n * @returns Boolean\n * @static\n */\n ZeroClipboard.isFlashUnusable = function() {\n return _isFlashUnusable.apply(this, _args(arguments));\n };\n /**\n * Register an event listener.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.on = function() {\n return _on.apply(this, _args(arguments));\n };\n /**\n * Unregister an event listener.\n * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all listeners for every event type.\n *\n * @returns `ZeroClipboard`\n * @static\n */\n ZeroClipboard.off = function() {\n return _off.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType`.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.handlers = function() {\n return _listeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n * @static\n */\n ZeroClipboard.emit = function() {\n return _emit.apply(this, _args(arguments));\n };\n /**\n * Create and embed the Flash object.\n *\n * @returns The Flash object\n * @static\n */\n ZeroClipboard.create = function() {\n return _create.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything, including the embedded Flash object.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.destroy = function() {\n return _destroy.apply(this, _args(arguments));\n };\n /**\n * Set the pending data for clipboard injection.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.setData = function() {\n return _setData.apply(this, _args(arguments));\n };\n /**\n * Clear the pending data for clipboard injection.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.clearData = function() {\n return _clearData.apply(this, _args(arguments));\n };\n /**\n * Get a copy of the pending data for clipboard injection.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n * @static\n */\n ZeroClipboard.getData = function() {\n return _getData.apply(this, _args(arguments));\n };\n /**\n * Sets the current HTML object that the Flash object should overlay. This will put the global\n * Flash object on top of the current element; depending on the setup, this may also set the\n * pending clipboard text data as well as the Flash object's wrapping element's title attribute\n * based on the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.focus = ZeroClipboard.activate = function() {\n return _focus.apply(this, _args(arguments));\n };\n /**\n * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on\n * the setup, this may also unset the Flash object's wrapping element's title attribute based on\n * the underlying HTML element and ZeroClipboard configuration.\n *\n * @returns `undefined`\n * @static\n */\n ZeroClipboard.blur = ZeroClipboard.deactivate = function() {\n return _blur.apply(this, _args(arguments));\n };\n /**\n * Returns the currently focused/\"activated\" HTML element that the Flash object is wrapping.\n *\n * @returns `HTMLElement` or `null`\n * @static\n */\n ZeroClipboard.activeElement = function() {\n return _activeElement.apply(this, _args(arguments));\n };\n /**\n * Keep track of the ZeroClipboard client instance counter.\n */\n var _clientIdCounter = 0;\n /**\n * Keep track of the state of the client instances.\n *\n * Entry structure:\n * _clientMeta[client.id] = {\n * instance: client,\n * elements: [],\n * handlers: {}\n * };\n */\n var _clientMeta = {};\n /**\n * Keep track of the ZeroClipboard clipped elements counter.\n */\n var _elementIdCounter = 0;\n /**\n * Keep track of the state of the clipped element relationships to clients.\n *\n * Entry structure:\n * _elementMeta[element.zcClippingId] = [client1.id, client2.id];\n */\n var _elementMeta = {};\n /**\n * Keep track of the state of the mouse event handlers for clipped elements.\n *\n * Entry structure:\n * _mouseHandlers[element.zcClippingId] = {\n * mouseover: function(event) {},\n * mouseout: function(event) {},\n * mouseenter: function(event) {},\n * mouseleave: function(event) {},\n * mousemove: function(event) {}\n * };\n */\n var _mouseHandlers = {};\n /**\n * Extending the ZeroClipboard configuration defaults for the Client module.\n */\n _extend(_globalConfig, {\n autoActivate: true\n });\n /**\n * The real constructor for `ZeroClipboard` client instances.\n * @private\n */\n var _clientConstructor = function(elements) {\n var client = this;\n client.id = \"\" + _clientIdCounter++;\n _clientMeta[client.id] = {\n instance: client,\n elements: [],\n handlers: {}\n };\n if (elements) {\n client.clip(elements);\n }\n ZeroClipboard.on(\"*\", function(event) {\n return client.emit(event);\n });\n ZeroClipboard.on(\"destroy\", function() {\n client.destroy();\n });\n ZeroClipboard.create();\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.on`.\n * @private\n */\n var _clientOn = function(eventType, listener) {\n var i, len, events, added = {}, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;\n if (typeof eventType === \"string\" && eventType) {\n events = eventType.toLowerCase().split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n this.on(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].replace(/^on/, \"\");\n added[eventType] = true;\n if (!handlers[eventType]) {\n handlers[eventType] = [];\n }\n handlers[eventType].push(listener);\n }\n if (added.ready && _flashState.ready) {\n this.emit({\n type: \"ready\",\n client: this\n });\n }\n if (added.error) {\n var errorTypes = [ \"disabled\", \"outdated\", \"unavailable\", \"deactivated\", \"overdue\" ];\n for (i = 0, len = errorTypes.length; i < len; i++) {\n if (_flashState[errorTypes[i]]) {\n this.emit({\n type: \"error\",\n name: \"flash-\" + errorTypes[i],\n client: this\n });\n break;\n }\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.off`.\n * @private\n */\n var _clientOff = function(eventType, listener) {\n var i, len, foundIndex, events, perEventHandlers, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;\n if (arguments.length === 0) {\n events = _keys(handlers);\n } else if (typeof eventType === \"string\" && eventType) {\n events = eventType.split(/\\s+/);\n } else if (typeof eventType === \"object\" && eventType && typeof listener === \"undefined\") {\n for (i in eventType) {\n if (_hasOwn.call(eventType, i) && typeof i === \"string\" && i && typeof eventType[i] === \"function\") {\n this.off(i, eventType[i]);\n }\n }\n }\n if (events && events.length) {\n for (i = 0, len = events.length; i < len; i++) {\n eventType = events[i].toLowerCase().replace(/^on/, \"\");\n perEventHandlers = handlers[eventType];\n if (perEventHandlers && perEventHandlers.length) {\n if (listener) {\n foundIndex = perEventHandlers.indexOf(listener);\n while (foundIndex !== -1) {\n perEventHandlers.splice(foundIndex, 1);\n foundIndex = perEventHandlers.indexOf(listener, foundIndex);\n }\n } else {\n perEventHandlers.length = 0;\n }\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.\n * @private\n */\n var _clientListeners = function(eventType) {\n var copy = null, handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;\n if (handlers) {\n if (typeof eventType === \"string\" && eventType) {\n copy = handlers[eventType] ? handlers[eventType].slice(0) : [];\n } else {\n copy = _deepCopy(handlers);\n }\n }\n return copy;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.\n * @private\n */\n var _clientEmit = function(event) {\n if (_clientShouldEmit.call(this, event)) {\n if (typeof event === \"object\" && event && typeof event.type === \"string\" && event.type) {\n event = _extend({}, event);\n }\n var eventCopy = _extend({}, _createEvent(event), {\n client: this\n });\n _clientDispatchCallbacks.call(this, eventCopy);\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.\n * @private\n */\n var _clientClip = function(elements) {\n elements = _prepClip(elements);\n for (var i = 0; i < elements.length; i++) {\n if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {\n if (!elements[i].zcClippingId) {\n elements[i].zcClippingId = \"zcClippingId_\" + _elementIdCounter++;\n _elementMeta[elements[i].zcClippingId] = [ this.id ];\n if (_globalConfig.autoActivate === true) {\n _addMouseHandlers(elements[i]);\n }\n } else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {\n _elementMeta[elements[i].zcClippingId].push(this.id);\n }\n var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;\n if (clippedElements.indexOf(elements[i]) === -1) {\n clippedElements.push(elements[i]);\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.\n * @private\n */\n var _clientUnclip = function(elements) {\n var meta = _clientMeta[this.id];\n if (!meta) {\n return this;\n }\n var clippedElements = meta.elements;\n var arrayIndex;\n if (typeof elements === \"undefined\") {\n elements = clippedElements.slice(0);\n } else {\n elements = _prepClip(elements);\n }\n for (var i = elements.length; i--; ) {\n if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {\n arrayIndex = 0;\n while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {\n clippedElements.splice(arrayIndex, 1);\n }\n var clientIds = _elementMeta[elements[i].zcClippingId];\n if (clientIds) {\n arrayIndex = 0;\n while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {\n clientIds.splice(arrayIndex, 1);\n }\n if (clientIds.length === 0) {\n if (_globalConfig.autoActivate === true) {\n _removeMouseHandlers(elements[i]);\n }\n delete elements[i].zcClippingId;\n }\n }\n }\n }\n return this;\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.\n * @private\n */\n var _clientElements = function() {\n var meta = _clientMeta[this.id];\n return meta && meta.elements ? meta.elements.slice(0) : [];\n };\n /**\n * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.\n * @private\n */\n var _clientDestroy = function() {\n this.unclip();\n this.off();\n delete _clientMeta[this.id];\n };\n /**\n * Inspect an Event to see if the Client (`this`) should honor it for emission.\n * @private\n */\n var _clientShouldEmit = function(event) {\n if (!(event && event.type)) {\n return false;\n }\n if (event.client && event.client !== this) {\n return false;\n }\n var clippedEls = _clientMeta[this.id] && _clientMeta[this.id].elements;\n var hasClippedEls = !!clippedEls && clippedEls.length > 0;\n var goodTarget = !event.target || hasClippedEls && clippedEls.indexOf(event.target) !== -1;\n var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;\n var goodClient = event.client && event.client === this;\n if (!(goodTarget || goodRelTarget || goodClient)) {\n return false;\n }\n return true;\n };\n /**\n * Handle the actual dispatching of events to a client instance.\n *\n * @returns `this`\n * @private\n */\n var _clientDispatchCallbacks = function(event) {\n if (!(typeof event === \"object\" && event && event.type)) {\n return;\n }\n var async = _shouldPerformAsync(event);\n var wildcardTypeHandlers = _clientMeta[this.id] && _clientMeta[this.id].handlers[\"*\"] || [];\n var specificTypeHandlers = _clientMeta[this.id] && _clientMeta[this.id].handlers[event.type] || [];\n var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);\n if (handlers && handlers.length) {\n var i, len, func, context, eventCopy, originalContext = this;\n for (i = 0, len = handlers.length; i < len; i++) {\n func = handlers[i];\n context = originalContext;\n if (typeof func === \"string\" && typeof _window[func] === \"function\") {\n func = _window[func];\n }\n if (typeof func === \"object\" && func && typeof func.handleEvent === \"function\") {\n context = func;\n func = func.handleEvent;\n }\n if (typeof func === \"function\") {\n eventCopy = _extend({}, event);\n _dispatchCallback(func, context, [ eventCopy ], async);\n }\n }\n }\n return this;\n };\n /**\n * Prepares the elements for clipping/unclipping.\n *\n * @returns An Array of elements.\n * @private\n */\n var _prepClip = function(elements) {\n if (typeof elements === \"string\") {\n elements = [];\n }\n return typeof elements.length !== \"number\" ? [ elements ] : elements;\n };\n /**\n * Add a `mouseover` handler function for a clipped element.\n *\n * @returns `undefined`\n * @private\n */\n var _addMouseHandlers = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n var _suppressMouseEvents = function(event) {\n if (!(event || (event = _window.event))) {\n return;\n }\n if (event._source !== \"js\") {\n event.stopImmediatePropagation();\n event.preventDefault();\n }\n delete event._source;\n };\n var _elementMouseOver = function(event) {\n if (!(event || (event = _window.event))) {\n return;\n }\n _suppressMouseEvents(event);\n ZeroClipboard.focus(element);\n };\n element.addEventListener(\"mouseover\", _elementMouseOver, false);\n element.addEventListener(\"mouseout\", _suppressMouseEvents, false);\n element.addEventListener(\"mouseenter\", _suppressMouseEvents, false);\n element.addEventListener(\"mouseleave\", _suppressMouseEvents, false);\n element.addEventListener(\"mousemove\", _suppressMouseEvents, false);\n _mouseHandlers[element.zcClippingId] = {\n mouseover: _elementMouseOver,\n mouseout: _suppressMouseEvents,\n mouseenter: _suppressMouseEvents,\n mouseleave: _suppressMouseEvents,\n mousemove: _suppressMouseEvents\n };\n };\n /**\n * Remove a `mouseover` handler function for a clipped element.\n *\n * @returns `undefined`\n * @private\n */\n var _removeMouseHandlers = function(element) {\n if (!(element && element.nodeType === 1)) {\n return;\n }\n var mouseHandlers = _mouseHandlers[element.zcClippingId];\n if (!(typeof mouseHandlers === \"object\" && mouseHandlers)) {\n return;\n }\n var key, val, mouseEvents = [ \"move\", \"leave\", \"enter\", \"out\", \"over\" ];\n for (var i = 0, len = mouseEvents.length; i < len; i++) {\n key = \"mouse\" + mouseEvents[i];\n val = mouseHandlers[key];\n if (typeof val === \"function\") {\n element.removeEventListener(key, val, false);\n }\n }\n delete _mouseHandlers[element.zcClippingId];\n };\n /**\n * Creates a new ZeroClipboard client instance.\n * Optionally, auto-`clip` an element or collection of elements.\n *\n * @constructor\n */\n ZeroClipboard._createClient = function() {\n _clientConstructor.apply(this, _args(arguments));\n };\n /**\n * Register an event listener to the client.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.on = function() {\n return _clientOn.apply(this, _args(arguments));\n };\n /**\n * Unregister an event handler from the client.\n * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`.\n * If no `eventType` is provided, it will unregister all handlers for every event type.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.off = function() {\n return _clientOff.apply(this, _args(arguments));\n };\n /**\n * Retrieve event listeners for an `eventType` from the client.\n * If no `eventType` is provided, it will retrieve all listeners for every event type.\n *\n * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`\n */\n ZeroClipboard.prototype.handlers = function() {\n return _clientListeners.apply(this, _args(arguments));\n };\n /**\n * Event emission receiver from the Flash object for this client's registered JavaScript event listeners.\n *\n * @returns For the \"copy\" event, returns the Flash-friendly \"clipData\" object; otherwise `undefined`.\n */\n ZeroClipboard.prototype.emit = function() {\n return _clientEmit.apply(this, _args(arguments));\n };\n /**\n * Register clipboard actions for new element(s) to the client.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.clip = function() {\n return _clientClip.apply(this, _args(arguments));\n };\n /**\n * Unregister the clipboard actions of previously registered element(s) on the page.\n * If no elements are provided, ALL registered elements will be unregistered.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.unclip = function() {\n return _clientUnclip.apply(this, _args(arguments));\n };\n /**\n * Get all of the elements to which this client is clipped.\n *\n * @returns array of clipped elements\n */\n ZeroClipboard.prototype.elements = function() {\n return _clientElements.apply(this, _args(arguments));\n };\n /**\n * Self-destruct and clean up everything for a single client.\n * This will NOT destroy the embedded Flash object.\n *\n * @returns `undefined`\n */\n ZeroClipboard.prototype.destroy = function() {\n return _clientDestroy.apply(this, _args(arguments));\n };\n /**\n * Stores the pending plain text to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setText = function(text) {\n ZeroClipboard.setData(\"text/plain\", text);\n return this;\n };\n /**\n * Stores the pending HTML text to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setHtml = function(html) {\n ZeroClipboard.setData(\"text/html\", html);\n return this;\n };\n /**\n * Stores the pending rich text (RTF) to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setRichText = function(richText) {\n ZeroClipboard.setData(\"application/rtf\", richText);\n return this;\n };\n /**\n * Stores the pending data to inject into the clipboard.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.setData = function() {\n ZeroClipboard.setData.apply(this, _args(arguments));\n return this;\n };\n /**\n * Clears the pending data to inject into the clipboard.\n * If no `format` is provided, all pending data formats will be cleared.\n *\n * @returns `this`\n */\n ZeroClipboard.prototype.clearData = function() {\n ZeroClipboard.clearData.apply(this, _args(arguments));\n return this;\n };\n /**\n * Gets a copy of the pending data to inject into the clipboard.\n * If no `format` is provided, a copy of ALL pending data formats will be returned.\n *\n * @returns `String` or `Object`\n */\n ZeroClipboard.prototype.getData = function() {\n return ZeroClipboard.getData.apply(this, _args(arguments));\n };\n if (typeof define === \"function\" && define.amd) {\n define(function() {\n return ZeroClipboard;\n });\n } else if (typeof module === \"object\" && module && typeof module.exports === \"object\" && module.exports) {\n module.exports = ZeroClipboard;\n } else {\n window.ZeroClipboard = ZeroClipboard;\n }\n})(function() {\n return this || window;\n}());"]}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/dist/ZeroClipboard.swf b/showcase/js/zeroclipboard/dist/ZeroClipboard.swf
new file mode 100755
index 0000000..d4e2561
Binary files /dev/null and b/showcase/js/zeroclipboard/dist/ZeroClipboard.swf differ
diff --git a/showcase/js/zeroclipboard/docs/api/ZeroClipboard.Core.md b/showcase/js/zeroclipboard/docs/api/ZeroClipboard.Core.md
new file mode 100755
index 0000000..7fccd33
--- /dev/null
+++ b/showcase/js/zeroclipboard/docs/api/ZeroClipboard.Core.md
@@ -0,0 +1,716 @@
+# ZeroClipboard.Core API
+
+This documents details the **ZeroClipboard.Core** API, including various types of properties, methods, and events. **ZeroClipboard.Core** is primarily intended for use in wrapping ZeroClipboard in 3rd party plugins, e.g. [jquery.zeroclipboard](https://github.com/zeroclipboard/jquery.zeroclipboard).
+
+
+## Static
+
+### Static Properties
+
+#### `ZeroClipboard.version`
+
+_[`String`]_ The version of the ZeroClipboard library being used, e.g. `"2.0.0"`.
+
+
+### Static Methods
+
+#### `ZeroClipboard.config(...)`
+
+```js
+var config = ZeroClipboard.config();
+```
+
+_[`Object`]_ Get a copy of the active configuration for ZeroClipboard.
+
+
+```js
+var swfPath = ZeroClipboard.config("swfPath");
+```
+
+_[`*`]_ Get a copy of the actively configured value for this configuration property for ZeroClipboard.
+
+
+```js
+var config = ZeroClipboard.config({
+ forceHandCursor: true
+});
+```
+
+_[`Object`]_ Set the active configuration for ZeroClipboard. Returns a copy of the updated active configuration. For complete details about what can be configured, see [**Configuration Options** below](#configuration-options).
+
+
+#### `ZeroClipboard.create()`
+_[`undefined`]_ Create the Flash bridge SWF object.
+
+
+#### `ZeroClipboard.destroy()`
+_[`undefined`]_ Emit the [`"destroy"`](#destroy) event, remove all event handlers, and destroy the Flash bridge.
+
+
+#### `ZeroClipboard.setData(...)`
+
+```js
+ZeroClipboard.setData("text/plain", "Blah");
+```
+
+_[`undefined`]_ Set the pending `data` of type `format` for clipboard injection.
+
+```js
+ZeroClipboard.setData({
+ "text/plain": "Blah",
+ "text/html": "Blah"
+});
+```
+
+_[`undefined`]_ Set the pending `data` of various formats for clipboard injection.
+
+
+#### `ZeroClipboard.clearData(...)`
+
+```js
+ZeroClipboard.clearData("text/plain");
+```
+
+_[`undefined`]_ Clear the pending data of type `format` for clipboard injection.
+
+```js
+ZeroClipboard.clearData();
+```
+
+_[`undefined`]_ Clear the pending data of ALL formats for clipboard injection.
+
+
+#### `ZeroClipboard.getData(...)`
+
+```js
+var text = ZeroClipboard.getData("text/plain");
+```
+
+_[`String`]_ Get the pending data of type `format` for clipboard injection.
+
+```js
+var dataObj = ZeroClipboard.getData();
+```
+
+_[`Object`]_ Get a copy of the pending data of ALL formats for clipboard injection.
+
+
+#### `ZeroClipboard.focus(...)`
+#### `ZeroClipboard.activate(...)`
+
+```js
+ZeroClipboard.focus(document.getElementById("d_clip_button"));
+```
+
+_[`undefined`]_ Focus/"activate" the provided element by moving the Flash SWF object in front of it. **NOTE:** The preferred method to use is `focus` but the alias `activate` is available for backward compatibility's sake.
+
+
+#### `ZeroClipboard.blur()`
+#### `ZeroClipboard.deactivate()`
+
+_[`undefined`]_ Blur/"deactivate" the currently focused/"activated" element, moving the Flash SWF object off the screen. **NOTE:** The preferred method to use is `blur` but the alias `deactivate` is available for backward compatibility's sake.
+
+
+#### `ZeroClipboard.activeElement()`
+
+```js
+var el = document.getElementById("d_clip_button");
+ZeroClipboard.focus(el);
+var activeEl = ZeroClipboard.activeElement(); // activeEl === el
+```
+
+_[`HTMLElement` or `null`]_ Return the currently "activated" element that the Flash SWF object is in front of it.
+
+
+#### `ZeroClipboard.state()`
+
+_[`Object`]_ Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
+
+
+#### `ZeroClipboard.isFlashUnavailable()`
+
+_[`Boolean`]_ Indicates if Flash Player is **definitely** unusable (disabled, outdated, unavailable, or deactivated). _**IMPORTANT:**_ This method should be considered private.
+
+
+#### `ZeroClipboard.on(...)`
+
+```js
+var listenerFn = function(e) { var ZeroClipboard = this; /* ... */ };
+ZeroClipboard.on("ready", listenerFn);
+
+var listenerObj = {
+ handleEvent: function(e) { var listenerObj = this; /* ... */ }
+};
+ZeroClipboard.on("error", listenerObj);
+```
+
+_[`undefined`]_ Add a `listener` function/object for an `eventType`.
+
+```js
+ZeroClipboard.on("ready error", function(e) { /* ... */ });
+```
+
+_[`undefined`]_ Add a `listener` function/object for multiple `eventType`s.
+
+```js
+ZeroClipboard.on({
+ "ready": function(e) { /* ... */ },
+ "error": function(e) { /* ... */ }
+});
+```
+
+_[`undefined`]_ Add a set of `eventType` to `listener` function/object mappings.
+
+
+#### `ZeroClipboard.off(...)`
+
+```js
+ZeroClipboard.off("ready", listenerFn);
+ZeroClipboard.off("error", listenerObj);
+```
+
+_[`undefined`]_ Remove a `listener` function/object for an `eventType`.
+
+```js
+ZeroClipboard.off("ready error", listenerFn);
+```
+
+_[`undefined`]_ Remove a `listener` function/object for multiple `eventType`s.
+
+```js
+ZeroClipboard.off({
+ "ready": readyListenerFn,
+ "error": errorListenerFn
+});
+```
+
+_[`undefined`]_ Remove a set of `eventType` to `listener` function/object mappings.
+
+```js
+ZeroClipboard.off("ready");
+```
+
+_[`undefined`]_ Remove ALL listener functions/objects for an `eventType`.
+
+```js
+ZeroClipboard.off();
+```
+
+_[`undefined`]_ Remove ALL listener functions/objects for ALL registered event types.
+
+
+#### `ZeroClipboard.emit(...)`
+
+```js
+ZeroClipboard.emit("ready");
+ZeroClipboard.emit({
+ type: "error",
+ name: "flash-disabled"
+});
+
+var pendingCopyData = ZeroClipboard.emit("copy");
+```
+
+_[`undefined`, or a Flash-friendly data Object for the `"copy"` event]_ Dispatch an event to all
+registered listeners. The emission of some types of events will result in side effects.
+
+
+#### `ZeroClipboard.handlers()`
+
+```js
+var listeners = ZeroClipboard.handlers("ready");
+```
+
+_[`Array`]_ Retrieves a copy of the registered listener functions/objects for the given `eventType`.
+
+
+```js
+var listeners = ZeroClipboard.handlers();
+```
+
+_[`Object`]_ Retrieves a copy of the map of registered listener functions/objects for ALL event types.
+
+
+
+### Static Events
+
+#### `"ready"`
+
+The `ready` event is fired when the Flash SWF completes loading and is ready for action. Please
+note that you need to set most configuration options [with [`ZeroClipboard.config(...)`](#zeroclipboardconfig)]
+before `ZeroClipboard.create()` is invoked.
+
+```js
+ZeroClipboard.on("ready", function(e) {
+/*
+ e = {
+ type: "ready",
+ message: "Flash communication is established",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ version: "11.2.202",
+ timeStamp: Date.now()
+ };
+*/
+});
+```
+
+
+#### `"beforecopy"`
+
+On `click`, the Flash object will fire off a `beforecopy` event. This event is generally only
+used for "UI prepartion" if you want to alter anything before the `copy` event fires.
+
+**IMPORTANT:** Handlers of this event are expected to operate synchronously if they intend to be
+finished before the "copy" event is triggered.
+
+```js
+ZeroClipboard.on("beforecopy", function(e) {
+/*
+ e = {
+ type: "beforecopy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now()
+ };
+*/
+});
+```
+
+
+#### `"copy"`
+
+On `click` (and after the `beforecopy` event), the Flash object will fire off a `copy` event. If
+the HTML object has `data-clipboard-text` or `data-clipboard-target`, then ZeroClipboard will take
+care of getting an initial set of data. It will then invoke any `copy` event handlers, in which you
+can call `event.clipboardData.setData` to set the text, which will complete the loop.
+
+**IMPORTANT:** If a handler of this event intends to modify the pending data for clipboard
+injection, it _MUST_ operate synchronously in order to maintain the temporarily elevated
+permissions granted by the user's `click` event. The most common "gotcha" for this restriction is
+if someone wants to make an asynchronous XMLHttpRequest in response to the `copy` event to get the
+data to inject — this won't work; make it a _synchronous_ XMLHttpRequest instead, or do the
+work in advance before the `copy` event is fired.
+
+```js
+ZeroClipboard.on("copy", function(e) {
+/*
+ e = {
+ type: "copy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ clipboardData: {
+ setData: ZeroClipboard.setData,
+ clearData: ZeroClipboard.clearData
+ }
+ };
+*/
+});
+```
+
+
+#### `"aftercopy"`
+
+The `aftercopy` event is fired when the text is copied [or failed to copy] to the clipboard.
+
+```js
+ZeroClipboard.on("aftercopy", function(e) {
+/*
+ e = {
+ type: "aftercopy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ success: {
+ "text/plain": true,
+ "text/html": true,
+ "application/rtf": false
+ },
+ data: {
+ "text/plain": "Blah",
+ "text/html": "Blah",
+ "application/rtf": "{\\rtf1\\ansi\n{\\b Blah}}"
+ }
+ };
+*/
+});
+```
+
+
+#### `"destroy"`
+
+The `destroy` event is fired when `ZeroClipboard.destroy()` is invoked.
+
+**IMPORTANT:** Handlers of this event are expected to operate synchronously if they intend to be
+finished before the destruction is complete.
+
+```js
+ZeroClipboard.on("destroy", function(e) {
+/*
+ e = {
+ type: "destroy",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ success: {
+ "text/plain": true,
+ "text/html": true,
+ "application/rtf": false
+ },
+ data: {
+ "text/plain": "Blah",
+ "text/html": "Blah",
+ "application/rtf": "{\\rtf1\\ansi\n{\\b Blah}}"
+ }
+ };
+*/
+});
+```
+
+
+#### `"error"`
+
+The `error` event is fired under a number of conditions, which will be detailed as sub-sections below.
+
+Some consumers may not consider all `error` types to be critical, and thus ZeroClipboard does not take it upon
+itself to implode by calling `ZeroClipboard.destroy()` under error conditions. However, many consumers may
+want to do just that.
+
+
+##### `error[name = "flash-disabled"]`
+
+This type of `error` event fires when Flash Player is either not installed or not enabled in the browser.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-disabled",
+ messsage: "Flash is disabled or not installed",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-outdated"]`
+
+This type of `error` event fires when Flash Player is installed in the browser but the version is too old
+for ZeroClipboard. ZeroClipboard requires Flash Player 11.0.0 or above.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-outdated",
+ messsage: "Flash is too outdated to support ZeroClipboard",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "10.3.183"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-unavailable"]`
+
+This type of `error` event fires when the browser's installation of Flash Player cannot communicate bidirectionally with JavaScript.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-unavailable",
+ messsage: "Flash is unable to communicate bidirectionally with JavaScript",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-deactivated"]`
+
+This type of `error` event fires when the browser's installation of Flash Player is either too old
+for the browser [but _not_ too old for ZeroClipboard] or if Flash objects are configured as
+click-to-play and the user does not authorize it within `_globalConfig.flashLoadTimeout`
+milliseconds or does not authorize it at all.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-deactivated",
+ messsage: "Flash is too outdated for your browser and/or is configured as click-to-activate",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-overdue"]`
+
+This type of `error` event fires when the SWF loads successfully but takes longer than
+`_globalConfig.flashLoadTimeout` milliseconds to do so. This would likely be caused by
+one of the following situations:
+ 1. Too short of a `_globalConfig.flashLoadTimeout` duration configured
+ 2. Network latency
+ 3. The user's installation of Flash is configured as click-to-play but then authorized
+ by the user too late such that the SWF does not finish loading before the timeout
+ period has expired (or it may have expired before they authorized it at all).
+
+The appropriate response to this event is left up to the consumer. For instance, if they
+chose to invoke `ZeroClipboard.destroy()` in response to the earlier
+`error[name = "flash-deactivated"]` event but then receive this `error[name = "flash-overdue"]`
+event, they may choose to "restart" their process and construct new ZeroClipboard client instances,
+or they may choose to just log the error to their server so they can consider increasing the
+allowed timeout duration in the future.
+
+This may be especially important for SPA or PJAX-based applications to consider as their users
+may remain on a single page for an extended period of time during which they _possibly_ could
+have enjoyed an improved experience if ZeroClipboard had been "restarted" after an initial hiccup.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-overdue",
+ messsage: "Flash communication was established but NOT within the acceptable time limit",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+
+## Configuration Options
+
+These are default values for the global configurations options. You should generally update these _before_ you create your clients.
+
+```js
+var _globalConfig = {
+
+ // SWF URL, relative to the page. Default value will be "ZeroClipboard.swf"
+ // under the same path as the ZeroClipboard JS file.
+ swfPath: _swfPath,
+
+ // SWF inbound scripting policy: page domains that the SWF should trust.
+ // (single string, or array of strings)
+ trustedDomains: window.location.host ? [window.location.host] : [],
+
+ // Include a "noCache" query parameter on requests for the SWF.
+ cacheBust: true,
+
+ // Enable use of the fancy "Desktop" clipboard, even on Linux where it is
+ // known to suck.
+ forceEnhancedClipboard: false,
+
+ // How many milliseconds to wait for the Flash SWF to load and respond before assuming that
+ // Flash is deactivated (e.g. click-to-play) in the user's browser. If you don't care about
+ // how long it takes to load the SWF, you can set this to `null`.
+ flashLoadTimeout: 30000,
+
+ // Setting this to `false` would allow users to handle calling `ZeroClipboard.focus(...);`
+ // themselves instead of relying on our per-element `mouseover` handler.
+ autoActivate: true,
+
+ // Bubble synthetic events in JavaScript after they are received by the Flash object.
+ bubbleEvents: true,
+
+ // Sets the ID of the `div` encapsulating the Flash object.
+ // Value is validated against the [HTML4 spec for `ID` tokens][valid_ids].
+ containerId: "global-zeroclipboard-html-bridge",
+
+ // Sets the class of the `div` encapsulating the Flash object.
+ containerClass: "global-zeroclipboard-container",
+
+ // Sets the ID and name of the Flash `object` element.
+ // Value is validated against the [HTML4 spec for `ID` and `Name` tokens][valid_ids].
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+
+ // The class used to indicate that a clipped element is being hovered over.
+ hoverClass: "zeroclipboard-is-hover",
+
+ // The class used to indicate that a clipped element is active (is being clicked).
+ activeClass: "zeroclipboard-is-active",
+
+
+
+ // Forcibly set the hand cursor ("pointer") for all clipped elements.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ forceHandCursor: false,
+
+ // Sets the title of the `div` encapsulating the Flash object.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ title: null,
+
+ // The z-index used by the Flash object.
+ // Max value (32-bit): 2147483647.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ zIndex: 999999999
+
+};
+```
+
+You can override the defaults by making calls like `ZeroClipboard.config({ swfPath: "new/path" });`
+before you create any clients.
+
+
+### SWF Inbound Scripting Access: The `trustedDomains` option
+
+This allows other SWF files and HTML pages from the allowed domains to access/call publicly
+exposed ActionScript code, e.g. functions shared via `ExternalInterface.addCallback`. In other
+words, it controls the SWF inbound scripting access.
+
+If your ZeroClipboard SWF is served from a different origin/domain than your page, you need to tell
+the SWF that it's OK to trust your page. The default value of `[window.location.host]` is almost
+_**always**_ what you will want unless you specifically want the SWF to communicate with pages from
+other domains (e.g. in `iframe`s or child windows).
+
+For more information about trusted domains, consult the [_official Flash documentation for `flash.system.Security.allowDomain(...)`_](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Security.html#allowDomain\(\)).
+
+
+### SWF Outbound Scripting Access
+
+The `allowScriptAccess` parameter (for Flash embedding markup) allows the SWF file to access/call
+JavaScript/HTML functionality of HTML pages on allowed domains, e.g. invoking functions via
+`ExternalInterface.call`. In other words, it controls the SWF outbound scripting access.
+
+As of version `v2.0.0-alpha.2`, the `allowScriptAccess` configuration option no longer exists. The
+appropriate value will be determined immediately before the Flash object is embedded on the page.
+The value is based on a relationship between the current domain (`window.location.host`) and the
+value of the `trustedDomains` configuration option.
+
+For more information about `allowScriptAccess`, consult the [_official Flash documentation_](http://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html).
+
+
+### Cross-Protocol Limitations
+
+ZeroClipboard was intentionally configured to _not_ allow the SWF to be served from a secure domain (HTTPS) but scripted by an insecure domain (HTTP).
+
+If you find yourself in this situation (as in [Issue #170](https://github.com/zeroclipboard/zeroclipboard/issues/170)), please consider the following options:
+ 1. Serve the SWF over HTTP instead of HTTPS. If the page's protocol can vary (e.g. authorized/unauthorized, staging/production, etc.), you should include add the SWF with a relative protocol (`//s3.amazonaws.com/blah/ZeroClipboard.swf`) instead of an absolute protocol (`https://s3.amazonaws.com/blah/ZeroClipboard.swf`).
+ 2. Serve the page over HTTPS instead of HTTP. If the page's protocol can vary, see the note on the previous option (1).
+ 3. Update ZeroClipboard's ActionScript codebase to call the [`allowInsecureDomain`](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Security.html#allowInsecureDomain\(\)) method, then recompile the SWF with your custom changes.
+
+
+### `file://` Protocol Limitations
+
+If you want to host a page locally on the `file://` protocol, you must specifically configure
+ZeroClipboard to trust ALL domains for SWF interaction via a wildcard. This configuration must be
+set _before_ creating ZeroClipboard client instances as a typical consumer, or before calling
+`ZeroClipboard.create()` in a 3rd party wrapper:
+
+```js
+ZeroClipboard.config({ trustedDomains: ["*"] });
+```
+
+This wildcard configuration should _**NOT**_ be used in environments hosted over HTTP/HTTPS.
+
+
+## Extending `ZeroClipboard`
+
+For developers who want to wrap ZeroClipboard into a 3rd party plugin
+(e.g. [jquery.zeroclipboard](https://github.com/zeroclipboard/jquery.zeroclipboard)), here
+are the important extension points:
+
+
+### Constructor
+
+Although the root `ZeroClipboard` function itself is actually a constructor, it also contains a
+particular hook that checks for the existence of a `ZeroClipboard._createClient` static function
+and invokes it with `this` (the freshly created `ZeroClipboard` instance) as the context and passes
+along all provided arguments to the constructor function, e.g.:
+
+```js
+var counterId = 0;
+ZeroClipboard._createClient = function(elements, otherStuff, etc) {
+ this.id = "" + (counterId++);
+ /* ... */
+};
+
+var $elementsToOperateOn = $(".clip_button");
+
+var client = new ZeroClipboard($elementsToOperateOn);
+```
+
+
+### Prototype Chain
+
+Using the `ZeroClipboard` constructor will allow you to also extend the underlying prototype with
+new instance-based methods, e.g.:
+
+```js
+ZeroClipboard.prototype.clientEmitOrSomeOtherOperationToInvoke = function(e) {
+ e.client = this;
+};
+```
+
+
+### Eventing
+
+Most clients will want to listen for some or all of the `ZeroClipboard.Core` events, and some
+clients will even want to regurgitate the same events to their own client-based listeners. To
+make the latter easier, `ZeroClipboard.Core` will also allow you to add a listener to an
+`eventType` of `"*"`, e.g.:
+
+```js
+ZeroClipboard._createClient = function() {
+ var client = this;
+ ZeroClipboard.on("*", function(e) {
+ client.clientEmitOrSomeOtherOperationToInvoke(e);
+ });
+};
+```
+
+
+### Static Extension
+
+The `ZeroClipboard.Core` API is composed of static methods stored as properties of the
+root `ZeroClipboard` function. As such, additional static methods can be added as desired, if there
+is any actual benefit to doing so, e.g.:
+
+```js
+ZeroClipboard.log = function() {
+ if (typeof console !== "undefined" && console.log) {
+ console.log.apply(console, Array.prototype.slice.call(arguments, 0));
+ }
+};
+```
diff --git a/showcase/js/zeroclipboard/docs/api/ZeroClipboard.md b/showcase/js/zeroclipboard/docs/api/ZeroClipboard.md
new file mode 100755
index 0000000..8a7b808
--- /dev/null
+++ b/showcase/js/zeroclipboard/docs/api/ZeroClipboard.md
@@ -0,0 +1,892 @@
+# ZeroClipboard API
+
+This documents details the ZeroClipboard API, including various types of properties, methods, and events.
+
+
+## Static
+
+### Static Properties
+
+#### `ZeroClipboard.version`
+
+_[`String`]_ The version of the ZeroClipboard library being used, e.g. `"2.0.0"`.
+
+
+### Static Methods
+
+#### `ZeroClipboard.config(...)`
+
+```js
+var config = ZeroClipboard.config();
+```
+
+_[`Object`]_ Get a copy of the active configuration for ZeroClipboard.
+
+
+```js
+var swfPath = ZeroClipboard.config("swfPath");
+```
+
+_[`*`]_ Get a copy of the actively configured value for this configuration property for ZeroClipboard.
+
+
+```js
+var config = ZeroClipboard.config({
+ forceHandCursor: true
+});
+```
+
+_[`Object`]_ Set the active configuration for ZeroClipboard. Returns a copy of the updated active configuration. For complete details about what can be configured, see [**Configuration Options** below](#configuration-options).
+
+
+#### `ZeroClipboard.create()`
+_[`undefined`]_ Create the Flash bridge SWF object. _**IMPORTANT:**_ This method should be considered private.
+
+
+#### `ZeroClipboard.destroy()`
+_[`undefined`]_ Emit the [`"destroy"`](#destroy) event, remove all event handlers, and destroy the Flash bridge.
+
+
+#### `ZeroClipboard.setData(...)`
+
+```js
+ZeroClipboard.setData("text/plain", "Blah");
+```
+
+_[`undefined`]_ Set the pending `data` of type `format` for clipboard injection.
+
+```js
+ZeroClipboard.setData({
+ "text/plain": "Blah",
+ "text/html": "Blah"
+});
+```
+
+_[`undefined`]_ Set the pending `data` of various formats for clipboard injection.
+
+
+#### `ZeroClipboard.clearData(...)`
+
+```js
+ZeroClipboard.clearData("text/plain");
+```
+
+_[`undefined`]_ Clear the pending data of type `format` for clipboard injection.
+
+```js
+ZeroClipboard.clearData();
+```
+
+_[`undefined`]_ Clear the pending data of ALL formats for clipboard injection.
+
+
+#### `ZeroClipboard.getData(...)`
+
+```js
+var text = ZeroClipboard.getData("text/plain");
+```
+
+_[`String`]_ Get the pending data of type `format` for clipboard injection.
+
+```js
+var dataObj = ZeroClipboard.getData();
+```
+
+_[`Object`]_ Get a copy of the pending data of ALL formats for clipboard injection.
+
+
+#### `ZeroClipboard.focus(...)`
+#### `ZeroClipboard.activate(...)`
+
+```js
+ZeroClipboard.focus(document.getElementById("d_clip_button"));
+```
+
+_[`undefined`]_ Focus/"activate" the provided element by moving the Flash SWF object in front of it. **NOTE:** The preferred method to use is `focus` but the alias `activate` is available for backward compatibility's sake.
+
+
+#### `ZeroClipboard.blur()`
+#### `ZeroClipboard.deactivate()`
+
+_[`undefined`]_ Blur/"deactivate" the currently focused/"activated" element, moving the Flash SWF object off the screen. **NOTE:** The preferred method to use is `blur` but the alias `deactivate` is available for backward compatibility's sake.
+
+
+#### `ZeroClipboard.activeElement()`
+
+```js
+var el = document.getElementById("d_clip_button");
+ZeroClipboard.focus(el);
+var activeEl = ZeroClipboard.activeElement(); // activeEl === el
+```
+
+_[`HTMLElement` or `null`]_ Return the currently "activated" element that the Flash SWF object is in front of it.
+
+
+#### `ZeroClipboard.state()`
+
+_[`Object`]_ Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
+
+
+#### `ZeroClipboard.isFlashUnavailable()`
+
+_[`Boolean`]_ Indicates if Flash Player is **definitely** unusable (disabled, outdated, unavailable, or deactivated). _**IMPORTANT:**_ This method should be considered private.
+
+
+#### `ZeroClipboard.on(...)`
+
+```js
+var listenerFn = function(e) { var ZeroClipboard = this; /* ... */ };
+ZeroClipboard.on("ready", listenerFn);
+
+var listenerObj = {
+ handleEvent: function(e) { var listenerObj = this; /* ... */ }
+};
+ZeroClipboard.on("error", listenerObj);
+```
+
+_[`undefined`]_ Add a `listener` function/object for an `eventType`.
+
+```js
+ZeroClipboard.on("ready error", function(e) { /* ... */ });
+```
+
+_[`undefined`]_ Add a `listener` function/object for multiple `eventType`s.
+
+```js
+ZeroClipboard.on({
+ "ready": function(e) { /* ... */ },
+ "error": function(e) { /* ... */ }
+});
+```
+
+_[`undefined`]_ Add a set of `eventType` to `listener` function/object mappings.
+
+
+#### `ZeroClipboard.off(...)`
+
+```js
+ZeroClipboard.off("ready", listenerFn);
+ZeroClipboard.off("error", listenerObj);
+```
+
+_[`undefined`]_ Remove a `listener` function/object for an `eventType`.
+
+```js
+ZeroClipboard.off("ready error", listenerFn);
+```
+
+_[`undefined`]_ Remove a `listener` function/object for multiple `eventType`s.
+
+```js
+ZeroClipboard.off({
+ "ready": readyListenerFn,
+ "error": errorListenerFn
+});
+```
+
+_[`undefined`]_ Remove a set of `eventType` to `listener` function/object mappings.
+
+```js
+ZeroClipboard.off("ready");
+```
+
+_[`undefined`]_ Remove ALL listener functions/objects for an `eventType`.
+
+```js
+ZeroClipboard.off();
+```
+
+_[`undefined`]_ Remove ALL listener functions/objects for ALL registered event types.
+
+
+#### `ZeroClipboard.emit(...)`
+
+```js
+ZeroClipboard.emit("ready");
+ZeroClipboard.emit({
+ type: "error",
+ name: "flash-disabled"
+});
+
+var pendingCopyData = ZeroClipboard.emit("copy");
+```
+
+_[`undefined`, or a Flash-friendly data `Object` for the `"copy"` event]_ Dispatch an event to all
+registered listeners. The emission of some types of events will result in side effects.
+
+
+#### `ZeroClipboard.handlers(...)`
+
+```js
+var listeners = ZeroClipboard.handlers("ready");
+```
+
+_[`Array`]_ Retrieves a copy of the registered listener functions/objects for the given `eventType`.
+
+
+```js
+var listeners = ZeroClipboard.handlers();
+```
+
+_[`Object`]_ Retrieves a copy of the map of registered listener functions/objects for ALL event types.
+
+
+
+### Static Events
+
+#### `"ready"`
+
+The `ready` event is fired when the Flash SWF completes loading and is ready for action. Please
+note that you need to set most configuration options [with [`ZeroClipboard.config(...)`](#zeroclipboardconfig)]
+before `ZeroClipboard.create()` is invoked.
+
+```js
+ZeroClipboard.on("ready", function(e) {
+/*
+ e = {
+ type: "ready",
+ message: "Flash communication is established",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ version: "11.2.202",
+ timeStamp: Date.now()
+ };
+*/
+});
+```
+
+
+#### `"beforecopy"`
+
+On `click`, the Flash object will fire off a `beforecopy` event. This event is generally only
+used for "UI prepartion" if you want to alter anything before the `copy` event fires.
+
+**IMPORTANT:** Handlers of this event are expected to operate synchronously if they intend to be
+finished before the "copy" event is triggered.
+
+```js
+ZeroClipboard.on("beforecopy", function(e) {
+/*
+ e = {
+ type: "beforecopy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now()
+ };
+*/
+});
+```
+
+
+#### `"copy"`
+
+On `click` (and after the `beforecopy` event), the Flash object will fire off a `copy` event. If
+the HTML object has `data-clipboard-text` or `data-clipboard-target`, then ZeroClipboard will take
+care of getting an initial set of data. It will then invoke any `copy` event handlers, in which you
+can call `event.clipboardData.setData` to set the text, which will complete the loop.
+
+**IMPORTANT:** If a handler of this event intends to modify the pending data for clipboard
+injection, it _MUST_ operate synchronously in order to maintain the temporarily elevated
+permissions granted by the user's `click` event. The most common "gotcha" for this restriction is
+if someone wants to make an asynchronous XMLHttpRequest in response to the `copy` event to get the
+data to inject — this won't work; make it a _synchronous_ XMLHttpRequest instead, or do the
+work in advance before the `copy` event is fired.
+
+```js
+ZeroClipboard.on("copy", function(e) {
+/*
+ e = {
+ type: "copy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ clipboardData: {
+ setData: ZeroClipboard.setData,
+ clearData: ZeroClipboard.clearData
+ }
+ };
+*/
+});
+```
+
+
+#### `"aftercopy"`
+
+The `aftercopy` event is fired when the text is copied [or failed to copy] to the clipboard.
+
+```js
+ZeroClipboard.on("aftercopy", function(e) {
+/*
+ e = {
+ type: "aftercopy",
+ target: currentlyActivatedElementOrNull,
+ relatedTarget: dataClipboardElementTargetOfCurrentlyActivatedElementOrNull,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ success: {
+ "text/plain": true,
+ "text/html": true,
+ "application/rtf": false
+ },
+ data: {
+ "text/plain": "Blah",
+ "text/html": "Blah",
+ "application/rtf": "{\\rtf1\\ansi\n{\\b Blah}}"
+ }
+ };
+*/
+});
+```
+
+
+#### `"destroy"`
+
+The `destroy` event is fired when `ZeroClipboard.destroy()` is invoked.
+
+**IMPORTANT:** Handlers of this event are expected to operate synchronously if they intend to be
+finished before the destruction is complete.
+
+```js
+ZeroClipboard.on("destroy", function(e) {
+/*
+ e = {
+ type: "destroy",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ success: {
+ "text/plain": true,
+ "text/html": true,
+ "application/rtf": false
+ },
+ data: {
+ "text/plain": "Blah",
+ "text/html": "Blah",
+ "application/rtf": "{\\rtf1\\ansi\n{\\b Blah}}"
+ }
+ };
+*/
+});
+```
+
+
+#### `"error"`
+
+The `error` event is fired under a number of conditions, which will be detailed as sub-sections below.
+
+Some consumers may not consider all `error` types to be critical, and thus ZeroClipboard does not take it upon
+itself to implode by calling `ZeroClipboard.destroy()` under error conditions. However, many consumers may
+want to do just that.
+
+
+##### `error[name = "flash-disabled"]`
+
+This type of `error` event fires when Flash Player is either not installed or not enabled in the browser.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-disabled",
+ messsage: "Flash is disabled or not installed",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-outdated"]`
+
+This type of `error` event fires when Flash Player is installed in the browser but the version is too old
+for ZeroClipboard. ZeroClipboard requires Flash Player 11.0.0 or above.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-outdated",
+ messsage: "Flash is too outdated to support ZeroClipboard",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "10.3.183"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-unavailable"]`
+
+This type of `error` event fires when the browser's installation of Flash Player cannot communicate bidirectionally with JavaScript.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-unavailable",
+ messsage: "Flash is unable to communicate bidirectionally with JavaScript",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-deactivated"]`
+
+This type of `error` event fires when the browser's installation of Flash Player is either too old
+for the browser [but _not_ too old for ZeroClipboard] or if Flash objects are configured as
+click-to-play and the user does not authorize it within `_globalConfig.flashLoadTimeout`
+milliseconds or does not authorize it at all.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-deactivated",
+ messsage: "Flash is too outdated for your browser and/or is configured as click-to-activate",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+##### `error[name = "flash-overdue"]`
+
+This type of `error` event fires when the SWF loads successfully but takes longer than
+`_globalConfig.flashLoadTimeout` milliseconds to do so. This would likely be caused by
+one of the following situations:
+ 1. Too short of a `_globalConfig.flashLoadTimeout` duration configured
+ 2. Network latency
+ 3. The user's installation of Flash is configured as click-to-play but then authorized
+ by the user too late such that the SWF does not finish loading before the timeout
+ period has expired (or it may have expired before they authorized it at all).
+
+The appropriate response to this event is left up to the consumer. For instance, if they
+chose to invoke `ZeroClipboard.destroy()` in response to the earlier
+`error[name = "flash-deactivated"]` event but then receive this `error[name = "flash-overdue"]`
+event, they may choose to "restart" their process and construct new ZeroClipboard client instances,
+or they may choose to just log the error to their server so they can consider increasing the
+allowed timeout duration in the future.
+
+This may be especially important for SPA or PJAX-based applications to consider as their users
+may remain on a single page for an extended period of time during which they _possibly_ could
+have enjoyed an improved experience if ZeroClipboard had been "restarted" after an initial hiccup.
+
+```js
+ZeroClipboard.on("error", function(e) {
+/*
+ e = {
+ type: "error",
+ name: "flash-overdue",
+ messsage: "Flash communication was established but NOT within the acceptable time limit",
+ target: null,
+ relatedTarget: null,
+ currentTarget: flashSwfObjectRef,
+ timeStamp: Date.now(),
+ minimumVersion: "11.0.0",
+ version: "11.2.202"
+ };
+*/
+});
+```
+
+
+
+## Instance
+
+The following properties and methods are accessible via a `ZeroClipboard` client instance, e.g.
+
+```js
+var clippedEl = document.getElementById("d_clip_button");
+var client = new ZeroClipboard(clippedEl);
+```
+
+
+### Instance Properties
+
+#### `client.id`
+
+_[`String`]_ A unique identifier for this ZeroClipboard client instance.
+
+
+### Instance Methods
+
+#### `client.destroy()`
+
+_[`undefined`]_ Remove all event handlers and unclip all clipped elements.
+
+
+#### `client.setText(...)`
+
+```js
+client.setText("Blah");
+```
+
+_[`this`]_ Set the pending `data` of type `"text/plain"` for clipboard injection.
+
+
+#### `client.setHtml(...)`
+
+```js
+client.setHtml("Blah");
+```
+
+_[`this`]_ Set the pending `data` of type `"text/html"` for clipboard injection.
+
+
+#### `client.setRichText(...)`
+
+```js
+client.setRichText("{\\rtf1\\ansi\n{\\b Blah}}");
+```
+
+_[`this`]_ Set the pending `data` of type `"application/rtf"` for clipboard injection.
+
+
+#### `client.setData(...)`
+
+```js
+client.setData("text/plain", "Blah");
+```
+
+_[`this`]_ Set the pending `data` of type `format` for clipboard injection.
+
+```js
+client.setData({
+ "text/plain": "Blah",
+ "text/html": "Blah"
+});
+```
+
+_[`this`]_ Set the pending `data` of various formats for clipboard injection. This particular
+function signature (passing in an `Object`) will implicitly clear out any existing pending data.
+
+
+#### `client.clearData(...)`
+
+```js
+client.clearData("text/plain");
+```
+
+_[`this`]_ Clear the pending data of type `format` for clipboard injection.
+
+```js
+client.clearData();
+```
+
+_[`this`]_ Clear the pending data of ALL formats for clipboard injection.
+
+
+#### `client.getData(...)`
+
+```js
+var text = client.getData("text/plain");
+```
+
+_[`String`]_ Get the pending data of type `format` for clipboard injection.
+
+```js
+var dataObj = client.getData();
+```
+
+_[`Object`]_ Get a copy of the pending data of ALL formats for clipboard injection.
+
+
+#### `client.clip(...)`
+
+```js
+client.clip(document.getElementById("d_clip_button"))
+client.clip(document.querySelectorAll(".clip_button"));
+client.clip(jQuery(".clip_button"));
+```
+
+_[`this`]_ Register clipboard actions for new element(s) to the client. This includes
+automatically invoking `ZeroClipboard.focus` on the current element when it is hovered over,
+unless the `autoActivate` configuration property is set to `false`.
+
+
+#### `client.unclip(...)`
+
+```js
+client.unclip(document.getElementById("d_clip_button"))
+client.unclip(document.querySelectorAll(".clip_button"));
+client.unclip(jQuery(".clip_button"));
+client.unclip();
+```
+
+_[`this`]_ Unregister the clipboard actions of previously registered element(s) on the page.
+If no elements are provided, ALL clipped/registered elements will be unregistered.
+
+
+#### `client.elements()`
+
+```js
+var els = client.elements();
+```
+
+_[`Array`]_ Get all of the elements to which this client is clipped/registered.
+
+
+#### `client.on(...)`
+
+```js
+var listenerFn = function(e) { var client = this; /* ... */ };
+client.on("ready", listenerFn);
+
+var listenerObj = {
+ handleEvent: function(e) { var listenerObj = this; /* ... */ }
+};
+client.on("error", listenerObj);
+```
+
+_[`this`]_ Add a `listener` function/object for an `eventType` within this client instance.
+
+```js
+client.on("ready error", function(e) { /* ... */ });
+```
+
+_[`this`]_ Add a `listener` function/object for multiple `eventType`s within this client instance.
+
+```js
+client.on({
+ "ready": function(e) { /* ... */ },
+ "error": function(e) { /* ... */ }
+});
+```
+
+_[`this`]_ Add a set of `eventType` to `listener` function/object mappings within this client instance.
+
+
+#### `client.off(...)`
+
+```js
+client.off("ready", listenerFn);
+client.off("error", listenerObj);
+```
+
+_[`this`]_ Remove a `listener` function/object for an `eventType` within this client instance.
+
+```js
+client.off("ready error", listenerFn);
+```
+
+_[`this`]_ Remove a `listener` function/object for multiple `eventType`s within this client instance.
+
+```js
+client.off({
+ "ready": readyListenerFn,
+ "error": errorListenerFn
+});
+```
+
+_[`this`]_ Remove a set of `eventType` to `listener` function/object mappings within this client instance.
+
+```js
+client.off("ready");
+```
+
+_[`this`]_ Remove ALL listener functions/objects for an `eventType` within this client instance.
+
+```js
+client.off();
+```
+
+_[`this`]_ Remove ALL listener functions/objects for ALL registered event types within this client instance.
+
+
+#### `client.emit(...)`
+
+```js
+client.emit("ready");
+client.emit({
+ type: "error",
+ name: "flash-disabled"
+});
+```
+
+_[`undefined`]_ Dispatch an event to all registered listeners within this client instance.
+
+
+#### `client.handlers(...)`
+
+```js
+var listeners = client.handlers("ready");
+```
+
+_[`Array`]_ Retrieves a copy of the registered listener functions/objects for the given `eventType` within this client instance.
+
+
+```js
+var listeners = client.handlers();
+```
+
+_[`Object`]_ Retrieves a copy of the map of registered listener functions/objects for ALL event types within this client instance.
+
+
+
+### Instance Events
+
+See the [Static Events](#static-events) listing. The ZeroClipboard client instances regurgitate the `ZeroClipboard.Core` events, ONLY if the event is NOT impertinent to this particular client. The only difference is that the clients' event dispatching will update the `event` object to include a `client` property that references the relevant client instance, e.g.:
+
+```js
+var client = new ZeroClipboard();
+client.on("ready", function(e) {
+ if (e.client === client && client === this) {
+ console.log("This client instance is ready!");
+ }
+});
+```
+
+
+
+## Configuration Options
+
+These are default values for the global configurations options. You should generally update these _before_ you create your clients.
+
+```js
+var _globalConfig = {
+
+ // SWF URL, relative to the page. Default value will be "ZeroClipboard.swf"
+ // under the same path as the ZeroClipboard JS file.
+ swfPath: _swfPath,
+
+ // SWF inbound scripting policy: page domains that the SWF should trust.
+ // (single string, or array of strings)
+ trustedDomains: window.location.host ? [window.location.host] : [],
+
+ // Include a "noCache" query parameter on requests for the SWF.
+ cacheBust: true,
+
+ // Enable use of the fancy "Desktop" clipboard, even on Linux where it is
+ // known to suck.
+ forceEnhancedClipboard: false,
+
+ // How many milliseconds to wait for the Flash SWF to load and respond before assuming that
+ // Flash is deactivated (e.g. click-to-play) in the user's browser. If you don't care about
+ // how long it takes to load the SWF, you can set this to `null`.
+ flashLoadTimeout: 30000,
+
+ // Setting this to `false` would allow users to handle calling `ZeroClipboard.focus(...);`
+ // themselves instead of relying on our per-element `mouseover` handler.
+ autoActivate: true,
+
+ // Bubble synthetic events in JavaScript after they are received by the Flash object.
+ bubbleEvents: true,
+
+ // Sets the ID of the `div` encapsulating the Flash object.
+ // Value is validated against the [HTML4 spec for `ID` tokens][valid_ids].
+ containerId: "global-zeroclipboard-html-bridge",
+
+ // Sets the class of the `div` encapsulating the Flash object.
+ containerClass: "global-zeroclipboard-container",
+
+ // Sets the ID and name of the Flash `object` element.
+ // Value is validated against the [HTML4 spec for `ID` and `Name` tokens][valid_ids].
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+
+ // The class used to indicate that a clipped element is being hovered over.
+ hoverClass: "zeroclipboard-is-hover",
+
+ // The class used to indicate that a clipped element is active (is being clicked).
+ activeClass: "zeroclipboard-is-active",
+
+
+
+ // Forcibly set the hand cursor ("pointer") for all clipped elements.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ forceHandCursor: false,
+
+ // Sets the title of the `div` encapsulating the Flash object.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ title: null,
+
+ // The z-index used by the Flash object.
+ // Max value (32-bit): 2147483647.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ zIndex: 999999999
+
+};
+```
+
+You can override the defaults by making calls like `ZeroClipboard.config({ swfPath: "new/path" });`
+before you create any clients.
+
+
+### SWF Inbound Scripting Access: The `trustedDomains` option
+
+This allows other SWF files and HTML pages from the allowed domains to access/call publicly
+exposed ActionScript code, e.g. functions shared via `ExternalInterface.addCallback`. In other
+words, it controls the SWF inbound scripting access.
+
+If your ZeroClipboard SWF is served from a different origin/domain than your page, you need to tell
+the SWF that it's OK to trust your page. The default value of `[window.location.host]` is almost
+_**always**_ what you will want unless you specifically want the SWF to communicate with pages from
+other domains (e.g. in `iframe`s or child windows).
+
+For more information about trusted domains, consult the [_official Flash documentation for `flash.system.Security.allowDomain(...)`_](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Security.html#allowDomain\(\)).
+
+
+### SWF Outbound Scripting Access
+
+The `allowScriptAccess` parameter (for Flash embedding markup) allows the SWF file to access/call
+JavaScript/HTML functionality of HTML pages on allowed domains, e.g. invoking functions via
+`ExternalInterface.call`. In other words, it controls the SWF outbound scripting access.
+
+As of version `v2.0.0-alpha.2`, the `allowScriptAccess` configuration option no longer exists. The
+appropriate value will be determined immediately before the Flash object is embedded on the page.
+The value is based on a relationship between the current domain (`window.location.host`) and the
+value of the `trustedDomains` configuration option.
+
+For more information about `allowScriptAccess`, consult the [_official Flash documentation_](http://helpx.adobe.com/flash/kb/control-access-scripts-host-web.html).
+
+
+### Cross-Protocol Limitations
+
+ZeroClipboard was intentionally configured to _not_ allow the SWF to be served from a secure domain (HTTPS) but scripted by an insecure domain (HTTP).
+
+If you find yourself in this situation (as in [Issue #170](https://github.com/zeroclipboard/zeroclipboard/issues/170)), please consider the following options:
+ 1. Serve the SWF over HTTP instead of HTTPS. If the page's protocol can vary (e.g. authorized/unauthorized, staging/production, etc.), you should include add the SWF with a relative protocol (`//s3.amazonaws.com/blah/ZeroClipboard.swf`) instead of an absolute protocol (`https://s3.amazonaws.com/blah/ZeroClipboard.swf`).
+ 2. Serve the page over HTTPS instead of HTTP. If the page's protocol can vary, see the note on the previous option (1).
+ 3. Update ZeroClipboard's ActionScript codebase to call the [`allowInsecureDomain`](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Security.html#allowInsecureDomain\(\)) method, then recompile the SWF with your custom changes.
+
+
+### `file://` Protocol Limitations
+
+If you want to host a page locally on the `file://` protocol, you must specifically configure
+ZeroClipboard to trust ALL domains for SWF interaction via a wildcard. This configuration must be
+set _before_ creating ZeroClipboard client instances as a typical consumer, or before calling
+`ZeroClipboard.create()` in a 3rd party wrapper:
+
+```js
+ZeroClipboard.config({ trustedDomains: ["*"] });
+```
+
+This wildcard configuration should _**NOT**_ be used in environments hosted over HTTP/HTTPS.
diff --git a/showcase/js/zeroclipboard/docs/instructions.md b/showcase/js/zeroclipboard/docs/instructions.md
new file mode 100755
index 0000000..a01dcb7
--- /dev/null
+++ b/showcase/js/zeroclipboard/docs/instructions.md
@@ -0,0 +1,439 @@
+### WARNING
+**This `master` branch contains the `v2.x` codebase for ZeroClipboard! For the `v1.x` codebase, see the [`1.x-master`](https://github.com/zeroclipboard/zeroclipboard/tree/1.x-master) branch instead.**
+
+
+# Overview
+
+The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible [Adobe Flash](http://en.wikipedia.org/wiki/Adobe_Flash) movie and a [JavaScript](http://en.wikipedia.org/wiki/JavaScript) interface. The "Zero" signifies that the library is invisible and the user interface is left entirely up to you.
+
+This is achieved by automatically floating the invisible movie on top of a [DOM](http://en.wikipedia.org/wiki/Document_Object_Model) element of your choice. Standard mouse events are even propagated out to your DOM element, so you can still have rollover and mousedown effects.
+
+
+## Limitations
+
+### User Interaction Required
+
+Due to browser and Flash security restrictions, this clipboard injection can _**ONLY**_ occur when
+the user clicks on the invisible Flash movie. A simulated `click` event from JavaScript will not
+suffice as this would enable [clipboard poisoning](http://www.computerworld.com/s/article/9117268/Adobe_patches_Flash_clickjacking_and_clipboard_poisoning_bugs).
+
+### Synchronicity Required During `copy`
+
+If a handler of `copy` event intends to modify the pending data for clipboard
+injection, it _MUST_ operate synchronously in order to maintain the temporarily elevated
+permissions granted by the user's `click` event. The most common "gotcha" for this restriction is
+if someone wants to make an asynchronous XMLHttpRequest in response to the `copy` event to get the
+data to inject — this will not work. You must make it a _synchronous_ XMLHttpRequest instead, or do the
+work in advance before the `copy` event is fired.
+
+### OS-Specific Limitations
+
+See [OS Considerations](#os-considerations) below.
+
+
+## Installation
+
+### [NPM](https://www.npmjs.org/) [![NPM version](https://badge.fury.io/js/zeroclipboard.png)](https://www.npmjs.org/package/zeroclipboard)
+
+```shell
+npm install zeroclipboard
+```
+
+### [Bower](http://bower.io/) [![Bower version](https://badge.fury.io/bo/zeroclipboard.png)](http://bower.io/search/?q=zeroclipboard)
+
+```shell
+bower install zeroclipboard
+```
+
+### [SPM](http://spmjs.io/) [![SPM version](http://spmjs.io/badge/zeroclipboard)](http://spmjs.io/package/zeroclipboard)
+
+```shell
+spm install zeroclipboard
+```
+
+### [PHP Composer](https://getcomposer.org/) [![PHP version](https://badge.fury.io/ph/zeroclipboard%2Fzeroclipboard.svg)](https://packagist.org/packages/zeroclipboard/zeroclipboard)
+
+For any PHP Composer users, ZeroClipboard is also [available on Packagist](https://packagist.org/packages/zeroclipboard/zeroclipboard).
+
+### [Ruby Gem](https://rubygems.org/)
+
+For any Rails users, the [`zeroclipboard-rails` Ruby Gem](https://rubygems.org/gems/zeroclipboard-rails) is available to automatically add ZeroClipboard into your Rails asset pipeline.
+
+
+## CDN Availability
+
+If you'd like to use ZeroClipboard hosted via a CDN (content delivery network), you can try:
+
+ - **cdnjs**: http://cdnjs.com/libraries/zeroclipboard
+ - **jsDelivr**: http://www.jsdelivr.com/#!zeroclipboard
+
+
+## Setup
+
+To use the library, simply include the following JavaScript file in your page:
+
+```html
+
+```
+
+You also need to have the "`ZeroClipboard.swf`" file available to the browser. If this file is
+located in the same directory as your web page, then it will work out of the box. However, if the
+SWF file is hosted elsewhere, you need to set the URL like this (place this code _after_ the script
+tag):
+
+```js
+ZeroClipboard.config( { swfPath: "http://YOURSERVER/path/ZeroClipboard.swf" } );
+```
+
+
+## Clients
+
+Now you are ready to create one or more _clients_. A client is a single instance of the clipboard
+library on the page, linked to one or more DOM elements. Here is how to create a client instance:
+
+```js
+var client = new ZeroClipboard();
+```
+
+You can also include an element or array of elements in the new client. _\*\*This example uses jQuery to find "copy buttons"._
+
+```js
+var client = new ZeroClipboard($(".copy-button"));
+```
+
+
+## API
+
+For the full API documentation, see [api/ZeroClipboard.md](api/ZeroClipboard.md). The full set of
+[Configuration Options](api/ZeroClipboard.md#configuration-options) are also documented there.
+
+For developers who want to wrap ZeroClipboard into a 3rd party plugin
+(e.g. [jquery.zeroclipboard](https://github.com/zeroclipboard/jquery.zeroclipboard)),
+see the [api/ZeroClipboard.Core.md](api/ZeroClipboard.Core.md) documentation instead.
+
+
+### Text To Copy
+
+Setting the clipboard text can be done in 4 ways:
+
+1. Add a `copy` event handler in which you call `event.clipboardData.setData` to set the appropriate data. This event is triggered every time ZeroClipboard tries to inject into the clipboard. Example:
+
+ ```js
+ client.on( "copy", function (event) {
+ var clipboard = event.clipboardData;
+ clipboard.setData( "text/plain", "Copy me!" );
+ clipboard.setData( "text/html", "Copy me!" );
+ clipboard.setData( "application/rtf", "{\\rtf1\\ansi\n{\\b Copy me!}}" );
+ });
+ ```
+
+2. Set the "text/plain" [and _usually_ "text/html"] clipboard segments via `data-clipboard-target` attribute on the button. ZeroClipboard will look for the target element via ID and try to get the HTML value via `.value`, `.outerHTML`, or `.innerHTML`, and the text value via `.value`, `.textContent`, or `.innerText`. If the HTML and text values for the targeted element match, the value will only be placed into the "text/plain" segment of the clipboard (i.e. the "text/html" segment will cleared).
+
+ ```html
+
+
+
+
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ ```
+
+3. Set the "text/plain" clipboard segment via `data-clipboard-text` attribute on the button. Doing this will let ZeroClipboard take care of the rest.
+
+ ```html
+
+ ```
+
+4. Set the data via the `ZeroClipboard.setData` (any segment) method. You can call this function at any time: when the page first loads, or later like in a `copy` event handler. Example:
+
+ ```js
+ ZeroClipboard.setData( "text/plain", "Copy me!" );
+ ```
+
+ The important caveat of using `ZeroClipboard.setData` is that the data it sets is **transient** and _will only be used for a single copy operation_. As such, we do not particularly
+ recommend using `ZeroClipboard.setData` (and friends) other than inside of a `copy` event handler; however, the API will not prevent you from using it in other ways.
+
+5. Set the data via the `client.setText` ("text/plain" segment), `client.setHtml` ("text/html" segment), `client.setRichText` ("application/rtf" segment), or `client.setData` (any segment) methods. You can call this function at any time: when the page first loads, or later like in a `copy` event handler. Example:
+
+ ```js
+ client.setText( "Copy me!" );
+ ```
+
+ The important caveat of using `client.setData` (and friends) is that the data it sets is **transient** and _will only be used for a single copy operation_. As such, we do not particularly
+ recommend using `client.setData` (and friends) other than inside of a `copy` event handler; however, the API will not prevent you from using it in other ways.
+
+
+### Clipping
+
+Clipping refers to the process of "linking" the Flash movie to a DOM element on the page. Since the Flash movie is completely transparent, the user sees nothing out of the ordinary.
+
+The Flash movie receives the click event and copies the text to the clipboard. Also, mouse actions like hovering and `mousedown` generate events that you can capture (see [_Event Handlers_](#event-handlers) below).
+
+To clip elements, you must pass an element, or array of elements to the `clip` function.
+
+Here is how to clip your client library instance to a DOM element:
+
+```js
+client.clip( document.getElementById("d_clip_button") );
+```
+
+You can pass in a reference to the actual DOM element object itself or an array of DOM objects. The rest all happens automatically: the movie is created, all your options set, and it is floated above the element, awaiting clicks from the user.
+
+
+### Example Implementation
+
+```html
+
+```
+
+And the code:
+
+```js
+var client = new ZeroClipboard( $("button#my-button") );
+```
+
+
+## CSS Effects
+
+Since the Flash movie is floating on top of your DOM element, it will receive all the mouse events before the browser has a chance to catch them. However, for convenience, these events are passed through to your clipboard client which you can capture (see _Event Handlers_ below), so long as the `bubbleEvents` configuration property remains set to `true`.
+
+In addition to this, ZeroClipboard can also manage CSS classes on the clipped elements that mimic the CSS pseudo-classes ":hover" and ":active" on your DOM element. This essentially allows your elements to behave normally, even though the floating Flash movie is the first object receiving all the mouse events during the event bubbling phase. These "pseudo-pseudo-class" names are configurable via the `hoverClass` and `activeClass` configuration properties.
+
+Example CSS, targeting a DOM element with a class of "clip_button":
+
+```css
+ .clip_button {
+ width: 150px;
+ text-align: center;
+ border: 1px solid black;
+ background-color: #ccc;
+ margin: 10px;
+ padding: 10px;
+ }
+ .clip_button.zeroclipboard-is-hover { background-color: #eee; }
+ .clip_button.zeroclipboard-is-active { background-color: #aaa; }
+```
+
+
+## Examples
+
+The following are complete, working examples of using the clipboard client library in HTML pages.
+
+
+### Minimal Example
+
+Here is a quick example using as few calls as possible:
+
+```html
+
+
+
Copy To Clipboard
+
+
+
+
+
+```
+
+When clicked, the text "Copy me!" will be copied to the clipboard.
+
+
+### A More Complete Example
+
+Here is a more complete example which exercises many of the configuration options and event handlers:
+
+```html
+
+
+
+
+
+
+
+
+
Copy To Clipboard
+
Copy This Too!
+
+
+
+
+```
+
+
+## Namespacing ZeroClipboard
+
+ZeroClipboard creates DOM elements with pre-configured attributes, e.g. a `div` element with an ID of `"global-zeroclipboard-html-bridge"` to encapsulate the Flash object.
+
+If you have a need to change the default values, they can be configured by passing in values for `containerId`, `containerClass`, and/or `swfObjectId` using the `ZeroClipboard.config` method. Configuration of these values is completely optional. These values cannot be reconfigured while the ZeroClipboard SWF is actively embedded, and so are completely ignored during that time.
+
+Values for `containerId` and `swfObjectId` are validated against the [HTML4 spec for `ID` and `Name` tokens][valid_ids].
+
+
+## AMD
+
+If using [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD) with a library such as [RequireJS](http://requirejs.org/), etc., you shouldn't need to do any special configuration for ZeroClipboard to work correctly as an AMD module.
+
+
+## CommonJS
+
+If using [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) with a library such as [Browserify](http://browserify.org/), [Webmake](https://github.com/medikoo/modules-webmake), etc., you shouldn't need to do any special configuration for ZeroClipboard to work correctly as an CommonJS module.
+
+
+## Known Conflicts With Other Libraries
+
+### [IE freezes when clicking a ZeroClipboard clipped element within a Bootstrap Modal](https://github.com/zeroclipboard/zeroclipboard/issues/159).
+ - **Cause:** Bootstrap's Modal has an `enforceFocus` function that tries to keep the focus on the modal.
+ However, since the ZeroClipboard container is an immediate child of the `body`, this enforcement conflicts. Note that
+ this workaround actually _overrides_ a core Bootstrap Modal function, and as such must be kept in sync as this function
+ changes in future versions of Bootstrap.
+ - **Workaround:** _Targeted against [Bootstrap v3.x](https://github.com/twbs/bootstrap/blob/96a9e1bae06cb21f8cf72ec528b8e31b6ab27272/js/modal.js#L115-123)._
+
+#### Workaround A
+
+```js
+if (/MSIE|Trident/.test(window.navigator.userAgent)) {
+ (function($) {
+ var zcContainerId = ZeroClipboard.config('containerId');
+ $('#' + zcContainerId).on('focusin', false);
+ })(window.jQuery);
+}
+```
+
+#### Workaround B
+
+```js
+if (/MSIE|Trident/.test(window.navigator.userAgent)) {
+ (function($) {
+ var zcClass = '.' + ZeroClipboard.config('containerClass');
+ var proto = $.fn.modal.Constructor.prototype;
+ proto.enforceFocus = function() {
+ $(document)
+ .off('focusin.bs.modal') /* Guard against infinite focus loop */
+ .on('focusin.bs.modal', $.proxy(function(e) {
+ if (this.$element[0] !== e.target &&
+ !this.$element.has(e.target).length &&
+ /* Adding this final condition check is the only real change */
+ !$(e.target).closest(zcClass).length
+ ) {
+ this.$element.focus();
+ }
+ }, this));
+ };
+ })(window.jQuery);
+}
+```
+
+
+### [IE freezes when clicking a ZeroClipboard clipped element within a jQuery UI [Modal] Dialog](https://github.com/zeroclipboard/zeroclipboard/issues/159).
+ - **Cause:** jQuery UI's Dialog (with `{ modal: true }` set) has a `_keepFocus` function that tries to keep the focus on the modal.
+ However, since the ZeroClipboard container is an immediate child of the `body`, this enforcement conflicts. Luckily, jQuery UI offers
+ more natural extension points than Bootstrap, so the workaround is smaller and less likely to be broken in future versions.
+ - **Workaround:** _Targeted against [jQuery UI v1.10.x](https://github.com/jquery/jquery-ui/blob/457b275880b63b05b16b7c9ee6c22f29f682ebc8/ui/jquery.ui.dialog.js#L695-703)._
+
+```js
+if (/MSIE|Trident/.test(window.navigator.userAgent)) {
+ (function($) {
+ var zcClass = '.' + ZeroClipboard.config('containerClass');
+ $.widget( 'ui.dialog', $.ui.dialog, {
+ _allowInteraction: function( event ) {
+ return this._super(event) || $( event.target ).closest( zcClass ).length;
+ }
+ } );
+ })(window.jQuery);
+}
+```
+
+
+## Support
+
+This library is fully compatible with Flash Player 11.0.0 and above, which requires
+that the clipboard copy operation be initiated by a user click event inside the
+Flash movie. This is achieved by automatically floating the invisible movie on top
+of a [DOM](http://en.wikipedia.org/wiki/Document_Object_Model) element of your
+choice. Standard mouse events are even propagated out to your DOM element, so you
+can still have rollover and mousedown effects with just a _little_ extra effort.
+
+ZeroClipboard `v2.x` is expected to work in IE9+ and all of the evergreen browsers.
+
+
+
+## OS Considerations
+
+Because ZeroClipboard will be interacting with your users' system clipboards, there are some special considerations
+specific to the users' operating systems that you should be aware of. With this information, you can make informed
+decisions of how _your_ site should handle each of these situations.
+
+ - **Windows:**
+ - If you want to ensure that your Windows users will be able to paste their copied text into Windows
+ Notepad and have it honor line breaks, you'll need to ensure that the text uses the sequence `\r\n` instead of
+ just `\n` for line breaks. If the text to copy is based on user input (e.g. a `textarea`), then you can achieve
+ this transformation by utilizing the `copy` event handler, e.g.
+
+ ```js
+ client.on('copy', function(event) {
+ var text = document.getElementById('yourTextArea').value;
+ var windowsText = text.replace(/\n/g, '\r\n');
+ event.clipboardData.setData('text/plain', windowsText);
+ });
+ ```
+
+ - **Linux:**
+ - The Linux Clipboard system (a.k.a. "Selection Atoms" within the [X Consortium's Standard Inter-Client Communication Conventions Manual](http://www.x.org/docs/ICCCM/icccm.pdf)) is a complex but capable setup. However,
+ for normal end users, it stinks. Flash Player's Clipboard API can either:
+ 1. Insert plain text into the "System Clipboard" and have it available everywhere; or
+ 2. Insert plain, HTML, and RTF text into the "Desktop Clipboard" but it will only be available in applications whose UI are managed by the Desktop Manager System (e.g. GNOME, etc.). This, for example, means that a user on a typical Linux configuration would not be able to paste something copied with ZeroClipboard into a terminal shell but they may still be able to paste it into OpenOffice, the browser, etc.
+
+ As such, the default behavior for ZeroClipboard while running on Linux is to only inject plain text into the "System Clipboard" to cover the most bases. If you want to ignore that caution and use the "Desktop Clipboard" anyway, just set the `forceEnhancedClipboard` configuration option to `true`, i.e.:
+
+ ```js
+ ZeroClipboard.config({
+ forceEnhancedClipboard: true
+ });
+ ```
+
+
+
+
+[valid_ids]: http://www.w3.org/TR/html4/types.html#type-id "HTML4 specification for `ID` and `Name` tokens"
diff --git a/showcase/js/zeroclipboard/docs/roadmap.md b/showcase/js/zeroclipboard/docs/roadmap.md
new file mode 100755
index 0000000..4967b72
--- /dev/null
+++ b/showcase/js/zeroclipboard/docs/roadmap.md
@@ -0,0 +1,9 @@
+# Roadmap
+
+These are things that we have expressed interest in but haven't implemented yet. There is no order, if you feel like you can complete one of the tasks. Feel free to fork the project and send a pull request with the new code.
+
+## HTML5 Clipboard API
+In a perfect world we wouldn't need ZeroClipboard, and the browsers would just take care of it. We would like to write ZeroClipboard to use the browser's clipboard API when available. See Issues [#171](https://github.com/zeroclipboard/zeroclipboard/issues/171) and [~~#105~~](https://github.com/zeroclipboard/zeroclipboard/issues/105).
+
+## Flash Tests
+We want to setup a unit test suite for the Flash SWF and/or its underlying ActionScript files. See Issue [#43](https://github.com/zeroclipboard/zeroclipboard/issues/43).
diff --git a/showcase/js/zeroclipboard/docs/security.md b/showcase/js/zeroclipboard/docs/security.md
new file mode 100755
index 0000000..0b20fd2
--- /dev/null
+++ b/showcase/js/zeroclipboard/docs/security.md
@@ -0,0 +1,32 @@
+# Security
+
+We try our best to keep ZeroClipboard secure but there are some rules that you should consider following to keep your site safe.
+
+
+## Existing Configuration
+
+For the existing configuration options available for security, see [Configuration Options](api/ZeroClipboard.md#configuration-options).
+
+
+## Rules
+
+Basically, if an attacker gets access to the main window/global object via an XSS exploit, it's pretty much an instant "GAME OVER" unless **ALL** of the following are true:
+ 1. The `ZeroClipboard` object itself is not globally accessible.
+ 2. The `ZeroClipboard.prototype` object itself is not globally accessible.
+ 3. No `ZeroClipboard` instances are globally accessible.
+ 4. No callback functions for dispatched ZeroClipboard events are globally accessible.
+ 5. If a variable is used to set the path to the SWF via `ZeroClipboard.config`, that variable must not be globally accessible.
+ 6. The DOM is not accessible (due to built-in support for `data-clipboard-text` and `data-clipboard-target` attributes).
+
+
+## Examples
+
+ 1. Having `ZeroClipboard` instances globally accessible (versus encapsulated in a closure). This allows an attacker to manually call a client's `setText` method and inject their own text.
+ 2. As with all globally accessible functions in JavaScript, any globally accessible callback functions (hooked to events) can be overridden by an attacker. This isn't terribly dangerous but could be annoying.
+ 3. Overriding any of the `ZeroClipboard` or `ZeroClipboard.prototype` properties or methods, if globally accessible.
+ 4. Adding `data-clipboard-text` or `data-clipboard-target` attributes to every element in the DOM.
+
+
+### Responsible Disclosure
+
+If you find any security holes that you believe can be patched, please submit a pull request or file an issue. We will be very appreciative!
diff --git a/showcase/js/zeroclipboard/index.js b/showcase/js/zeroclipboard/index.js
new file mode 100755
index 0000000..734458b
--- /dev/null
+++ b/showcase/js/zeroclipboard/index.js
@@ -0,0 +1,29 @@
+/*jshint node:true */
+
+
+// Module exports
+exports = module.exports = setup;
+
+// Module dependencies
+var http = require("http");
+var send = require("send");
+
+
+var root = __dirname;
+var swf = "/ZeroClipboard.swf";
+
+function setup() {
+ return http.createServer(onReq);
+}
+
+function onReq(req, res) {
+ send(req, swf)
+ .root(root)
+ .on("error", onError)
+ .pipe(res);
+}
+
+function onError(err) {
+ res.statusCode = err.status || 500;
+ res.end(err.message);
+}
diff --git a/showcase/js/zeroclipboard/package.json b/showcase/js/zeroclipboard/package.json
new file mode 100755
index 0000000..b2bd819
--- /dev/null
+++ b/showcase/js/zeroclipboard/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "zeroclipboard",
+ "title": "ZeroClipboard",
+ "version": "2.1.6",
+ "description": "The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface.",
+ "keywords": [
+ "flash",
+ "clipboard",
+ "copy",
+ "cut",
+ "paste",
+ "zclip",
+ "clip",
+ "clippy"
+ ],
+ "homepage": "http://zeroclipboard.org/",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/zeroclipboard/zeroclipboard/blob/master/LICENSE"
+ }
+ ],
+ "contributors": [
+ {
+ "name": "Jon Rohan",
+ "url": "http://jonrohan.me/"
+ },
+ {
+ "name": "James M. Greene",
+ "email": "james.m.greene@gmail.com",
+ "url": "http://jamesgreene.net/"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/zeroclipboard/zeroclipboard.git"
+ },
+ "bugs": {
+ "url": "https://github.com/zeroclipboard/zeroclipboard/issues"
+ },
+ "dependencies": {
+ "send": "0"
+ },
+ "devDependencies": {
+ "flex-sdk": "~4.6.0-0",
+ "flexpmd": "^1.3.0-1",
+ "grunt": "^0.4.5",
+ "grunt-chmod": "^1.0.3",
+ "grunt-contrib-clean": "^0.5.0",
+ "grunt-contrib-concat": "^0.4.0",
+ "grunt-contrib-connect": "^0.8.0",
+ "grunt-contrib-jshint": "^0.10.0",
+ "grunt-contrib-uglify": "^0.5.0",
+ "grunt-contrib-watch": "^0.6.1",
+ "grunt-coveralls": "^0.3.0",
+ "grunt-flexpmd": "^0.1.2",
+ "grunt-mxmlc": "^0.5.1",
+ "grunt-qunit-istanbul": "^0.4.5",
+ "grunt-template": "^0.2.3",
+ "jquery": "^2.1.1",
+ "load-grunt-tasks": "^0.6.0",
+ "qunit-composite": "^1.0.1",
+ "qunitjs": "^1.14.0",
+ "spm": "^3.0.1"
+ },
+ "main": "./dist/ZeroClipboard.js",
+ "component": {
+ "scripts": {
+ "zeroclipboard": "./dist/ZeroClipboard.js"
+ }
+ },
+ "spm": {
+ "main": "dist/ZeroClipboard.js",
+ "output": [
+ "dist/ZeroClipboard.swf",
+ "dist/ZeroClipboard.Core.js"
+ ]
+ },
+ "scripts": {
+ "test": "grunt travis --verbose",
+ "postpublish": "spm publish"
+ }
+}
diff --git a/showcase/js/zeroclipboard/src/flash/ClipboardInjector.as b/showcase/js/zeroclipboard/src/flash/ClipboardInjector.as
new file mode 100755
index 0000000..9f3c437
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/flash/ClipboardInjector.as
@@ -0,0 +1,185 @@
+package {
+
+ import flash.system.Capabilities;
+ import flash.system.System;
+ import flash.desktop.Clipboard;
+ import flash.desktop.ClipboardFormats;
+ import flash.utils.ByteArray;
+
+
+ /**
+ * An abstraction for injecting data into the user's clipboard.
+ */
+ internal class ClipboardInjector {
+ /**
+ * Use the fancy "Desktop" clipboard for expanded text support (e.g. HTML, RTF, etc.) if not on Linux
+ */
+ private var useEnhancedClipboard:Boolean = Capabilities.os.slice(0, 5).toLowerCase() !== "linux";
+
+
+ /**
+ * @constructor
+ */
+ public function ClipboardInjector(forceEnhancedClipboard:Boolean = false) {
+ // The JIT Compiler does not compile constructors, so any
+ // cyclomatic complexity higher than 1 is discouraged.
+ this.ctor(forceEnhancedClipboard);
+ }
+
+
+ /**
+ * The real constructor.
+ *
+ * @return `undefined`
+ */
+ private function ctor(forceEnhancedClipboard:Boolean = false): void {
+ // Should we use the fancy "Desktop" clipboard for expanded text support (e.g. HTML, RTF, etc.)?
+ this.useEnhancedClipboard = this.useEnhancedClipboard || forceEnhancedClipboard;
+ }
+
+
+ /**
+ * Inject data into the user's clipboard.
+ *
+ * @return A clipboard "results" object
+ */
+ public function inject(
+ clipData:Object // NOPMD
+ ): Object { // NOPMD
+ var results:Object = {}; // NOPMD
+
+ // Set all data formats' results to `false` (failed) initially
+ for (var dataFormat:String in clipData) {
+ if (dataFormat && clipData.hasOwnProperty(dataFormat)) {
+ results[dataFormat] = false;
+ }
+ }
+
+ // If there is any viable data to copy...
+ if (ClipboardInjector.hasData(clipData)) {
+ // ...and we only need to handle plain text...
+ if (!this.useEnhancedClipboard || ClipboardInjector.hasOnlyPlainText(clipData)) {
+ this.injectPlainTextOnly(clipData, results);
+ }
+ // ...else if there is viable data to copy and we can copy enhanced formats
+ else if (this.useEnhancedClipboard) {
+ this.injectEnhancedData(clipData, results);
+ }
+ }
+
+ return results;
+ }
+
+
+
+ /**
+ * Inject plain text into the System clipboard (i.e. Flash 9+ Clipboard).
+ *
+ * @return `undefined`
+ */
+ private function injectPlainTextOnly(
+ clipData:Object, // NOPMD
+ results:Object // NOPMD
+ ): void {
+ // Linux currently doesn't use the correct clipboard buffer with the new
+ // Flash 10 API, so we need to use this until we can figure out an alternative
+ try {
+ System.setClipboard(clipData.text);
+ results.text = true;
+ }
+ catch (e:Error) {
+ // Yes, this is already set but FlexPMD complains about empty `catch` blocks
+ results.text = false;
+ }
+ }
+
+
+ /**
+ * Inject plain text, HTML, and RTF into the Desktop clipboard (i.e. Flash 10+ Clipboard).
+ *
+ * @return `undefined`
+ */
+ private function injectEnhancedData(
+ clipData:Object, // NOPMD
+ results:Object // NOPMD
+ ): void {
+ // Clear out the clipboard before starting to copy data
+ Clipboard.generalClipboard.clear();
+
+ //
+ // Handle each data type in succession...
+ //
+ // Plain text
+ if (typeof clipData.text === "string" && clipData.text) {
+ try {
+ results.text = Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT, clipData.text);
+ }
+ catch (e:Error) {
+ results.text = false;
+ }
+ }
+
+ // HTML
+ if (typeof clipData.html === "string" && clipData.html) {
+ try {
+ results.html = Clipboard.generalClipboard.setData(ClipboardFormats.HTML_FORMAT, clipData.html);
+ }
+ catch (e:Error) {
+ results.html = false;
+ }
+ }
+
+ // Rich Text (RTF)
+ if (typeof clipData.rtf === "string" && clipData.rtf) {
+ try {
+ var bytes:ByteArray = new ByteArray();
+ bytes.writeUTFBytes(clipData.rtf);
+ if (bytes && bytes.length > 0) {
+ results.rtf = Clipboard.generalClipboard.setData(ClipboardFormats.RICH_TEXT_FORMAT, bytes);
+ }
+ }
+ catch (e:Error) {
+ results.rtf = false;
+ }
+ }
+ }
+
+
+ /**
+ * Check if data object contains any keys with associated values that are non-empty Strings.
+ *
+ * @return Boolean
+ */
+ private static function hasData(
+ clipData:Object // NOPMD
+ ): Boolean {
+ return typeof clipData === "object" && clipData &&
+ (
+ (typeof clipData.text === "string" && clipData.text) ||
+ (typeof clipData.html === "string" && clipData.html) ||
+ (typeof clipData.rtf === "string" && clipData.rtf )
+ );
+ }
+
+
+ /**
+ * Check if a data object's ONLY injectable data is plain text.
+ *
+ * @return Boolean
+ */
+ private static function hasOnlyPlainText(
+ clipData:Object // NOPMD
+ ): Boolean {
+ var hasPlainText:Boolean = false;
+ var hasOtherTypes:Boolean = false;
+ if (typeof clipData === "object" && clipData) {
+ hasPlainText = typeof clipData.text === "string" && clipData.text;
+ hasOtherTypes = (
+ (typeof clipData.html === "string" && clipData.html) ||
+ (typeof clipData.rtf === "string" && clipData.rtf )
+ );
+ }
+ return !hasOtherTypes && hasPlainText;
+ }
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/src/flash/JsProxy.as b/showcase/js/zeroclipboard/src/flash/JsProxy.as
new file mode 100755
index 0000000..88a8526
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/flash/JsProxy.as
@@ -0,0 +1,140 @@
+package {
+
+ import flash.external.ExternalInterface;
+ import flash.net.navigateToURL;
+ import flash.net.URLRequest;
+
+
+ /**
+ * An abstraction for communicating with JavaScript from Flash.
+ */
+ internal class JsProxy {
+ private var hosted:Boolean = false;
+ private var bidirectional:Boolean = false;
+ private var disabled:Boolean = false;
+
+
+ /**
+ * @constructor
+ */
+ public function JsProxy(expectedObjectId:String = null) {
+ // The JIT Compiler does not compile constructors, so any
+ // cyclomatic complexity higher than 1 is discouraged.
+ this.ctor(expectedObjectId);
+ }
+
+
+ /**
+ * The real constructor.
+ *
+ * @return `undefined`
+ */
+ private function ctor(expectedObjectId:String = null): void {
+ // Do we authoritatively know that this Flash object is hosted in a browser?
+ this.hosted = ExternalInterface.available === true &&
+ ExternalInterface.objectID &&
+ (expectedObjectId ? (expectedObjectId === ExternalInterface.objectID) : true);
+
+ // Can we retrieve values from JavaScript?
+ // Try this regardless of the return value of `ExternalInterface.call`.
+ try {
+ this.bidirectional = ExternalInterface.call("(function() { return true; })") === true;
+ }
+ catch (e:Error) {
+ // We do NOT authoritatively know if this Flash object is hosted in a browser,
+ // nor if JavaScript is disabled.
+ this.bidirectional = false;
+ }
+
+ // If hosted but cannot bidirectionally communicate with JavaScript,
+ // then JavaScript is disabled on the page!
+ this.disabled = this.hosted && !this.bidirectional;
+ }
+
+
+ /**
+ * Are we authoritatively certain that we can execute JavaScript bidirectionally?
+ *
+ * @return Boolean
+ */
+ public function isComplete(): Boolean {
+ return this.hosted && this.bidirectional;
+ }
+
+
+ /**
+ * Register an ActionScript method as callable from the container's JavaScript
+ *
+ * This will execute the JavaScript ONLY if ExternalInterface is completely
+ * available (hosted in the browser AND supporting bidirectional communication).
+ *
+ * @return `undefined`
+ */
+ public function addCallback(functionName:String, closure:Function): void {
+ if (this.isComplete()) {
+ ExternalInterface.addCallback(functionName, closure);
+ }
+ }
+
+ /**
+ * Execute a function expression or named function, with optional arguments,
+ * and receive its return value.
+ *
+ * This will execute the JavaScript ONLY if ExternalInterface is completely
+ * available (hosted in the browser AND supporting bidirectional communication).
+ *
+ * @example
+ * var jsProxy:JsProxy = new JsProxy("global-zeroclipboard-flash-bridge");
+ * var result:Object = jsProxy.call("ZeroClipboard.emit", [{ type: "copy" }]);
+ * jsProxy.call("(function(eventObj) { return ZeroClipboard.emit(eventObj); })", [{ type: "ready"}]);
+ *
+ * @return `undefined`, or anything
+ */
+ public function call(
+ jsFuncExpr:String,
+ args:Array = null
+ ): * { // NOPMD
+ var result:* = undefined; // NOPMD
+ if (jsFuncExpr && this.isComplete()) {
+ if (args == null) {
+ args = [];
+ }
+ result = ExternalInterface.call.apply(ExternalInterface, [jsFuncExpr].concat(args));
+ }
+ return result;
+ }
+
+
+ /**
+ * Execute a function expression or named function, with optional arguments.
+ * No return values will ever be received.
+ *
+ * This will attempt to execute the JavaScript, even if ExternalInterface is
+ * not available; in which case: the worst thing that can happen is that
+ * the JavaScript is not executed (i.e. if JavaScript is disabled, or if
+ * the SWF is not allowed to communicate with JavaScript on its host page).
+ *
+ * @return `undefined`
+ */
+ public function send(jsFuncExpr:String, args:Array = null): void {
+ if (jsFuncExpr) {
+ if (this.isComplete()) {
+ this.call(jsFuncExpr, args);
+ }
+ else if (!this.disabled) {
+ if (args == null) {
+ args = [];
+ }
+ var argsStr:String = "";
+ for (var counter:int = 0; counter < args.length; counter++) {
+ argsStr += JSON.stringify(args[counter]);
+ if ((counter + 1) < args.length) {
+ argsStr += ", ";
+ }
+ }
+ navigateToURL(new URLRequest("javascript:" + jsFuncExpr + "(" + argsStr + ");"), "_self");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/src/flash/XssUtils.as b/showcase/js/zeroclipboard/src/flash/XssUtils.as
new file mode 100755
index 0000000..101d5e5
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/flash/XssUtils.as
@@ -0,0 +1,38 @@
+package {
+
+ /**
+ * Utility methods for XSS attack prevention.
+ */
+ internal class XssUtils {
+
+ /**
+ * Sanitize a string to avoid XSS vulnerabilities.
+ *
+ * @return an XSS safe String
+ * @static
+ */
+ public static function sanitizeString(dirty:String): String {
+ return (typeof dirty === "string" && dirty) ? dirty.replace(/\\/g, "\\\\") : "";
+ }
+
+
+ /**
+ * Sanitize the Loader parameters by filtering out all URL query parameters,
+ * leaving ONLY parameters that were specified via FlashVars in the HTML
+ * embedding markup.
+ *
+ * @return a filtered parameters object, a.k.a. FlashVars
+ * @static
+ */
+ public static function filterToFlashVars(
+ parameters:Object // NOPMD
+ ): Object { // NOPMD
+ //
+ // TODO: Implement this for real
+ // See: https://github.com/zeroclipboard/zeroclipboard/pull/336
+ //
+ return parameters;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/src/flash/ZeroClipboard.as b/showcase/js/zeroclipboard/src/flash/ZeroClipboard.as
new file mode 100755
index 0000000..aa33399
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/flash/ZeroClipboard.as
@@ -0,0 +1,299 @@
+package {
+
+ import flash.display.Stage;
+ import flash.display.StageAlign;
+ import flash.display.StageScaleMode;
+ import flash.display.StageQuality;
+ import flash.display.Sprite;
+ import flash.events.Event;
+ import flash.events.MouseEvent;
+ import flash.system.Security;
+
+
+ /**
+ * The ZeroClipboard class creates a simple Sprite button that will put
+ * text in the user's clipboard when clicked.
+ */
+ [SWF(widthPercent="100%", heightPercent="100%", backgroundColor="#FFFFFF")]
+ public class ZeroClipboard extends Sprite {
+
+ /**
+ * Function through which JavaScript events are emitted. Accounts for scenarios
+ * in which ZeroClipboard is used via AMD/CommonJS module loaders, too.
+ */
+ private var jsEmitter:String = null;
+
+ /**
+ * JavaScript proxy object.
+ */
+ private var jsProxy:JsProxy = null;
+
+ /**
+ * Clipboard proxy object.
+ */
+ private var clipboard:ClipboardInjector = null;
+
+
+ /**
+ * @constructor
+ */
+ public function ZeroClipboard() {
+ // The JIT Compiler does not compile constructors, so ANY
+ // cyclomatic complexity higher than 1 is discouraged.
+ this.ctor();
+ }
+
+
+ /**
+ * The real constructor.
+ *
+ * @return `undefined`
+ */
+ private function ctor(): void {
+ // If the `stage` is available, begin!
+ if (stage) {
+ this.init();
+ }
+ else {
+ // Otherwise, wait for the `stage`....
+ this.addEventListener(Event.ADDED_TO_STAGE, this.init);
+ }
+ }
+
+
+ /**
+ * Initialize the class when the Stage is ready.
+ *
+ * @return `undefined`
+ */
+ private function init(): void {
+ // Remove the event listener, if any
+ this.removeEventListener(Event.ADDED_TO_STAGE, this.init);
+
+ // Get the flashvars
+ var flashvars:Object; // NOPMD
+ flashvars = XssUtils.filterToFlashVars(this.loaderInfo.parameters);
+
+ // Configure the SWF object's ID
+ var swfObjectId:String = "global-zeroclipboard-flash-bridge";
+ if (flashvars.swfObjectId && typeof flashvars.swfObjectId === "string") {
+ var swfId = XssUtils.sanitizeString(flashvars.swfObjectId);
+
+ // Validate the ID against the HTML4 spec for `ID` tokens.
+ if (/^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(swfId)) {
+ swfObjectId = swfId;
+ }
+ }
+
+ // Allow the SWF object to communicate with a page on a different origin than its own (e.g. SWF served from CDN)
+ if (flashvars.trustedOrigins && typeof flashvars.trustedOrigins === "string") {
+ var origins:Array = XssUtils.sanitizeString(flashvars.trustedOrigins).split(",");
+ Security.allowDomain.apply(Security, origins);
+ }
+
+ // Enable use of the fancy "Desktop" clipboard, even on Linux where it is known to suck
+ var forceEnhancedClipboard:Boolean = false;
+ if (flashvars.forceEnhancedClipboard === "true" || flashvars.forceEnhancedClipboard === true) {
+ forceEnhancedClipboard = true;
+ }
+
+ this.jsEmitter =
+ "(function(eventObj) {\n" +
+ " var objectId = '" + swfObjectId + "',\n" +
+ " ZC = null,\n" +
+ " swf = null;\n" +
+ " if (typeof ZeroClipboard === 'function' && typeof ZeroClipboard.emit === 'function') {\n" +
+ " \nZC = ZeroClipboard;\n" +
+ " }\n" +
+ " else {\n" +
+ " swf = document[objectId] || document.getElementById(objectId);\n" +
+ " if (swf && typeof swf.ZeroClipboard === 'function' && typeof swf.ZeroClipboard.emit === 'function') {\n" +
+ " ZC = swf.ZeroClipboard;\n" +
+ " }\n" +
+ " }\n" +
+ " if (!ZC) {\n" +
+ " throw new Error('ERROR: ZeroClipboard SWF could not locate ZeroClipboard JS object!\\n" +
+ "Expected element ID: ' + objectId);\n" +
+ " }\n" +
+ " return ZC.emit(eventObj);\n" +
+ "})";
+
+ // Create an invisible "button" and transparently fill the entire Stage
+ var button:Sprite = this.prepareUI();
+
+ // Configure the clipboard injector
+ this.clipboard = new ClipboardInjector(forceEnhancedClipboard);
+
+ // Establish a communication line with JavaScript
+ this.jsProxy = new JsProxy(swfObjectId);
+
+ // Only proceed if this SWF is hosted in the browser as expected
+ if (this.jsProxy.isComplete()) {
+
+ // Add the MouseEvent listeners
+ this.addMouseHandlers(button);
+
+ // Expose the external functions
+ this.jsProxy.addCallback(
+ "setHandCursor",
+ function(enabled:Boolean) {
+ button.useHandCursor = enabled === true;
+ }
+ );
+
+ // Signal to the browser that we are ready
+ this.emit("ready");
+ }
+ else {
+ // Signal to the browser that something is wrong
+ this.emit("error", {
+ name: "flash-unavailable"
+ });
+ }
+ }
+
+
+ /**
+ * Prepare the Stage and Button.
+ *
+ * @return Button
+ */
+ private function prepareUI(): Sprite {
+ // Set the stage!
+ stage.align = StageAlign.TOP_LEFT;
+ stage.scaleMode = StageScaleMode.EXACT_FIT;
+ stage.quality = StageQuality.BEST;
+
+ // Create an invisible "button" and transparently fill the entire Stage
+ var button:Sprite = new Sprite();
+ button.graphics.beginFill(0xFFFFFF);
+ button.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
+ button.alpha = 0.0;
+
+ // Act like a button. This includes:
+ // - Showing a hand cursor by default
+ // - Receiving click events
+ // - Receiving keypress events of space/"Enter" as click
+ // events IF AND ONLY IF the Sprite is focused.
+ button.buttonMode = true;
+
+ // Override the hand cursor default
+ button.useHandCursor = false;
+
+ // Add the invisible "button" to the stage!
+ this.addChild(button);
+
+ // Return the button for adding event listeners
+ return button;
+ }
+
+
+ /**
+ * Clears the clipboard and sets new clipboard text. It gets this from the "_clipData"
+ * variable on the JavaScript side. Once the text has been placed in the clipboard, it
+ * then signals to the JavaScript that it is done.
+ *
+ * @return `undefined`
+ */
+ private function onClick(event:MouseEvent): void {
+ var clipData:Object; // NOPMD
+ var clipInjectSuccess:Object = {}; // NOPMD
+
+ // Allow for any "UI preparation" work before the "copy" event begins
+ this.emit("beforecopy");
+
+ // Request pending clipboard data from the page
+ clipData = this.emit("copy");
+
+ // Inject all pending data into the user's clipboard
+ clipInjectSuccess = this.clipboard.inject(clipData);
+
+ // Compose and serialize a results object, send it back to the page
+ this.emit(
+ "aftercopy",
+ {
+ success: clipInjectSuccess,
+ data: clipData
+ }
+ );
+ }
+
+
+ /**
+ * Emit events to JavaScript.
+ *
+ * @return `undefined`, or the new "_clipData" object
+ */
+ private function emit(
+ eventType:String,
+ eventObj:Object = null // NOPMD
+ ): Object { // NOPMD
+ if (eventObj == null) {
+ eventObj = {};
+ }
+ eventObj.type = eventType;
+
+ var result:Object = undefined; // NOPMD
+ if (this.jsProxy.isComplete()) {
+ result = this.jsProxy.call(this.jsEmitter, [eventObj]);
+ }
+ else {
+ this.jsProxy.send(this.jsEmitter, [eventObj]);
+ }
+ return result;
+ }
+
+
+ /**
+ * Signals to the page that a MouseEvent occurred.
+ *
+ * @return `undefined`
+ */
+ private function onMouseEvent(event:MouseEvent): void {
+ var evtData:Object = {}; // NOPMD
+
+ // If an event is passed in, return what modifier keys are pressed, etc.
+ if (event) {
+ var props:Object; // NOPMD
+ props = {
+ "altKey": "altKey",
+ "commandKey": "metaKey",
+ "controlKey": "ctrlKey",
+ "shiftKey": "shiftKey",
+ "clickCount": "detail",
+ "movementX": "movementX",
+ "movementY": "movementY",
+ "stageX": "_stageX",
+ "stageY": "_stageY"
+ };
+
+ for (var prop in props) {
+ if (event.hasOwnProperty(prop) && event[prop] != null) {
+ evtData[props[prop]] = event[prop];
+ }
+ }
+ evtData.type = "_" + event.type.toLowerCase();
+ evtData._source = "swf";
+ }
+
+ this.emit(evtData.type, evtData);
+ }
+
+
+ /**
+ * Add mouse event handlers to the button.
+ *
+ * @return `undefined`
+ */
+ private function addMouseHandlers(button:Sprite): Sprite {
+ button.addEventListener(MouseEvent.MOUSE_MOVE, this.onMouseEvent);
+ button.addEventListener(MouseEvent.MOUSE_OVER, this.onMouseEvent);
+ button.addEventListener(MouseEvent.MOUSE_OUT, this.onMouseEvent);
+ button.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseEvent);
+ button.addEventListener(MouseEvent.MOUSE_UP, this.onMouseEvent);
+ button.addEventListener(MouseEvent.CLICK, this.onClick);
+ button.addEventListener(MouseEvent.CLICK, this.onMouseEvent);
+ return button;
+ }
+ }
+}
diff --git a/showcase/js/zeroclipboard/src/js/client/api.js b/showcase/js/zeroclipboard/src/js/client/api.js
new file mode 100755
index 0000000..7b3bbb5
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/client/api.js
@@ -0,0 +1,162 @@
+/**
+ * Creates a new ZeroClipboard client instance.
+ * Optionally, auto-`clip` an element or collection of elements.
+ *
+ * @constructor
+ */
+ZeroClipboard._createClient = function(/* elements */) {
+ // Invoke the real constructor
+ _clientConstructor.apply(this, _args(arguments));
+};
+
+
+/**
+ * Register an event listener to the client.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.on = function(/* eventType, listener */) {
+ return _clientOn.apply(this, _args(arguments));
+};
+
+
+/**
+ * Unregister an event handler from the client.
+ * If no `listener` function/object is provided, it will unregister all handlers for the provided `eventType`.
+ * If no `eventType` is provided, it will unregister all handlers for every event type.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.off = function(/* eventType, listener */) {
+ return _clientOff.apply(this, _args(arguments));
+};
+
+
+/**
+ * Retrieve event listeners for an `eventType` from the client.
+ * If no `eventType` is provided, it will retrieve all listeners for every event type.
+ *
+ * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
+ */
+ZeroClipboard.prototype.handlers = function(/* eventType */) {
+ return _clientListeners.apply(this, _args(arguments));
+};
+
+
+/**
+ * Event emission receiver from the Flash object for this client's registered JavaScript event listeners.
+ *
+ * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
+ */
+ZeroClipboard.prototype.emit = function(/* event */) {
+ return _clientEmit.apply(this, _args(arguments));
+};
+
+
+/**
+ * Register clipboard actions for new element(s) to the client.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.clip = function(/* elements */) {
+ return _clientClip.apply(this, _args(arguments));
+};
+
+
+/**
+ * Unregister the clipboard actions of previously registered element(s) on the page.
+ * If no elements are provided, ALL registered elements will be unregistered.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.unclip = function(/* elements */) {
+ return _clientUnclip.apply(this, _args(arguments));
+};
+
+
+/**
+ * Get all of the elements to which this client is clipped.
+ *
+ * @returns array of clipped elements
+ */
+ZeroClipboard.prototype.elements = function() {
+ return _clientElements.apply(this, _args(arguments));
+};
+
+
+/**
+ * Self-destruct and clean up everything for a single client.
+ * This will NOT destroy the embedded Flash object.
+ *
+ * @returns `undefined`
+ */
+ZeroClipboard.prototype.destroy = function() {
+ return _clientDestroy.apply(this, _args(arguments));
+};
+
+
+/**
+ * Stores the pending plain text to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.setText = function(text) {
+ ZeroClipboard.setData("text/plain", text);
+ return this;
+};
+
+
+/**
+ * Stores the pending HTML text to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.setHtml = function(html) {
+ ZeroClipboard.setData("text/html", html);
+ return this;
+};
+
+
+/**
+ * Stores the pending rich text (RTF) to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.setRichText = function(richText) {
+ ZeroClipboard.setData("application/rtf", richText);
+ return this;
+};
+
+
+/**
+ * Stores the pending data to inject into the clipboard.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.setData = function(/* format, data */) {
+ ZeroClipboard.setData.apply(this, _args(arguments));
+ return this;
+};
+
+
+/**
+ * Clears the pending data to inject into the clipboard.
+ * If no `format` is provided, all pending data formats will be cleared.
+ *
+ * @returns `this`
+ */
+ZeroClipboard.prototype.clearData = function(/* format */) {
+ ZeroClipboard.clearData.apply(this, _args(arguments));
+ return this;
+};
+
+
+/**
+ * Gets a copy of the pending data to inject into the clipboard.
+ * If no `format` is provided, a copy of ALL pending data formats will be returned.
+ *
+ * @returns `String` or `Object`
+ */
+ZeroClipboard.prototype.getData = function(/* format */) {
+ return ZeroClipboard.getData.apply(this, _args(arguments));
+};
diff --git a/showcase/js/zeroclipboard/src/js/client/private.js b/showcase/js/zeroclipboard/src/js/client/private.js
new file mode 100755
index 0000000..33743b1
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/client/private.js
@@ -0,0 +1,476 @@
+/**
+ * The real constructor for `ZeroClipboard` client instances.
+ * @private
+ */
+var _clientConstructor = function(elements) {
+ // Save a closure reference for the following event handlers
+ var client = this;
+
+ // Assign an ID to the client instance
+ client.id = "" + (_clientIdCounter++);
+
+ // Create the meta information store for this client
+ _clientMeta[client.id] = {
+ instance: client,
+ elements: [],
+ handlers: {}
+ };
+
+ // If the elements argument exists, clip it
+ if (elements) {
+ client.clip(elements);
+ }
+
+ // ECHO! Our client's sounding board.
+ ZeroClipboard.on("*", function(event) {
+ return client.emit(event);
+ });
+
+ // Await imminent destruction...
+ ZeroClipboard.on("destroy", function() {
+ client.destroy();
+ });
+
+ // Move on: embed the SWF
+ ZeroClipboard.create();
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.on`.
+ * @private
+ */
+var _clientOn = function(eventType, listener) {
+ // add user event handler for event
+ var i, len, events,
+ added = {},
+ handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+
+ if (typeof eventType === "string" && eventType) {
+ events = eventType.toLowerCase().split(/\s+/);
+ }
+ else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ this.on(i, eventType[i]);
+ }
+ }
+ }
+
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].replace(/^on/, "");
+ added[eventType] = true;
+ if (!handlers[eventType]) {
+ handlers[eventType] = [];
+ }
+ handlers[eventType].push(listener);
+ }
+
+ // The following events must be memorized and fired immediately if relevant as they only occur
+ // once per Flash object load.
+
+ // If the SWF was already loaded, we're à gogo!
+ if (added.ready && _flashState.ready) {
+ this.emit({
+ type: "ready",
+ client: this
+ });
+ }
+ if (added.error) {
+ var errorTypes = ["disabled", "outdated", "unavailable", "deactivated", "overdue"];
+ for (i = 0, len = errorTypes.length; i < len; i++) {
+ if (_flashState[errorTypes[i]]) {
+ this.emit({
+ type: "error",
+ name: "flash-" + errorTypes[i],
+ client: this
+ });
+ break;
+ }
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.off`.
+ * @private
+ */
+var _clientOff = function(eventType, listener) {
+ var i, len, foundIndex, events, perEventHandlers,
+ handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+ if (arguments.length === 0) {
+ // Remove ALL of the handlers for ALL event types
+ events = _keys(handlers);
+ }
+ else if (typeof eventType === "string" && eventType) {
+ events = eventType.split(/\s+/);
+ }
+ else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ this.off(i, eventType[i]);
+ }
+ }
+ }
+
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].toLowerCase().replace(/^on/, "");
+ perEventHandlers = handlers[eventType];
+ if (perEventHandlers && perEventHandlers.length) {
+ if (listener) {
+ foundIndex = perEventHandlers.indexOf(listener);
+ while (foundIndex !== -1) {
+ perEventHandlers.splice(foundIndex, 1);
+ foundIndex = perEventHandlers.indexOf(listener, foundIndex);
+ }
+ }
+ else {
+ // If no `listener` was provided, remove ALL of the handlers for this event type
+ perEventHandlers.length = 0;
+ }
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.handlers`.
+ * @private
+ */
+var _clientListeners = function(eventType) {
+ var copy = null,
+ handlers = _clientMeta[this.id] && _clientMeta[this.id].handlers;
+
+ if (handlers) {
+ if (typeof eventType === "string" && eventType) {
+ copy = handlers[eventType] ? handlers[eventType].slice(0) : [];
+ }
+ else {
+ // Make a deep copy of the handlers object
+ copy = _deepCopy(handlers);
+ }
+ }
+ return copy;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.emit`.
+ * @private
+ */
+var _clientEmit = function(event) {
+ if (_clientShouldEmit.call(this, event)) {
+ // Don't modify the original Event, if it is an object (as expected)
+ if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
+ event = _extend({}, event);
+ }
+ var eventCopy = _extend({}, _createEvent(event), { client: this });
+ _clientDispatchCallbacks.call(this, eventCopy);
+ }
+ return this;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.clip`.
+ * @private
+ */
+var _clientClip = function(elements) {
+ elements = _prepClip(elements);
+
+ for (var i = 0; i < elements.length ; i++) {
+ if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
+ // If the element hasn't been clipped to ANY client yet, add a metadata ID and event handler
+ if (!elements[i].zcClippingId) {
+ elements[i].zcClippingId = "zcClippingId_" + (_elementIdCounter++);
+ _elementMeta[elements[i].zcClippingId] = [this.id];
+ if (_globalConfig.autoActivate === true) {
+ _addMouseHandlers(elements[i]);
+ }
+ }
+ else if (_elementMeta[elements[i].zcClippingId].indexOf(this.id) === -1) {
+ _elementMeta[elements[i].zcClippingId].push(this.id);
+ }
+
+ // If the element hasn't been clipped to THIS client yet, add it
+ var clippedElements = _clientMeta[this.id] && _clientMeta[this.id].elements;
+ if (clippedElements.indexOf(elements[i]) === -1) {
+ clippedElements.push(elements[i]);
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.unclip`.
+ * @private
+ */
+var _clientUnclip = function(elements) {
+ var meta = _clientMeta[this.id];
+
+ if (!meta) {
+ return this;
+ }
+
+ var clippedElements = meta.elements;
+ var arrayIndex;
+
+ // If no elements were provided, unclip ALL of this client's clipped elements
+ if (typeof elements === "undefined") {
+ elements = clippedElements.slice(0);
+ }
+ else {
+ elements = _prepClip(elements);
+ }
+
+ for (var i = elements.length; i--; ) {
+ if (_hasOwn.call(elements, i) && elements[i] && elements[i].nodeType === 1) {
+ // If the element was clipped to THIS client yet, remove it
+ arrayIndex = 0;
+ while ((arrayIndex = clippedElements.indexOf(elements[i], arrayIndex)) !== -1) {
+ clippedElements.splice(arrayIndex, 1);
+ }
+
+ // If the element isn't clipped to ANY other client, remove its metadata ID and event handler
+ var clientIds = _elementMeta[elements[i].zcClippingId];
+ if (clientIds) {
+ arrayIndex = 0;
+ while ((arrayIndex = clientIds.indexOf(this.id, arrayIndex)) !== -1) {
+ clientIds.splice(arrayIndex, 1);
+ }
+ if (clientIds.length === 0) {
+ if (_globalConfig.autoActivate === true) {
+ _removeMouseHandlers(elements[i]);
+ }
+ delete elements[i].zcClippingId;
+ }
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.elements`.
+ * @private
+ */
+var _clientElements = function() {
+ var meta = _clientMeta[this.id];
+ return (meta && meta.elements) ? meta.elements.slice(0) : [];
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.Client.prototype.destroy`.
+ * @private
+ */
+var _clientDestroy = function() {
+ // Unclip all the elements
+ this.unclip();
+
+ // Remove all event handlers
+ this.off();
+
+ // Delete the client's metadata store
+ delete _clientMeta[this.id];
+};
+
+
+
+
+//
+// Helper functions
+//
+
+/**
+ * Inspect an Event to see if the Client (`this`) should honor it for emission.
+ * @private
+ */
+var _clientShouldEmit = function(event) {
+ // If no event is received
+ if (!(event && event.type)) {
+ return false;
+ }
+
+ // If this event's `client` was specifically set to a client other than this client, bail out
+ if (event.client && event.client !== this) {
+ return false;
+ }
+
+ // If this event's targeted element(s) is/are not clipped by this client, bail out
+ // unless the event's `client` was specifically set to this client.
+ var clippedEls = _clientMeta[this.id] && _clientMeta[this.id].elements;
+ var hasClippedEls = !!clippedEls && clippedEls.length > 0;
+ var goodTarget = !event.target || (hasClippedEls && clippedEls.indexOf(event.target) !== -1);
+ var goodRelTarget = event.relatedTarget && hasClippedEls && clippedEls.indexOf(event.relatedTarget) !== -1;
+ var goodClient = event.client && event.client === this;
+ // At least one of these must be true....
+ if (!(goodTarget || goodRelTarget || goodClient)) {
+ return false;
+ }
+
+ // Otherwise... go for it!
+ return true;
+};
+
+
+/**
+ * Handle the actual dispatching of events to a client instance.
+ *
+ * @returns `this`
+ * @private
+ */
+var _clientDispatchCallbacks = function(event) {
+ if (!(typeof event === "object" && event && event.type)) {
+ return;
+ }
+
+ var async = _shouldPerformAsync(event);
+
+ // User defined handlers for events
+ var wildcardTypeHandlers = (_clientMeta[this.id] && _clientMeta[this.id].handlers["*"]) || [];
+ var specificTypeHandlers = (_clientMeta[this.id] && _clientMeta[this.id].handlers[event.type]) || [];
+ // Execute wildcard handlers before type-specific handlers
+ var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
+
+ if (handlers && handlers.length) {
+ var i, len, func, context, eventCopy,
+ originalContext = this;
+ for (i = 0, len = handlers.length; i < len; i++) {
+ func = handlers[i];
+ context = originalContext;
+
+ // If the user provided a string for their callback, grab that function
+ if (typeof func === "string" && typeof _window[func] === "function") {
+ func = _window[func];
+ }
+ if (typeof func === "object" && func && typeof func.handleEvent === "function") {
+ context = func;
+ func = func.handleEvent;
+ }
+
+ if (typeof func === "function") {
+ eventCopy = _extend({}, event);
+ _dispatchCallback(func, context, [eventCopy], async);
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * Prepares the elements for clipping/unclipping.
+ *
+ * @returns An Array of elements.
+ * @private
+ */
+var _prepClip = function(elements) {
+ // if elements is a string, ignore it
+ if (typeof elements === "string") {
+ elements = [];
+ }
+ // if the elements isn't an array, wrap it with one
+ return typeof elements.length !== "number" ? [elements] : elements;
+};
+
+
+/**
+ * Add a `mouseover` handler function for a clipped element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _addMouseHandlers = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+
+ // Create a `mouseout` handler function
+ var _suppressMouseEvents = function(event) {
+ if (!(event || (event = _window.event))) {
+ return;
+ }
+
+ // Don't allow this event to be handled by consumers unless it originated from ZeroClipboard
+ if (event._source !== "js") {
+ event.stopImmediatePropagation();
+ event.preventDefault();
+ }
+ delete event._source;
+ };
+
+ // Create a `mouseover` handler function
+ var _elementMouseOver = function(event) {
+ if (!(event || (event = _window.event))) {
+ return;
+ }
+
+ // Don't allow this event to be handled by consumers unless it originated from ZeroClipboard
+ _suppressMouseEvents(event);
+
+ // Set this as the new currently active element
+ ZeroClipboard.focus(element);
+ };
+
+ // Add the `mouseover` handler function
+ element.addEventListener("mouseover", _elementMouseOver, false);
+
+ // Add other mouse event handler functions
+ element.addEventListener("mouseout", _suppressMouseEvents, false);
+ element.addEventListener("mouseenter", _suppressMouseEvents, false);
+ element.addEventListener("mouseleave", _suppressMouseEvents, false);
+ element.addEventListener("mousemove", _suppressMouseEvents, false);
+
+ // Save these function references to a global variable
+ _mouseHandlers[element.zcClippingId] = {
+ mouseover: _elementMouseOver,
+ mouseout: _suppressMouseEvents,
+ mouseenter: _suppressMouseEvents,
+ mouseleave: _suppressMouseEvents,
+ mousemove: _suppressMouseEvents
+ };
+};
+
+
+/**
+ * Remove a `mouseover` handler function for a clipped element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _removeMouseHandlers = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+
+ // Retrieve these function references from a global variable
+ var mouseHandlers = _mouseHandlers[element.zcClippingId];
+ if (!(typeof mouseHandlers === "object" && mouseHandlers)) {
+ return;
+ }
+
+ // Remove the mouse event handlers
+ var key, val,
+ mouseEvents = ["move", "leave", "enter", "out", "over"];
+ for (var i = 0, len = mouseEvents.length; i < len; i++) {
+ key = "mouse" + mouseEvents[i];
+ val = mouseHandlers[key];
+ if (typeof val === "function") {
+ element.removeEventListener(key, val, false);
+ }
+ }
+
+ // Delete these function references from a global variable
+ delete _mouseHandlers[element.zcClippingId];
+};
diff --git a/showcase/js/zeroclipboard/src/js/client/state.js b/showcase/js/zeroclipboard/src/js/client/state.js
new file mode 100755
index 0000000..7620641
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/client/state.js
@@ -0,0 +1,60 @@
+/**
+ * Keep track of the ZeroClipboard client instance counter.
+ */
+var _clientIdCounter = 0;
+
+
+/**
+ * Keep track of the state of the client instances.
+ *
+ * Entry structure:
+ * _clientMeta[client.id] = {
+ * instance: client,
+ * elements: [],
+ * handlers: {}
+ * };
+ */
+var _clientMeta = {};
+
+
+/**
+ * Keep track of the ZeroClipboard clipped elements counter.
+ */
+var _elementIdCounter = 0;
+
+
+/**
+ * Keep track of the state of the clipped element relationships to clients.
+ *
+ * Entry structure:
+ * _elementMeta[element.zcClippingId] = [client1.id, client2.id];
+ */
+var _elementMeta = {};
+
+
+/**
+ * Keep track of the state of the mouse event handlers for clipped elements.
+ *
+ * Entry structure:
+ * _mouseHandlers[element.zcClippingId] = {
+ * mouseover: function(event) {},
+ * mouseout: function(event) {},
+ * mouseenter: function(event) {},
+ * mouseleave: function(event) {},
+ * mousemove: function(event) {}
+ * };
+ */
+var _mouseHandlers = {};
+
+
+/**
+ * Extending the ZeroClipboard configuration defaults for the Client module.
+ */
+_extend(_globalConfig, {
+
+ // Setting this to `false` would allow users to handle calling
+ // `ZeroClipboard.focus(...);` themselves instead of relying on our
+ // per-element `mouseover` handler.
+ autoActivate: true
+
+});
diff --git a/showcase/js/zeroclipboard/src/js/core/api.js b/showcase/js/zeroclipboard/src/js/core/api.js
new file mode 100755
index 0000000..0870de9
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/core/api.js
@@ -0,0 +1,211 @@
+/**
+ * A shell constructor for `ZeroClipboard` client instances.
+ *
+ * @constructor
+ */
+var ZeroClipboard = function() {
+
+ // Ensure the constructor is invoked with the `new` keyword.
+ if (!(this instanceof ZeroClipboard)) {
+ return new ZeroClipboard();
+ }
+
+ // EXTREMELY IMPORTANT!
+ // Ensure the `ZeroClipboard._createClient` function is invoked if available.
+ // This allows an extension point for 3rd parties to create their own
+ // interpretations of what a ZeroClipboard "Client" should be like.
+ if (typeof ZeroClipboard._createClient === "function") {
+ ZeroClipboard._createClient.apply(this, _args(arguments));
+ }
+
+};
+
+
+/**
+ * The ZeroClipboard library's version number.
+ *
+ * @static
+ * @readonly
+ * @property {string}
+ */
+_defineProperty(ZeroClipboard, "version", {
+ value: "<%= version %>",
+ writable: false,
+ configurable: true,
+ enumerable: true
+});
+
+
+/**
+ * Update or get a copy of the ZeroClipboard global configuration.
+ * Returns a copy of the current/updated configuration.
+ *
+ * @returns Object
+ * @static
+ */
+ZeroClipboard.config = function(/* options */) {
+ return _config.apply(this, _args(arguments));
+};
+
+
+/**
+ * Diagnostic method that describes the state of the browser, Flash Player, and ZeroClipboard.
+ *
+ * @returns Object
+ * @static
+ */
+ZeroClipboard.state = function() {
+ return _state.apply(this, _args(arguments));
+};
+
+
+/**
+ * Check if Flash is unusable for any reason: disabled, outdated, deactivated, etc.
+ *
+ * @returns Boolean
+ * @static
+ */
+ZeroClipboard.isFlashUnusable = function() {
+ return _isFlashUnusable.apply(this, _args(arguments));
+};
+
+
+/**
+ * Register an event listener.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ZeroClipboard.on = function(/* eventType, listener */) {
+ return _on.apply(this, _args(arguments));
+};
+
+
+/**
+ * Unregister an event listener.
+ * If no `listener` function/object is provided, it will unregister all listeners for the provided `eventType`.
+ * If no `eventType` is provided, it will unregister all listeners for every event type.
+ *
+ * @returns `ZeroClipboard`
+ * @static
+ */
+ZeroClipboard.off = function(/* eventType, listener */) {
+ return _off.apply(this, _args(arguments));
+};
+
+
+/**
+ * Retrieve event listeners for an `eventType`.
+ * If no `eventType` is provided, it will retrieve all listeners for every event type.
+ *
+ * @returns array of listeners for the `eventType`; if no `eventType`, then a map/hash object of listeners for all event types; or `null`
+ */
+ZeroClipboard.handlers = function(/* eventType */) {
+ return _listeners.apply(this, _args(arguments));
+};
+
+
+/**
+ * Event emission receiver from the Flash object, forwarding to any registered JavaScript event listeners.
+ *
+ * @returns For the "copy" event, returns the Flash-friendly "clipData" object; otherwise `undefined`.
+ * @static
+ */
+ZeroClipboard.emit = function(/* event */) {
+ return _emit.apply(this, _args(arguments));
+};
+
+
+/**
+ * Create and embed the Flash object.
+ *
+ * @returns The Flash object
+ * @static
+ */
+ZeroClipboard.create = function() {
+ return _create.apply(this, _args(arguments));
+};
+
+
+/**
+ * Self-destruct and clean up everything, including the embedded Flash object.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ZeroClipboard.destroy = function() {
+ return _destroy.apply(this, _args(arguments));
+};
+
+
+/**
+ * Set the pending data for clipboard injection.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ZeroClipboard.setData = function(/* format, data */) {
+ return _setData.apply(this, _args(arguments));
+};
+
+
+/**
+ * Clear the pending data for clipboard injection.
+ * If no `format` is provided, all pending data formats will be cleared.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ZeroClipboard.clearData = function(/* format */) {
+ return _clearData.apply(this, _args(arguments));
+};
+
+
+/**
+ * Get a copy of the pending data for clipboard injection.
+ * If no `format` is provided, a copy of ALL pending data formats will be returned.
+ *
+ * @returns `String` or `Object`
+ * @static
+ */
+ZeroClipboard.getData = function(/* format */) {
+ return _getData.apply(this, _args(arguments));
+};
+
+
+/**
+ * Sets the current HTML object that the Flash object should overlay. This will put the global
+ * Flash object on top of the current element; depending on the setup, this may also set the
+ * pending clipboard text data as well as the Flash object's wrapping element's title attribute
+ * based on the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ZeroClipboard.focus = ZeroClipboard.activate = function(/* element */) {
+ return _focus.apply(this, _args(arguments));
+};
+
+
+/**
+ * Un-overlays the Flash object. This will put the global Flash object off-screen; depending on
+ * the setup, this may also unset the Flash object's wrapping element's title attribute based on
+ * the underlying HTML element and ZeroClipboard configuration.
+ *
+ * @returns `undefined`
+ * @static
+ */
+ZeroClipboard.blur = ZeroClipboard.deactivate = function() {
+ return _blur.apply(this, _args(arguments));
+};
+
+
+/**
+ * Returns the currently focused/"activated" HTML element that the Flash object is wrapping.
+ *
+ * @returns `HTMLElement` or `null`
+ * @static
+ */
+ZeroClipboard.activeElement = function() {
+ return _activeElement.apply(this, _args(arguments));
+};
diff --git a/showcase/js/zeroclipboard/src/js/core/private.js b/showcase/js/zeroclipboard/src/js/core/private.js
new file mode 100755
index 0000000..4c1c33b
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/core/private.js
@@ -0,0 +1,1701 @@
+/**
+ * The underlying implementation of `ZeroClipboard.config`.
+ * @private
+ */
+var _config = function(options) {
+ /*jshint maxdepth:6 */
+ if (typeof options === "object" && options !== null) {
+ for (var prop in options) {
+ if (_hasOwn.call(options, prop)) {
+ // These configuration values CAN be modified while a SWF is actively embedded.
+ if (/^(?:forceHandCursor|title|zIndex|bubbleEvents)$/.test(prop)) {
+ _globalConfig[prop] = options[prop];
+ }
+ // All other configuration values CANNOT be modified while a SWF is actively embedded.
+ else if (_flashState.bridge == null) {
+ if (prop === "containerId" || prop === "swfObjectId") {
+ // Validate values against the HTML4 spec for `ID` and `Name` tokens
+ if (_isValidHtml4Id(options[prop])) {
+ _globalConfig[prop] = options[prop];
+ }
+ else {
+ throw new Error("The specified `" + prop + "` value is not valid as an HTML4 Element ID");
+ }
+ }
+ else {
+ _globalConfig[prop] = options[prop];
+ }
+ }
+ }
+ }
+ }
+
+ if (typeof options === "string" && options) {
+ if (_hasOwn.call(_globalConfig, options)) {
+ // TODO: MAYBE do a `_deepCopy` of this as well? It is convenient to NOT
+ // do a `_deepCopy` if we want to allow consumers to, for example, be
+ // able to update the `trustedDomains` array on their own terms rather
+ // than having to send in a whole new array.
+ return _globalConfig[options];
+ }
+ // else `return undefined;`
+ return;
+ }
+
+ return _deepCopy(_globalConfig);
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.state`.
+ * @private
+ */
+var _state = function() {
+ return {
+ browser: _pick(_navigator, ["userAgent", "platform", "appName"]),
+ flash: _omit(_flashState, ["bridge"]),
+ zeroclipboard: {
+ version: ZeroClipboard.version,
+ config: ZeroClipboard.config()
+ }
+ };
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.isFlashUnusable`.
+ * @private
+ */
+var _isFlashUnusable = function() {
+ return !!(
+ _flashState.disabled ||
+ _flashState.outdated ||
+ _flashState.unavailable ||
+ _flashState.deactivated
+ );
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.on`.
+ * @private
+ */
+var _on = function(eventType, listener) {
+ var i, len, events,
+ added = {};
+
+ if (typeof eventType === "string" && eventType) {
+ events = eventType.toLowerCase().split(/\s+/);
+ }
+ else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.on(i, eventType[i]);
+ }
+ }
+ }
+
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].replace(/^on/, "");
+ added[eventType] = true;
+ if (!_handlers[eventType]) {
+ _handlers[eventType] = [];
+ }
+ _handlers[eventType].push(listener);
+ }
+
+ // The following events must be memorized and fired immediately if relevant as they only occur
+ // once per Flash object load.
+
+ // If the SWF was already loaded, we're à gogo!
+ if (added.ready && _flashState.ready) {
+ ZeroClipboard.emit({
+ type: "ready"
+ });
+ }
+ if (added.error) {
+ var errorTypes = ["disabled", "outdated", "unavailable", "deactivated", "overdue"];
+ for (i = 0, len = errorTypes.length; i < len; i++) {
+ if (_flashState[errorTypes[i]] === true) {
+ ZeroClipboard.emit({
+ type: "error",
+ name: "flash-" + errorTypes[i]
+ });
+ break;
+ }
+ }
+ }
+ }
+
+ return ZeroClipboard;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.off`.
+ * @private
+ */
+var _off = function(eventType, listener) {
+ var i, len, foundIndex, events, perEventHandlers;
+ if (arguments.length === 0) {
+ // Remove ALL of the _handlers for ALL event types
+ events = _keys(_handlers);
+ }
+ else if (typeof eventType === "string" && eventType) {
+ events = eventType.split(/\s+/);
+ }
+ else if (typeof eventType === "object" && eventType && typeof listener === "undefined") {
+ for (i in eventType) {
+ if (_hasOwn.call(eventType, i) && typeof i === "string" && i && typeof eventType[i] === "function") {
+ ZeroClipboard.off(i, eventType[i]);
+ }
+ }
+ }
+
+ if (events && events.length) {
+ for (i = 0, len = events.length; i < len; i++) {
+ eventType = events[i].toLowerCase().replace(/^on/, "");
+ perEventHandlers = _handlers[eventType];
+ if (perEventHandlers && perEventHandlers.length) {
+ if (listener) {
+ foundIndex = perEventHandlers.indexOf(listener);
+ while (foundIndex !== -1) {
+ perEventHandlers.splice(foundIndex, 1);
+ foundIndex = perEventHandlers.indexOf(listener, foundIndex);
+ }
+ }
+ else {
+ // If no `listener` was provided, remove ALL of the handlers for this event type
+ perEventHandlers.length = 0;
+ }
+ }
+ }
+ }
+
+ return ZeroClipboard;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.handlers`.
+ * @private
+ */
+var _listeners = function(eventType) {
+ var copy;
+ if (typeof eventType === "string" && eventType) {
+ copy = _deepCopy(_handlers[eventType]) || null;
+ }
+ else {
+ copy = _deepCopy(_handlers);
+ }
+ return copy;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.emit`.
+ * @private
+ */
+var _emit = function(event) {
+ var eventCopy, returnVal, tmp;
+
+ // Create an event object for this event type
+ event = _createEvent(event);
+
+ if (!event) {
+ return;
+ }
+
+ // Preprocess any special behaviors, reactions, or state changes after receiving this event
+ if (_preprocessEvent(event)) {
+ return;
+ }
+
+ // If this was a Flash "ready" event that was overdue, bail out and fire an "error" event instead
+ if (event.type === "ready" && _flashState.overdue === true) {
+ return ZeroClipboard.emit({ "type": "error", "name": "flash-overdue" });
+ }
+
+ // Trigger any and all registered event handlers
+ eventCopy = _extend({}, event);
+ _dispatchCallbacks.call(this, eventCopy);
+
+ // For the `copy` event, be sure to return the `_clipData` to Flash to be injected into the clipboard
+ if (event.type === "copy") {
+ tmp = _mapClipDataToFlash(_clipData);
+ returnVal = tmp.data;
+ _clipDataFormatMap = tmp.formatMap;
+ }
+ return returnVal;
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.create`.
+ * @private
+ */
+var _create = function() {
+ // Setup the Flash <-> JavaScript bridge
+ if (typeof _flashState.ready !== "boolean") {
+ _flashState.ready = false;
+ }
+ if (!ZeroClipboard.isFlashUnusable() && _flashState.bridge === null) {
+ var maxWait = _globalConfig.flashLoadTimeout;
+ if (typeof maxWait === "number" && maxWait >= 0) {
+ _setTimeout(function() {
+ // If it took longer than `_globalConfig.flashLoadTimeout` milliseconds to receive
+ // a `ready` event, so consider Flash "deactivated".
+ if (typeof _flashState.deactivated !== "boolean") {
+ _flashState.deactivated = true;
+ }
+ if (_flashState.deactivated === true) {
+ ZeroClipboard.emit({ "type": "error", "name": "flash-deactivated" });
+ }
+ }, maxWait);
+ }
+
+ // If attempting a fresh SWF embedding, it is safe to ignore the `overdue` status
+ _flashState.overdue = false;
+
+ // Embed the SWF
+ _embedSwf();
+ }
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.destroy`.
+ * @private
+ */
+var _destroy = function() {
+ // Clear any pending clipboard data
+ ZeroClipboard.clearData();
+
+ // Deactivate during self-destruct, even if `_globalConfig.autoActivate` !== `true`
+ ZeroClipboard.blur();
+
+ // Emit a special [synchronously handled] event so that Clients may listen
+ // for it and destroy themselves
+ ZeroClipboard.emit("destroy");
+
+ // Un-embed the SWF
+ _unembedSwf();
+
+ // Remove all event handlers
+ ZeroClipboard.off();
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.setData`.
+ * @private
+ */
+var _setData = function(format, data) {
+ var dataObj;
+
+ if (typeof format === "object" && format && typeof data === "undefined") {
+ dataObj = format;
+
+ // Clear out existing pending data if an object is provided
+ ZeroClipboard.clearData();
+ }
+ else if (typeof format === "string" && format) {
+ dataObj = {};
+ dataObj[format] = data;
+ }
+ else {
+ return;
+ }
+
+ // Copy over owned properties with non-empty string values
+ for (var dataFormat in dataObj) {
+ if (
+ typeof dataFormat === "string" && dataFormat &&
+ _hasOwn.call(dataObj, dataFormat) && typeof dataObj[dataFormat] === "string" && dataObj[dataFormat]
+ ) {
+ _clipData[dataFormat] = dataObj[dataFormat];
+ }
+ }
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.clearData`.
+ * @private
+ */
+var _clearData = function(format) {
+ // If no format is passed, delete all of the pending data
+ if (typeof format === "undefined") {
+ _deleteOwnProperties(_clipData);
+ _clipDataFormatMap = null;
+ }
+ // Otherwise, delete only the pending data of the specified format
+ else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ delete _clipData[format];
+ }
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.getData`.
+ * @private
+ */
+var _getData = function(format) {
+ // If no format is passed, get a copy of ALL of the pending data
+ if (typeof format === "undefined") {
+ return _deepCopy(_clipData);
+ }
+ // Otherwise, get only the pending data of the specified format
+ else if (typeof format === "string" && _hasOwn.call(_clipData, format)) {
+ return _clipData[format];
+ }
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.focus`/`ZeroClipboard.activate`.
+ * @private
+ */
+var _focus = function(element) {
+ if (!(element && element.nodeType === 1)) {
+ return;
+ }
+
+ // "Ignore" the currently active element
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.activeClass);
+
+ if (_currentElement !== element) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ }
+ }
+
+ // Mark the element as currently activated
+ _currentElement = element;
+ _addClass(element, _globalConfig.hoverClass);
+
+ // If the element has a title, mimic it
+ var newTitle = element.getAttribute("title") || _globalConfig.title;
+ if (typeof newTitle === "string" && newTitle) {
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.setAttribute("title", newTitle);
+ }
+ }
+
+ // If the element has a pointer style, set to hand cursor
+ var useHandCursor = _globalConfig.forceHandCursor === true || _getStyle(element, "cursor") === "pointer";
+ // Update the hand cursor state without updating the `forceHandCursor` option
+ _setHandCursor(useHandCursor);
+
+ // Move the Flash object over the newly activated element
+ _reposition();
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.blur`/`ZeroClipboard.deactivate`.
+ * @private
+ */
+var _blur = function() {
+ // Hide the Flash object off-screen
+ var htmlBridge = _getHtmlBridge(_flashState.bridge);
+ if (htmlBridge) {
+ htmlBridge.removeAttribute("title");
+ htmlBridge.style.left = "0px";
+ htmlBridge.style.top = "-9999px";
+ htmlBridge.style.width = "1px";
+ htmlBridge.style.top = "1px";
+ }
+
+ // "Ignore" the currently active element
+ if (_currentElement) {
+ _removeClass(_currentElement, _globalConfig.hoverClass);
+ _removeClass(_currentElement, _globalConfig.activeClass);
+ _currentElement = null;
+ }
+};
+
+
+/**
+ * The underlying implementation of `ZeroClipboard.activeElement`.
+ * @private
+ */
+var _activeElement = function() {
+ return _currentElement || null;
+};
+
+
+
+//
+// Helper functions
+//
+
+/**
+ * Check if a value is a valid HTML4 `ID` or `Name` token.
+ * @private
+ */
+var _isValidHtml4Id = function(id) {
+ return typeof id === "string" && id && /^[A-Za-z][A-Za-z0-9_:\-\.]*$/.test(id);
+};
+
+
+/**
+ * Create or update an `event` object, based on the `eventType`.
+ * @private
+ */
+var _createEvent = function(event) {
+ /*jshint maxstatements:30 */
+
+ var eventType;
+ if (typeof event === "string" && event) {
+ eventType = event;
+ event = {};
+ }
+ else if (typeof event === "object" && event && typeof event.type === "string" && event.type) {
+ eventType = event.type;
+ }
+
+ // Bail if we don't have an event type
+ if (!eventType) {
+ return;
+ }
+
+ // Sanitize the event type and set the `target` and `relatedTarget` properties if not already set
+ if (!event.target && /^(copy|aftercopy|_click)$/.test(eventType.toLowerCase())) {
+ event.target = _copyTarget;
+ }
+
+ _extend(event, {
+ type: eventType.toLowerCase(),
+ target: event.target || _currentElement || null,
+ relatedTarget: event.relatedTarget || null,
+ currentTarget: (_flashState && _flashState.bridge) || null,
+ timeStamp: event.timeStamp || _now() || null
+ });
+
+ var msg = _eventMessages[event.type];
+ if (event.type === "error" && event.name && msg) {
+ msg = msg[event.name];
+ }
+ if (msg) {
+ event.message = msg;
+ }
+
+ if (event.type === "ready") {
+ _extend(event, {
+ target: null,
+ version: _flashState.version
+ });
+ }
+
+ if (event.type === "error") {
+ if (/^flash-(disabled|outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ target: null,
+ minimumVersion: _minimumFlashVersion
+ });
+ }
+ if (/^flash-(outdated|unavailable|deactivated|overdue)$/.test(event.name)) {
+ _extend(event, {
+ version: _flashState.version
+ });
+ }
+ }
+
+ // Add all of the special properties and methods for a `copy` event
+ if (event.type === "copy") {
+ event.clipboardData = {
+ setData: ZeroClipboard.setData,
+ clearData: ZeroClipboard.clearData
+ };
+ }
+
+ if (event.type === "aftercopy") {
+ event = _mapClipResultsFromFlash(event, _clipDataFormatMap);
+ }
+
+ if (event.target && !event.relatedTarget) {
+ event.relatedTarget = _getRelatedTarget(event.target);
+ }
+
+ event = _addMouseData(event);
+
+ return event;
+};
+
+
+/**
+ * Get a relatedTarget from the target's `data-clipboard-target` attribute
+ * @private
+ */
+var _getRelatedTarget = function(targetEl) {
+ var relatedTargetId = targetEl && targetEl.getAttribute && targetEl.getAttribute("data-clipboard-target");
+ return relatedTargetId ? _document.getElementById(relatedTargetId) : null;
+};
+
+
+/**
+ * Add element and position data to `MouseEvent` instances
+ * @private
+ */
+var _addMouseData = function(event) {
+ if (event && /^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ // Element data
+ var srcElement = event.target;
+ var fromElement = event.type === "_mouseover" && event.relatedTarget ? event.relatedTarget : undefined;
+ var toElement = event.type === "_mouseout" && event.relatedTarget ? event.relatedTarget : undefined;
+
+ // Calculate positional data
+ var pos = _getDOMObjectPosition(srcElement);
+ var screenLeft = _window.screenLeft || _window.screenX || 0;
+ var screenTop = _window.screenTop || _window.screenY || 0;
+ var scrollLeft = _document.body.scrollLeft + _document.documentElement.scrollLeft;
+ var scrollTop = _document.body.scrollTop + _document.documentElement.scrollTop;
+ var pageX = pos.left + (typeof event._stageX === "number" ? event._stageX : 0);
+ var pageY = pos.top + (typeof event._stageY === "number" ? event._stageY : 0);
+ var clientX = pageX - scrollLeft;
+ var clientY = pageY - scrollTop;
+ var screenX = screenLeft + clientX;
+ var screenY = screenTop + clientY;
+ var moveX = typeof event.movementX === "number" ? event.movementX : 0;
+ var moveY = typeof event.movementY === "number" ? event.movementY : 0;
+
+ // Remove these transient properties, if present
+ delete event._stageX;
+ delete event._stageY;
+
+ // Update the appropriate properties of `event`, mostly with position data.
+ // Good notes:
+ // http://www.jacklmoore.com/notes/mouse-position/
+ _extend(event, {
+ srcElement: srcElement,
+ fromElement: fromElement,
+ toElement: toElement,
+ screenX: screenX, // screenLeft + clientX
+ screenY: screenY, // screenTop + clientY
+ pageX: pageX, // scrollLeft + clientX
+ pageY: pageY, // scrollTop + clientY
+ clientX: clientX, // pageX - scrollLeft
+ clientY: clientY, // pageY - scrollTop
+ x: clientX, // clientX
+ y: clientY, // clientY
+ movementX: moveX, // movementX
+ movementY: moveY, // movementY
+ offsetX: 0, // Unworthy of calculation
+ offsetY: 0, // Unworthy of calculation
+ layerX: 0, // Unworthy of calculation
+ layerY: 0 // Unworthy of calculation
+ });
+ }
+
+ return event;
+};
+
+
+/**
+ * Determine if an event's registered handlers should be execute synchronously or asynchronously.
+ *
+ * @returns {boolean}
+ * @private
+ */
+var _shouldPerformAsync = function(event) {
+ var eventType = (event && typeof event.type === "string" && event.type) || "";
+
+ // Determine if the event handlers for this event can be performed asynchronously.
+ // - `beforecopy`: This event's callback cannot be performed asynchronously because the
+ // subsequent `copy` event cannot.
+ // - `copy`: This event's callback cannot be performed asynchronously as it would prevent the
+ // user from being able to call `.setText` successfully before the pending clipboard
+ // injection associated with this event fires.
+ // - `destroy`: This event's callback cannot be performed asynchronously as it is necessary
+ // to allow any downstream clients the chance to destroy themselves as well
+ // as well before the final destruction of the SWF object and removal of all
+ // registered event handlers.
+ // - The handlers for all other event types should be performed asynchronously.
+ return !/^(?:(?:before)?copy|destroy)$/.test(eventType);
+};
+
+
+/**
+ * Control if a callback should be executed asynchronously or not.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _dispatchCallback = function(func, context, args, async) {
+ if (async) {
+ _setTimeout(function() {
+ func.apply(context, args);
+ }, 0);
+ }
+ else {
+ func.apply(context, args);
+ }
+};
+
+
+/**
+ * Handle the actual dispatching of events to client instances.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _dispatchCallbacks = function(event) {
+ if (!(typeof event === "object" && event && event.type)) {
+ return;
+ }
+
+ var async = _shouldPerformAsync(event);
+
+ // User defined handlers for events
+ var wildcardTypeHandlers = _handlers["*"] || [];
+ var specificTypeHandlers = _handlers[event.type] || [];
+ // Execute wildcard handlers before type-specific handlers
+ var handlers = wildcardTypeHandlers.concat(specificTypeHandlers);
+
+ if (handlers && handlers.length) {
+ var i, len, func, context, eventCopy,
+ originalContext = this;
+ for (i = 0, len = handlers.length; i < len; i++) {
+ func = handlers[i];
+ context = originalContext;
+
+ // If the user provided a string for their callback, grab that function
+ if (typeof func === "string" && typeof _window[func] === "function") {
+ func = _window[func];
+ }
+ if (typeof func === "object" && func && typeof func.handleEvent === "function") {
+ context = func;
+ func = func.handleEvent;
+ }
+
+ if (typeof func === "function") {
+ eventCopy = _extend({}, event);
+ _dispatchCallback(func, context, [eventCopy], async);
+ }
+ }
+ }
+ return this;
+};
+
+
+/**
+ * Preprocess any special behaviors, reactions, or state changes after receiving this event.
+ * Executes only once per event emitted, NOT once per client.
+ * @private
+ */
+var _preprocessEvent = function(event) {
+ var element = event.target || _currentElement || null;
+
+ var sourceIsSwf = event._source === "swf";
+ delete event._source;
+
+ var flashErrorNames = [
+ "flash-disabled",
+ "flash-outdated",
+ "flash-unavailable",
+ "flash-deactivated",
+ "flash-overdue"
+ ];
+
+ switch (event.type) {
+ case "error":
+ if (flashErrorNames.indexOf(event.name) !== -1) {
+ _extend(_flashState, {
+ disabled: event.name === "flash-disabled",
+ outdated: event.name === "flash-outdated",
+ unavailable: event.name === "flash-unavailable",
+ deactivated: event.name === "flash-deactivated",
+ overdue: event.name === "flash-overdue",
+ ready: false
+ });
+ }
+ break;
+
+ case "ready":
+ var wasDeactivated = _flashState.deactivated === true;
+ _extend(_flashState, {
+ disabled: false,
+ outdated: false,
+ unavailable: false,
+ deactivated: false,
+ overdue: wasDeactivated,
+ ready: !wasDeactivated
+ });
+ break;
+
+ case "beforecopy":
+ _copyTarget = element;
+ break;
+
+ case "copy":
+ var textContent,
+ htmlContent,
+ targetEl = event.relatedTarget;
+ if (
+ !(_clipData["text/html"] || _clipData["text/plain"]) &&
+ targetEl &&
+ (htmlContent = targetEl.value || targetEl.outerHTML || targetEl.innerHTML) &&
+ (textContent = targetEl.value || targetEl.textContent || targetEl.innerText)
+ ) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ if (htmlContent !== textContent) {
+ event.clipboardData.setData("text/html", htmlContent);
+ }
+ }
+ else if (!_clipData["text/plain"] && event.target && (textContent = event.target.getAttribute("data-clipboard-text"))) {
+ event.clipboardData.clearData();
+ event.clipboardData.setData("text/plain", textContent);
+ }
+ break;
+
+ case "aftercopy":
+ // If the copy has [or should have] occurred, clear out all of the data
+ ZeroClipboard.clearData();
+
+ // Focus the context back on the trigger element (blur the Flash element)
+ if (element && element !== _safeActiveElement() && element.focus) {
+ element.focus();
+ }
+ break;
+
+ case "_mouseover":
+ // Set this as the new currently active element
+ ZeroClipboard.focus(element);
+
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (
+ element &&
+ element !== event.relatedTarget &&
+ !_containedBy(event.relatedTarget, element)
+ ) {
+ _fireMouseEvent(
+ _extend({}, event, {
+ type: "mouseenter",
+ bubbles: false,
+ cancelable: false
+ })
+ );
+ }
+
+ _fireMouseEvent(
+ _extend({}, event, {
+ type: "mouseover"
+ })
+ );
+ }
+ break;
+
+ case "_mouseout":
+ // If the mouse is moving to any other element, deactivate and...
+ ZeroClipboard.blur();
+
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ if (
+ element &&
+ element !== event.relatedTarget &&
+ !_containedBy(event.relatedTarget, element)
+ ) {
+ _fireMouseEvent(
+ _extend({}, event, {
+ type: "mouseleave",
+ bubbles: false,
+ cancelable: false
+ })
+ );
+ }
+
+ _fireMouseEvent(
+ _extend({}, event, {
+ type: "mouseout"
+ })
+ );
+ }
+ break;
+
+ case "_mousedown":
+ _addClass(element, _globalConfig.activeClass);
+
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, { type: event.type.slice(1) }));
+ }
+ break;
+
+ case "_mouseup":
+ _removeClass(element, _globalConfig.activeClass);
+
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, { type: event.type.slice(1) }));
+ }
+ break;
+
+ case "_click":
+ _copyTarget = null;
+
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, { type: event.type.slice(1) }));
+ }
+ break;
+
+ case "_mousemove":
+ if (_globalConfig.bubbleEvents === true && sourceIsSwf) {
+ _fireMouseEvent(_extend({}, event, { type: event.type.slice(1) }));
+ }
+ break;
+ } // end `switch`
+
+ // Return a flag to indicate that this event should stop being processed
+ if (/^_(?:click|mouse(?:over|out|down|up|move))$/.test(event.type)) {
+ return true;
+ }
+};
+
+
+/**
+ * Dispatch a synthetic MouseEvent.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _fireMouseEvent = function(event) {
+ if (!(event && typeof event.type === "string" && event)) {
+ return;
+ }
+
+ var e,
+ target = event.target || null,
+ doc = (target && target.ownerDocument) || _document,
+ defaults = {
+ view: doc.defaultView || _window,
+ canBubble: true,
+ cancelable: true,
+ detail: event.type === "click" ? 1 : 0,
+ button:
+ typeof event.which === "number" ?
+ (event.which - 1) :
+ (
+ typeof event.button === "number" ?
+ event.button :
+ (doc.createEvent ? 0 : 1)
+ )
+ },
+ // Update the Event data to its final state
+ args = _extend(defaults, event);
+
+ if (!target) {
+ return;
+ }
+
+ // Create and fire the MouseEvent
+ if (doc.createEvent && target.dispatchEvent) {
+ 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
+ ];
+ e = doc.createEvent("MouseEvents");
+ if (e.initMouseEvent) {
+ e.initMouseEvent.apply(e, args);
+ e._source = "js";
+ target.dispatchEvent(e);
+ }
+ }
+};
+
+
+/**
+ * Create the HTML bridge element to embed the Flash object into.
+ * @private
+ */
+var _createHtmlBridge = function() {
+ var container = _document.createElement("div");
+ container.id = _globalConfig.containerId;
+ container.className = _globalConfig.containerClass;
+ container.style.position = "absolute";
+ container.style.left = "0px";
+ container.style.top = "-9999px";
+ container.style.width = "1px";
+ container.style.height = "1px";
+ container.style.zIndex = "" + _getSafeZIndex(_globalConfig.zIndex);
+ return container;
+};
+
+
+/**
+ * Get the HTML element container that wraps the Flash bridge object/element.
+ * @private
+ */
+var _getHtmlBridge = function(flashBridge) {
+ var htmlBridge = flashBridge && flashBridge.parentNode;
+ while (htmlBridge && htmlBridge.nodeName === "OBJECT" && htmlBridge.parentNode) {
+ htmlBridge = htmlBridge.parentNode;
+ }
+ return htmlBridge || null;
+};
+
+
+/**
+ * Create the SWF object.
+ *
+ * @returns The SWF object reference.
+ * @private
+ */
+var _embedSwf = function() {
+ var len,
+ flashBridge = _flashState.bridge,
+ container = _getHtmlBridge(flashBridge);
+
+ if (!flashBridge) {
+ // Set `allowScriptAccess`/`allowNetworking` based on `trustedDomains` and `window.location.host` vs. `swfPath`
+ var allowScriptAccess = _determineScriptAccess(_window.location.host, _globalConfig);
+ var allowNetworking = allowScriptAccess === "never" ? "none" : "all";
+
+ // Prepare the FlashVars and cache-busting query param
+ var flashvars = _vars(_globalConfig);
+ var swfUrl = _globalConfig.swfPath + _cacheBust(_globalConfig.swfPath, _globalConfig);
+
+ // Create the outer container
+ container = _createHtmlBridge();
+
+ // Create a to-be-replaced child node
+ var divToBeReplaced = _document.createElement("div");
+ container.appendChild(divToBeReplaced);
+
+ // Add this outer container (and its to-be-replaced child node) to the DOM in advance in order
+ // to avoid Flash quirks in various browsers, e.g. https://github.com/zeroclipboard/zeroclipboard/issues/204
+ _document.body.appendChild(container);
+
+ // Create the actual Flash object's shell
+ var tmpDiv = _document.createElement("div");
+ // The object element plus its movie source URL both MUST be created together.
+ // Other attributes and child nodes can techncially be added afterward.
+ // Hybrid of Flash Satay markup is from Ambience:
+ // - Flash Satay version: http://alistapart.com/article/flashsatay
+ // - Ambience version: http://www.ambience.sk/flash-valid.htm
+ var oldIE = _flashState.pluginType === "activex";
+ /*jshint quotmark:single */
+ tmpDiv.innerHTML =
+ '';
+ /*jshint quotmark:double */
+ flashBridge = tmpDiv.firstChild;
+ tmpDiv = null;
+
+ // Store a reference to the `ZeroClipboard` object as a DOM property
+ // on the ZeroClipboard-owned "object" element. This will help us
+ // easily avoid issues with AMD/CommonJS loaders that don't have
+ // a global `ZeroClipboard` reliably available.
+ _unwrap(flashBridge).ZeroClipboard = ZeroClipboard;
+
+ // NOTE: Using `replaceChild` is very important!
+ // - https://github.com/swfobject/swfobject/blob/562fe358216edbb36445aa62f817c1a56252950c/swfobject/src/swfobject.js
+ // - http://pipwerks.com/2011/05/30/using-the-object-element-to-dynamically-embed-flash-swfs-in-internet-explorer/
+ container.replaceChild(flashBridge, divToBeReplaced);
+ }
+
+ if (!flashBridge) {
+ flashBridge = _document[_globalConfig.swfObjectId];
+ if (flashBridge && (len = flashBridge.length)) {
+ flashBridge = flashBridge[len - 1];
+ }
+ if (!flashBridge && container) {
+ flashBridge = container.firstChild;
+ }
+ }
+
+ _flashState.bridge = flashBridge || null;
+
+ return flashBridge;
+};
+
+
+/**
+ * Destroy the SWF object.
+ * @private
+ */
+var _unembedSwf = function() {
+ // Remove the Flash bridge
+ var flashBridge = _flashState.bridge;
+ if (flashBridge) {
+ var htmlBridge = _getHtmlBridge(flashBridge);
+ if (htmlBridge) {
+ // Some extra caution is necessary to prevent Flash from causing memory leaks in oldIE
+ // NOTE: Removing the SWF in IE may not be completed synchronously
+ if (_flashState.pluginType === "activex" && "readyState" in flashBridge) {
+ flashBridge.style.display = "none";
+ (function removeSwfFromIE() {
+ if (flashBridge.readyState === 4) {
+ // This step prevents memory leaks in oldIE
+ for (var prop in flashBridge) {
+ if (typeof flashBridge[prop] === "function") {
+ flashBridge[prop] = null;
+ }
+ }
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ }
+ else {
+ _setTimeout(removeSwfFromIE, 10);
+ }
+ })();
+ }
+ else {
+ if (flashBridge.parentNode) {
+ flashBridge.parentNode.removeChild(flashBridge);
+ }
+ if (htmlBridge.parentNode) {
+ htmlBridge.parentNode.removeChild(htmlBridge);
+ }
+ }
+ }
+
+ _flashState.ready = null;
+ _flashState.bridge = null;
+ // Reset the `deactivated` status in case the user wants to "try again", e.g. after receiving
+ // an `overdueFlash` event
+ _flashState.deactivated = null;
+ }
+};
+
+
+/**
+ * Map the data format names of the "clipData" to Flash-friendly names.
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+var _mapClipDataToFlash = function(clipData) {
+ var newClipData = {},
+ formatMap = {};
+ if (!(typeof clipData === "object" && clipData)) {
+ return;
+ }
+
+ for (var dataFormat in clipData) {
+ if (dataFormat && _hasOwn.call(clipData, dataFormat) && typeof clipData[dataFormat] === "string" && clipData[dataFormat]) {
+ // Standardize the allowed clipboard segment names to reduce complexity on the Flash side
+ switch (dataFormat.toLowerCase()) {
+ case "text/plain":
+ case "text":
+ case "air:text":
+ case "flash:text":
+ newClipData.text = clipData[dataFormat];
+ formatMap.text = dataFormat;
+ break;
+ case "text/html":
+ case "html":
+ case "air:html":
+ case "flash:html":
+ newClipData.html = clipData[dataFormat];
+ formatMap.html = dataFormat;
+ break;
+ case "application/rtf":
+ case "text/rtf":
+ case "rtf":
+ case "richtext":
+ case "air:rtf":
+ case "flash:rtf":
+ newClipData.rtf = clipData[dataFormat];
+ formatMap.rtf = dataFormat;
+ break;
+ default:
+ // Just ignore it: the Flash clipboard cannot handle any other formats
+ break;
+ }
+ }
+ }
+ return {
+ data: newClipData,
+ formatMap: formatMap
+ };
+};
+
+
+/**
+ * Map the data format names from Flash-friendly names back to their original "clipData" names (via a format mapping).
+ *
+ * @returns A new transformed object.
+ * @private
+ */
+var _mapClipResultsFromFlash = function(clipResults, formatMap) {
+ if (!(typeof clipResults === "object" && clipResults && typeof formatMap === "object" && formatMap)) {
+ return clipResults;
+ }
+
+ var newResults = {};
+
+ for (var prop in clipResults) {
+ if (_hasOwn.call(clipResults, prop)) {
+ if (prop !== "success" && prop !== "data") {
+ newResults[prop] = clipResults[prop];
+ continue;
+ }
+
+ newResults[prop] = {};
+
+ // Standardize the allowed clipboard segment names to reduce complexity on the Flash side
+ var tmpHash = clipResults[prop];
+ for (var dataFormat in tmpHash) {
+ if (dataFormat && _hasOwn.call(tmpHash, dataFormat) && _hasOwn.call(formatMap, dataFormat)) {
+ newResults[prop][formatMap[dataFormat]] = tmpHash[dataFormat];
+ }
+ }
+ }
+ }
+ return newResults;
+};
+
+
+/**
+ * Will look at a path, and will create a "?noCache={time}" or "&noCache={time}"
+ * query param string to return. Does NOT append that string to the original path.
+ * This is useful because ExternalInterface often breaks when a Flash SWF is cached.
+ *
+ * @returns The `noCache` query param with necessary "?"/"&" prefix.
+ * @private
+ */
+var _cacheBust = function(path, options) {
+ var cacheBust = options == null || (options && options.cacheBust === true);
+ if (cacheBust) {
+ return (path.indexOf("?") === -1 ? "?" : "&") + "noCache=" + _now();
+ }
+ else {
+ return "";
+ }
+};
+
+
+/**
+ * Creates a query string for the FlashVars param.
+ * Does NOT include the cache-busting query param.
+ *
+ * @returns FlashVars query string
+ * @private
+ */
+var _vars = function(options) {
+ var i, len, domain, domains,
+ str = "",
+ trustedOriginsExpanded = [];
+
+ if (options.trustedDomains) {
+ if (typeof options.trustedDomains === "string") {
+ domains = [options.trustedDomains];
+ }
+ else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) {
+ domains = options.trustedDomains;
+ }
+ }
+ if (domains && domains.length) {
+ for (i = 0, len = domains.length; i < len; i++) {
+ if (_hasOwn.call(domains, i) && domains[i] && typeof domains[i] === "string") {
+ domain = _extractDomain(domains[i]);
+
+ if (!domain) {
+ continue;
+ }
+
+ // If we encounter a wildcard, ignore everything else as they are irrelevant
+ if (domain === "*") {
+ trustedOriginsExpanded.length = 0;
+ trustedOriginsExpanded.push(domain);
+ break;
+ }
+
+ // Add the domain, relative protocol + domain, and absolute protocol + domain ("origin")
+ // because Flash Player seems to handle these inconsistently (perhaps in different versions)
+ trustedOriginsExpanded.push.apply(
+ trustedOriginsExpanded,
+ [
+ domain,
+ "//" + domain,
+ _window.location.protocol + "//" + domain
+ ]
+ );
+ }
+ }
+ }
+
+ if (trustedOriginsExpanded.length) {
+ str += "trustedOrigins=" + _encodeURIComponent(trustedOriginsExpanded.join(","));
+ }
+
+ if (options.forceEnhancedClipboard === true) {
+ str += (str ? "&" : "") + "forceEnhancedClipboard=true";
+ }
+
+ if (typeof options.swfObjectId === "string" && options.swfObjectId) {
+ str += (str ? "&" : "") + "swfObjectId=" + _encodeURIComponent(options.swfObjectId);
+ }
+
+ return str;
+};
+
+
+/**
+ * Extract the domain (e.g. "github.com") from an origin (e.g. "https://github.com") or
+ * URL (e.g. "https://github.com/zeroclipboard/zeroclipboard/").
+ *
+ * @returns the domain
+ * @private
+ */
+var _extractDomain = function(originOrUrl) {
+ if (originOrUrl == null || originOrUrl === "") {
+ return null;
+ }
+
+ // Trim
+ originOrUrl = originOrUrl.replace(/^\s+|\s+$/g, "");
+ if (originOrUrl === "") {
+ return null;
+ }
+
+ // Strip the protocol, if any was provided
+ var protocolIndex = originOrUrl.indexOf("//");
+ originOrUrl = protocolIndex === -1 ? originOrUrl : originOrUrl.slice(protocolIndex + 2);
+
+ // Strip the path, if any was provided
+ var pathIndex = originOrUrl.indexOf("/");
+ originOrUrl = pathIndex === -1 ? originOrUrl : protocolIndex === -1 || pathIndex === 0 ? null : originOrUrl.slice(0, pathIndex);
+
+ if (originOrUrl && originOrUrl.slice(-4).toLowerCase() === ".swf") {
+ return null;
+ }
+ return originOrUrl || null;
+};
+
+
+/**
+ * Set `allowScriptAccess` based on `trustedDomains` and `window.location.host` vs. `swfPath`.
+ *
+ * @returns The appropriate script access level.
+ * @private
+ */
+var _determineScriptAccess = (function() {
+ var _extractAllDomains = function(origins) {
+ var i, len, tmp,
+ resultsArray = [];
+ if (typeof origins === "string") {
+ origins = [origins];
+ }
+ if (!(typeof origins === "object" && origins && typeof origins.length === "number")) {
+ return resultsArray;
+ }
+ for (i = 0, len = origins.length; i < len; i++) {
+ if (_hasOwn.call(origins, i) && (tmp = _extractDomain(origins[i]))) {
+ if (tmp === "*") {
+ resultsArray.length = 0;
+ resultsArray.push("*");
+ break;
+ }
+ if (resultsArray.indexOf(tmp) === -1) {
+ resultsArray.push(tmp);
+ }
+ }
+ }
+ return resultsArray;
+ };
+
+ return function(currentDomain, configOptions) {
+ // Get SWF domain
+ var swfDomain = _extractDomain(configOptions.swfPath);
+ if (swfDomain === null) {
+ swfDomain = currentDomain;
+ }
+ // Get all trusted domains
+ var trustedDomains = _extractAllDomains(configOptions.trustedDomains);
+
+ var len = trustedDomains.length;
+ if (len > 0) {
+ if (len === 1 && trustedDomains[0] === "*") {
+ return "always";
+ }
+ if (trustedDomains.indexOf(currentDomain) !== -1) {
+ if (len === 1 && currentDomain === swfDomain) {
+ return "sameDomain";
+ }
+ return "always";
+ }
+ }
+ return "never";
+ };
+})();
+
+
+/**
+ * Get the currently active/focused DOM element.
+ *
+ * @returns the currently active/focused element, or `null`
+ * @private
+ */
+var _safeActiveElement = function() {
+ try {
+ return _document.activeElement;
+ }
+ catch (err) {
+ return null;
+ }
+};
+
+
+/**
+ * Add a class to an element, if it doesn't already have it.
+ *
+ * @returns The element, with its new class added.
+ * @private
+ */
+var _addClass = function(element, value) {
+
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+
+ // If the element has `classList`
+ if (element.classList) {
+ if (!element.classList.contains(value)) {
+ element.classList.add(value);
+ }
+ return element;
+ }
+
+ if (value && typeof value === "string") {
+ var classNames = (value || "").split(/\s+/);
+
+ if (element.nodeType === 1) {
+ if (!element.className) {
+ element.className = value;
+ }
+ else {
+ var className = " " + element.className + " ", setClass = element.className;
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ if (className.indexOf(" " + classNames[c] + " ") < 0) {
+ setClass += " " + classNames[c];
+ }
+ }
+ // jank trim
+ element.className = setClass.replace(/^\s+|\s+$/g, "");
+ }
+ }
+ }
+
+ return element;
+};
+
+
+/**
+ * Remove a class from an element, if it has it.
+ *
+ * @returns The element, with its class removed.
+ * @private
+ */
+var _removeClass = function(element, value) {
+
+ if (!element || element.nodeType !== 1) {
+ return element;
+ }
+
+ // If the element has `classList`
+ if (element.classList) {
+ if (element.classList.contains(value)) {
+ element.classList.remove(value);
+ }
+ return element;
+ }
+
+ if (typeof value === "string" && value) {
+ var classNames = value.split(/\s+/);
+
+ if (element.nodeType === 1 && element.className) {
+ var className = (" " + element.className + " ").replace(/[\n\t]/g, " ");
+ for (var c = 0, cl = classNames.length; c < cl; c++) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ // jank trim
+ element.className = className.replace(/^\s+|\s+$/g, "");
+ }
+ }
+
+ return element;
+};
+
+
+/**
+ * Attempt to interpret the element's CSS styling. If `prop` is `"cursor"`,
+ * then we assume that it should be a hand ("pointer") cursor if the element
+ * is an anchor element ("a" tag).
+ *
+ * @returns The computed style property.
+ * @private
+ */
+var _getStyle = function(el, prop) {
+ var value = _window.getComputedStyle(el, null).getPropertyValue(prop);
+ if (prop === "cursor") {
+ if (!value || value === "auto") {
+ if (el.nodeName === "A") {
+ return "pointer";
+ }
+ }
+ }
+
+ return value;
+};
+
+
+/**
+ * Get the zoom factor of the browser. Always returns `1.0`, except at
+ * non-default zoom levels in IE<8 and some older versions of WebKit.
+ *
+ * @returns Floating unit percentage of the zoom factor (e.g. 150% = `1.5`).
+ * @private
+ */
+var _getZoomFactor = function() {
+ var rect, physicalWidth, logicalWidth,
+ zoomFactor = 1;
+ if (typeof _document.body.getBoundingClientRect === "function") {
+ // rect is only in physical pixels in IE<8
+ rect = _document.body.getBoundingClientRect();
+ physicalWidth = rect.right - rect.left;
+ logicalWidth = _document.body.offsetWidth;
+
+ zoomFactor = _round((physicalWidth / logicalWidth) * 100) / 100;
+ }
+ return zoomFactor;
+};
+
+
+/**
+ * Get the DOM positioning info of an element.
+ *
+ * @returns Object containing the element's position, width, and height.
+ * @private
+ */
+var _getDOMObjectPosition = function(obj) {
+ // get absolute coordinates for dom element
+ var info = {
+ left: 0,
+ top: 0,
+ width: 0,
+ height: 0
+ };
+
+ // Use getBoundingClientRect where available (almost everywhere).
+ // See: http://www.quirksmode.org/dom/w3c_cssom.html
+ if (obj.getBoundingClientRect) {
+ // compute left / top offset (works for `position:fixed`, too!)
+ var rect = obj.getBoundingClientRect();
+ var pageXOffset, pageYOffset, zoomFactor;
+
+ // IE<9 doesn't support `pageXOffset`/`pageXOffset`
+ if ("pageXOffset" in _window && "pageYOffset" in _window) {
+ pageXOffset = _window.pageXOffset;
+ pageYOffset = _window.pageYOffset;
+ }
+ else {
+ zoomFactor = _getZoomFactor();
+ pageXOffset = _round(_document.documentElement.scrollLeft / zoomFactor);
+ pageYOffset = _round(_document.documentElement.scrollTop / zoomFactor);
+ }
+
+ // `clientLeft`/`clientTop` are to fix IE's 2px offset in standards mode
+ var leftBorderWidth = _document.documentElement.clientLeft || 0;
+ var topBorderWidth = _document.documentElement.clientTop || 0;
+
+ info.left = rect.left + pageXOffset - leftBorderWidth;
+ info.top = rect.top + pageYOffset - topBorderWidth;
+ info.width = "width" in rect ? rect.width : rect.right - rect.left;
+ info.height = "height" in rect ? rect.height : rect.bottom - rect.top;
+ }
+
+ return info;
+};
+
+
+/**
+ * Reposition the Flash object to cover the currently activated element.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _reposition = function() {
+ var htmlBridge;
+ // If there is no `_currentElement`, skip it
+ if (_currentElement && (htmlBridge = _getHtmlBridge(_flashState.bridge))) {
+ var pos = _getDOMObjectPosition(_currentElement);
+ _extend(htmlBridge.style, {
+ width: pos.width + "px",
+ height: pos.height + "px",
+ top: pos.top + "px",
+ left: pos.left + "px",
+ zIndex: "" + _getSafeZIndex(_globalConfig.zIndex)
+ });
+ }
+};
+
+
+/**
+ * Sends a signal to the Flash object to display the hand cursor if `true`.
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _setHandCursor = function(enabled) {
+ if (_flashState.ready === true) {
+ if (_flashState.bridge && typeof _flashState.bridge.setHandCursor === "function") {
+ _flashState.bridge.setHandCursor(enabled);
+ }
+ else {
+ _flashState.ready = false;
+ }
+ }
+};
+
+
+/**
+ * Get a safe value for `zIndex`
+ *
+ * @returns an integer, or "auto"
+ * @private
+ */
+var _getSafeZIndex = function(val) {
+ if (/^(?:auto|inherit)$/.test(val)) {
+ return val;
+ }
+
+ var zIndex;
+ if (typeof val === "number" && !_isNaN(val)) {
+ zIndex = val;
+ }
+ else if (typeof val === "string") {
+ zIndex = _getSafeZIndex(_parseInt(val, 10));
+ }
+ return typeof zIndex === "number" ? zIndex : "auto";
+};
+
+
+/**
+ * Detect the Flash Player status, version, and plugin type.
+ *
+ * @see {@link https://code.google.com/p/doctype-mirror/wiki/ArticleDetectFlash#The_code}
+ * @see {@link http://stackoverflow.com/questions/12866060/detecting-pepper-ppapi-flash-with-javascript}
+ *
+ * @returns `undefined`
+ * @private
+ */
+var _detectFlashSupport = function(ActiveXObject) {
+ var plugin, ax, mimeType,
+ hasFlash = false,
+ isActiveX = false,
+ isPPAPI = false,
+ flashVersion = "";
+
+ /**
+ * Derived from Apple's suggested sniffer.
+ * @param {String} desc e.g. "Shockwave Flash 7.0 r61"
+ * @returns {String} "7.0.61"
+ * @private
+ */
+ function parseFlashVersion(desc) {
+ var matches = desc.match(/[\d]+/g);
+ matches.length = 3; // To standardize IE vs FF
+ return matches.join(".");
+ }
+
+ function isPepperFlash(flashPlayerFileName) {
+ return !!flashPlayerFileName &&
+ (flashPlayerFileName = flashPlayerFileName.toLowerCase()) &&
+ (
+ /^(pepflashplayer\.dll|libpepflashplayer\.so|pepperflashplayer\.plugin)$/.test(flashPlayerFileName) ||
+ flashPlayerFileName.slice(-13) === "chrome.plugin"
+ );
+ }
+
+ function inspectPlugin(plugin) {
+ if (plugin) {
+ hasFlash = true;
+ if (plugin.version) {
+ flashVersion = parseFlashVersion(plugin.version);
+ }
+ if (!flashVersion && plugin.description) {
+ flashVersion = parseFlashVersion(plugin.description);
+ }
+ if (plugin.filename) {
+ isPPAPI = isPepperFlash(plugin.filename);
+ }
+ }
+ }
+
+ if (_navigator.plugins && _navigator.plugins.length) {
+ plugin = _navigator.plugins["Shockwave Flash"];
+ inspectPlugin(plugin);
+
+ if (_navigator.plugins["Shockwave Flash 2.0"]) {
+ hasFlash = true;
+ flashVersion = "2.0.0.11";
+ }
+ }
+ else if (_navigator.mimeTypes && _navigator.mimeTypes.length) {
+ mimeType = _navigator.mimeTypes["application/x-shockwave-flash"];
+ plugin = mimeType && mimeType.enabledPlugin;
+ inspectPlugin(plugin);
+ }
+ else if (typeof ActiveXObject !== "undefined") {
+ //
+ // Using IE < 11
+ //
+ isActiveX = true;
+
+ try {
+ // Try 7 first, since we know we can use GetVariable with it
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ }
+ catch (e1) {
+ // Try 6 next, some versions are known to crash with GetVariable calls
+ try {
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+ hasFlash = true;
+ flashVersion = "6.0.21"; // First public version of Flash 6
+ }
+ catch (e2) {
+ try {
+ // Try the default ActiveX
+ ax = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ hasFlash = true;
+ flashVersion = parseFlashVersion(ax.GetVariable("$version"));
+ }
+ catch (e3) {
+ // No flash
+ isActiveX = false;
+ }
+ }
+ }
+ }
+
+ _flashState.disabled = hasFlash !== true;
+ _flashState.outdated = flashVersion && (_parseFloat(flashVersion) < _parseFloat(_minimumFlashVersion));
+ _flashState.version = flashVersion || "0.0.0";
+ _flashState.pluginType = isPPAPI ? "pepper" : (isActiveX ? "activex" : (hasFlash ? "netscape" : "unknown"));
+};
+
+
+/**
+ * Invoke the Flash detection algorithms immediately upon inclusion so we're not waiting later.
+ */
+_detectFlashSupport(_ActiveXObject);
diff --git a/showcase/js/zeroclipboard/src/js/core/state.js b/showcase/js/zeroclipboard/src/js/core/state.js
new file mode 100755
index 0000000..9842c8f
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/core/state.js
@@ -0,0 +1,147 @@
+/**
+ * Keep track of the state of the Flash object.
+ * @private
+ */
+var _flashState = {
+ // Flash object reference
+ bridge: null,
+
+ // Flash metadata
+ version: "0.0.0",
+ pluginType: "unknown",
+
+ // Flash SWF state
+ disabled: null,
+ outdated: null,
+ unavailable: null,
+ deactivated: null,
+ overdue: null,
+ ready: null
+};
+
+
+/**
+ * The minimum Flash Player version required to use ZeroClipboard completely.
+ * @readonly
+ * @private
+ */
+var _minimumFlashVersion = "11.0.0";
+
+
+/**
+ * Keep track of all event listener registrations.
+ * @private
+ */
+var _handlers = {};
+
+
+/**
+ * Keep track of the currently activated element.
+ * @private
+ */
+var _currentElement;
+
+
+/**
+ * Keep track of the element that was activated when a `copy` process started.
+ * @private
+ */
+var _copyTarget;
+
+
+/**
+ * Keep track of data for the pending clipboard transaction.
+ * @private
+ */
+var _clipData = {};
+
+
+/**
+ * Keep track of data formats for the pending clipboard transaction.
+ * @private
+ */
+var _clipDataFormatMap = null;
+
+
+/**
+ * The `message` store for events
+ * @private
+ */
+var _eventMessages = {
+ "ready": "Flash communication is established",
+ "error": {
+ "flash-disabled": "Flash is disabled or not installed",
+ "flash-outdated": "Flash is too outdated to support ZeroClipboard",
+ "flash-unavailable": "Flash is unable to communicate bidirectionally with JavaScript",
+ "flash-deactivated": "Flash is too outdated for your browser and/or is configured as click-to-activate",
+ "flash-overdue": "Flash communication was established but NOT within the acceptable time limit"
+ }
+};
+
+
+/**
+ * ZeroClipboard configuration defaults for the Core module.
+ * @private
+ */
+var _globalConfig = {
+
+ // SWF URL, relative to the page. Default value will be "ZeroClipboard.swf"
+ // under the same path as the ZeroClipboard JS file.
+ swfPath: _getDefaultSwfPath(),
+
+ // SWF inbound scripting policy: page domains that the SWF should trust.
+ // (single string, or array of strings)
+ trustedDomains: window.location.host ? [window.location.host] : [],
+
+ // Include a "noCache" query parameter on requests for the SWF.
+ cacheBust: true,
+
+ // Enable use of the fancy "Desktop" clipboard, even on Linux where it is
+ // known to suck.
+ forceEnhancedClipboard: false,
+
+ // How many milliseconds to wait for the Flash SWF to load and respond before assuming that
+ // Flash is deactivated (e.g. click-to-play) in the user's browser. If you don't care about
+ // how long it takes to load the SWF, you can set this to `null`.
+ flashLoadTimeout: 30000,
+
+ // Setting this to `false` would allow users to handle calling `ZeroClipboard.focus(...);`
+ // themselves instead of relying on our per-element `mouseover` handler.
+ autoActivate: true,
+
+ // Bubble synthetic events in JavaScript after they are received by the Flash object.
+ bubbleEvents: true,
+
+ // Sets the ID of the `div` encapsulating the Flash object.
+ // Value is validated against the HTML4 spec for `ID` tokens.
+ containerId: "global-zeroclipboard-html-bridge",
+
+ // Sets the class of the `div` encapsulating the Flash object.
+ containerClass: "global-zeroclipboard-container",
+
+ // Sets the ID and name of the Flash `object` element.
+ // Value is validated against the HTML4 spec for `ID` and `Name` tokens.
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+
+ // The class used to indicate that a clipped element is being hovered over.
+ hoverClass: "zeroclipboard-is-hover",
+
+ // The class used to indicate that a clipped element is active (is being clicked).
+ activeClass: "zeroclipboard-is-active",
+
+
+
+ // Forcibly set the hand cursor ("pointer") for all clipped elements.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ forceHandCursor: false,
+
+ // Sets the title of the `div` encapsulating the Flash object.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ title: null,
+
+ // The z-index used by the Flash object.
+ // Max value (32-bit): 2147483647.
+ // IMPORTANT: This configuration value CAN be modified while a SWF is actively embedded.
+ zIndex: 999999999
+
+};
diff --git a/showcase/js/zeroclipboard/src/js/end.js b/showcase/js/zeroclipboard/src/js/end.js
new file mode 100755
index 0000000..9ff87ef
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/end.js
@@ -0,0 +1,21 @@
+
+
+// The AMDJS logic branch is evaluated first to avoid potential confusion over
+// the CommonJS syntactical sugar offered by AMD.
+if (typeof define === "function" && define.amd) {
+ define(function() {
+ return ZeroClipboard;
+ });
+}
+else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) {
+ // CommonJS module loaders....
+ module.exports = ZeroClipboard;
+}
+else {
+ window.ZeroClipboard = ZeroClipboard;
+}
+
+})((function() {
+ /*jshint strict: false */
+ return this || window;
+})());
diff --git a/showcase/js/zeroclipboard/src/js/shared/private.js b/showcase/js/zeroclipboard/src/js/shared/private.js
new file mode 100755
index 0000000..9d44c2d
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/shared/private.js
@@ -0,0 +1,317 @@
+/**
+ * Convert an `arguments` object into an Array.
+ *
+ * @returns The arguments as an Array
+ * @private
+ */
+var _args = function(argumentsObj) {
+ return _slice.call(argumentsObj, 0);
+};
+
+
+/**
+ * Shallow-copy the owned, enumerable properties of one object over to another, similar to jQuery's `$.extend`.
+ *
+ * @returns The target object, augmented
+ * @private
+ */
+var _extend = function() {
+ var i, len, arg, prop, src, copy,
+ args = _args(arguments),
+ target = args[0] || {};
+
+ for (i = 1, len = args.length; i < len; i++) {
+ // Only deal with non-null/undefined values
+ if ((arg = args[i]) != null) {
+ // Extend the base object
+ for (prop in arg) {
+ if (_hasOwn.call(arg, prop)) {
+ src = target[prop];
+ copy = arg[prop];
+
+ // Prevent never-ending loops and copying `undefined` valeus
+ if (target !== copy && copy !== undefined) {
+ target[prop] = copy;
+ }
+ }
+ }
+ }
+ }
+ return target;
+};
+
+
+/**
+ * Return a deep copy of the source object or array.
+ *
+ * @returns Object or Array
+ * @private
+ */
+var _deepCopy = function(source) {
+ var copy, i, len, prop;
+
+ // If not a non-null object, just return the original
+ if (typeof source !== "object" || source == null) {
+ copy = source;
+ }
+ // If an Array, iterate and recurse
+ else if (typeof source.length === "number") {
+ copy = [];
+ for (i = 0, len = source.length; i < len; i++) {
+ // Skip empty indices in sparse arrays
+ if (_hasOwn.call(source, i)) {
+ // Recurse
+ copy[i] = _deepCopy(source[i]);
+ }
+ }
+ }
+ // If an Object, enumerate and recurse
+ else {
+ copy = {};
+ for (prop in source) {
+ // Skip prototype properties
+ if (_hasOwn.call(source, prop)) {
+ copy[prop] = _deepCopy(source[prop]);
+ }
+ }
+ }
+
+ return copy;
+};
+
+
+/**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to keep.
+ * The inverse of `_omit`, mostly. The big difference is that these properties do NOT need to be enumerable to
+ * be kept.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+var _pick = function(obj, keys) {
+ var newObj = {};
+ for (var i = 0, len = keys.length; i < len; i++) {
+ if (keys[i] in obj) {
+ newObj[keys[i]] = obj[keys[i]];
+ }
+ }
+ return newObj;
+};
+
+
+/**
+ * Makes a shallow copy of `obj` (like `_extend`) but filters its properties based on a list of `keys` to omit.
+ * The inverse of `_pick`.
+ *
+ * @returns A new filtered object.
+ * @private
+ */
+var _omit = function(obj, keys) {
+ var newObj = {};
+ for (var prop in obj) {
+ if (keys.indexOf(prop) === -1) {
+ newObj[prop] = obj[prop];
+ }
+ }
+ return newObj;
+};
+
+
+/**
+ * Remove all owned, enumerable properties from an object.
+ *
+ * @returns The original object without its owned, enumerable properties.
+ * @private
+ */
+var _deleteOwnProperties = function(obj) {
+ if (obj) {
+ for (var prop in obj) {
+ if (_hasOwn.call(obj, prop)) {
+ delete obj[prop];
+ }
+ }
+ }
+ return obj;
+};
+
+
+/**
+ * Determine if an element is contained within another element.
+ *
+ * @returns Boolean
+ * @private
+ */
+var _containedBy = function(el, ancestorEl) {
+ 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)
+ )
+ ) {
+ do {
+ if (el === ancestorEl) {
+ return true;
+ }
+ el = el.parentNode;
+ }
+ while (el);
+ }
+
+ return false;
+};
+
+
+/**
+ * Get the URL path's parent directory.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+var _getDirPathOfUrl = function(url) {
+ var dir;
+ if (typeof url === "string" && url) {
+ dir = url.split("#")[0].split("?")[0];
+ dir = url.slice(0, url.lastIndexOf("/") + 1);
+ }
+ return dir;
+};
+
+
+/**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+var _getCurrentScriptUrlFromErrorStack = function(stack) {
+ var url, matches;
+ if (typeof stack === "string" && stack) {
+ matches = stack.match(/^(?:|[^:@]*@|.+\)@(?=http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ }
+ else {
+ matches = stack.match(/\)@((?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?/);
+ if (matches && matches[1]) {
+ url = matches[1];
+ }
+ }
+ }
+ return url;
+};
+
+
+/**
+ * Get the current script's URL by throwing an `Error` and analyzing it.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+var _getCurrentScriptUrlFromError = function() {
+ /*jshint newcap:false */
+ var url, err;
+ try {
+ throw new _Error();
+ }
+ catch (e) {
+ err = e;
+ }
+
+ if (err) {
+ url = err.sourceURL || err.fileName || _getCurrentScriptUrlFromErrorStack(err.stack);
+ }
+ return url;
+};
+
+
+/**
+ * Get the current script's URL.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+var _getCurrentScriptUrl = function() {
+ var jsPath, scripts, i;
+
+ // Try to leverage the `currentScript` feature
+ if (_document.currentScript && (jsPath = _document.currentScript.src)) {
+ return jsPath;
+ }
+
+ // If it it not available, then seek the script out instead...
+ scripts = _document.getElementsByTagName("script");
+
+ // If there is only one script
+ if (scripts.length === 1) {
+ return scripts[0].src || undefined;
+ }
+
+ // If `script` elements have the `readyState` property in this browser
+ if ("readyState" in scripts[0]) {
+ for (i = scripts.length; i--; ) {
+ if (scripts[i].readyState === "interactive" && (jsPath = scripts[i].src)) {
+ return jsPath;
+ }
+ }
+ }
+
+ // If the document is still parsing, then the last script in the document is the one that is currently loading
+ if (_document.readyState === "loading" && (jsPath = scripts[scripts.length - 1].src)) {
+ return jsPath;
+ }
+
+ // Else take more drastic measures...
+ if ((jsPath = _getCurrentScriptUrlFromError())) {
+ return jsPath;
+ }
+
+ // Otherwise we cannot reliably know which exact script is executing....
+ return undefined;
+};
+
+
+/**
+ * Get the unanimous parent directory of ALL script tags.
+ * If any script tags are either (a) inline or (b) from differing parent
+ * directories, this method must return `undefined`.
+ *
+ * @returns String or `undefined`
+ * @private
+ */
+var _getUnanimousScriptParentDir = function() {
+ var i, jsDir, jsPath,
+ scripts = _document.getElementsByTagName("script");
+
+ // If every `script` has a `src` attribute AND they all come from the same directory
+ for (i = scripts.length; i--; ) {
+ if (!(jsPath = scripts[i].src)) {
+ jsDir = null;
+ break;
+ }
+ jsPath = _getDirPathOfUrl(jsPath);
+ if (jsDir == null) {
+ jsDir = jsPath;
+ }
+ else if (jsDir !== jsPath) {
+ jsDir = null;
+ break;
+ }
+ }
+
+ // Otherwise we cannot reliably know what script is executing....
+ return jsDir || undefined;
+};
+
+
+/**
+ * Get the presumed location of the "ZeroClipboard.swf" file, based on the location
+ * of the executing JavaScript file (e.g. "ZeroClipboard.js", etc.).
+ *
+ * @returns String
+ * @private
+ */
+var _getDefaultSwfPath = function() {
+ var jsDir = _getDirPathOfUrl(_getCurrentScriptUrl()) || _getUnanimousScriptParentDir() || "";
+ return jsDir + "ZeroClipboard.swf";
+};
diff --git a/showcase/js/zeroclipboard/src/js/shared/state.js b/showcase/js/zeroclipboard/src/js/shared/state.js
new file mode 100755
index 0000000..41303bc
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/shared/state.js
@@ -0,0 +1,41 @@
+/*jshint -W079 */
+
+/**
+ * Store references to critically important global functions that may be
+ * overridden on certain web pages.
+ */
+var _window = window,
+ _document = _window.document,
+ _navigator = _window.navigator,
+ _setTimeout = _window.setTimeout,
+ _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,
+ _round = _window.Math.round,
+ _now = _window.Date.now,
+ _keys = _window.Object.keys,
+ _defineProperty = _window.Object.defineProperty,
+ _hasOwn = _window.Object.prototype.hasOwnProperty,
+ _slice = _window.Array.prototype.slice,
+ _unwrap = (function() {
+ var unwrapper = function(el) {
+ return el;
+ };
+ // For Polymer
+ if (typeof _window.wrap === "function" && typeof _window.unwrap === "function") {
+ try {
+ var div = _document.createElement("div");
+ var unwrappedDiv = _window.unwrap(div);
+ if (div.nodeType === 1 && unwrappedDiv && unwrappedDiv.nodeType === 1) {
+ unwrapper = _window.unwrap;
+ }
+ }
+ catch (e) {
+ // Some unreliable `window.unwrap` function is exposed
+ }
+ }
+ return unwrapper;
+ })();
diff --git a/showcase/js/zeroclipboard/src/js/start.js b/showcase/js/zeroclipboard/src/js/start.js
new file mode 100755
index 0000000..802443d
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/js/start.js
@@ -0,0 +1,2 @@
+(function(window, undefined) {
+ "use strict";
diff --git a/showcase/js/zeroclipboard/src/meta/LICENSE.tmpl b/showcase/js/zeroclipboard/src/meta/LICENSE.tmpl
new file mode 100755
index 0000000..9e1dc1c
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/meta/LICENSE.tmpl
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright (c) <%= grunt.template.today("yyyy") %> <%= _.pluck(contributors, "name").join(", ") %>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/showcase/js/zeroclipboard/src/meta/bower.json.tmpl b/showcase/js/zeroclipboard/src/meta/bower.json.tmpl
new file mode 100755
index 0000000..8df60da
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/meta/bower.json.tmpl
@@ -0,0 +1,17 @@
+{
+ "name": "<%= name %>",
+ "description": "<%= description %>",
+ "version": "<%= version %>",
+ "main": ["./dist/ZeroClipboard.js", "./dist/ZeroClipboard.swf"],
+ "keywords": <%= JSON.stringify(keywords) %>,
+ "license": "<%= licenses[0].url %>",
+ "authors": <%= JSON.stringify(contributors) %>,
+ "homepage": "<%= homepage %>",
+ "repository": <%= JSON.stringify(repository) %>,
+ "location": "<%= repository.url.replace(/^https:\/\//, "git://") %>",
+ "ignore": [
+ "*",
+ "!/bower.json",
+ "!/dist/**"
+ ]
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/src/meta/composer.json.tmpl b/showcase/js/zeroclipboard/src/meta/composer.json.tmpl
new file mode 100755
index 0000000..8991adb
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/meta/composer.json.tmpl
@@ -0,0 +1,33 @@
+{
+ "name": "<%= name %>/<%= name %>",
+ "description": "<%= description %>",
+ "version": "<%= version %>",
+ "type": "library",
+ "keywords": <%= JSON.stringify(keywords) %>,
+ "license": "<%= licenses[0].type %>",
+ "authors": <%= JSON.stringify(_.filter(_.map(contributors, function(c) {
+ var a;
+ /* Does not currently support the NPM package.json string format (as opposed to object format) for "people" types */
+ if (c && typeof c === "object") {
+ /* The `name` property is required */
+ if (c.name) {
+ a = {};
+ a.name = c.name;
+
+ if (c.email) {
+ a.email = c.email;
+ }
+ if (c.url) {
+ a.homepage = c.url;
+ }
+ a.role = c.role || "Developer";
+ }
+ }
+ return a;
+ }), function(a) { return !!a; })) %>,
+ "homepage": "<%= homepage %>",
+ "support": {
+ "source": "<%= repository.url %>",
+ "issues": "<%= bugs.url %>"
+ }
+}
\ No newline at end of file
diff --git a/showcase/js/zeroclipboard/src/meta/source-banner.tmpl b/showcase/js/zeroclipboard/src/meta/source-banner.tmpl
new file mode 100755
index 0000000..016d8bb
--- /dev/null
+++ b/showcase/js/zeroclipboard/src/meta/source-banner.tmpl
@@ -0,0 +1,8 @@
+/*!
+ * <%= title || name %>
+ * <%= description %>
+ * Copyright (c) <%= grunt.template.today("yyyy") %> <%= _.pluck(contributors, "name").join(", ") %>
+ * Licensed <%= _.pluck(licenses, "type").join(", ") %>
+ * <%= homepage %>
+ * v<%= version %>
+ */
diff --git a/showcase/js/zeroclipboard/test/.jshintrc b/showcase/js/zeroclipboard/test/.jshintrc
new file mode 100755
index 0000000..10a7be3
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/.jshintrc
@@ -0,0 +1,68 @@
+{
+ /* Enforcing options */
+ "bitwise": true,
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "es3": true,
+ "es5": false,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nonbsp": true,
+ "nonew": true,
+ "plusplus": false,
+ "quotmark": "double",
+ "undef": true,
+ "unused": true,
+ "strict": true,
+ "trailing": true,
+ "maxparams": 2,
+ "maxdepth": 3,
+ "maxstatements": false,
+ "maxlen": false, /* IDEAL: 120? */
+
+
+ /* Relaxing options */
+ "asi": false,
+ "boss": false,
+ "debug": false,
+ "eqnull": true,
+ "esnext": false,
+ "evil": false,
+ "expr": false,
+ "funcscope": false,
+ "gcl": false,
+ "globalstrict": false,
+ "iterator": false,
+ "lastsemic": false,
+ "laxbreak": false,
+ "laxcomma": false,
+ "loopfunc": false,
+ "maxerr": 50,
+ "moz": false,
+ "multistr": false,
+ "notypeof": false,
+ "proto": false,
+ "scripturl": false,
+ "smarttabs": false,
+ "shadow": false,
+ "sub": false,
+ "supernew": false,
+ "validthis": false,
+ "noyield": false,
+
+ /* Environments */
+ "browser": true,
+
+ /* Global variables */
+ "globals": {
+ "$": false,
+ "QUnit": false
+ }
+}
diff --git a/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js b/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js
new file mode 100755
index 0000000..d0588cc
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js
@@ -0,0 +1,67 @@
+/*global ZeroClipboard */
+
+(function(module, test) {
+ "use strict";
+
+ // Helper functions
+ var TestUtils = {
+ getHtmlBridge: function() {
+ return document.getElementById(ZeroClipboard.config("containerId"));
+ }
+ };
+
+ var originalConfig, originalFlashDetect;
+
+
+ module("ZeroClipboard.Core.js (built) unit tests", {
+ setup: function() {
+ // Store
+ originalConfig = ZeroClipboard.config();
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ // Modify
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ },
+ teardown: function() {
+ // Restore
+ ZeroClipboard.destroy();
+ ZeroClipboard.config(originalConfig);
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ }
+ });
+
+
+ test("`swfPath` finds the expected default URL", function(assert) {
+ assert.expect(1);
+
+ // Assert, act, assert
+ var rootOrigin = window.location.protocol + "//" + window.location.host + "/";
+ var indexOfTest = window.location.pathname.toLowerCase().indexOf("/test/");
+ var rootDir = window.location.pathname.slice(1, indexOfTest + 1);
+ var rootPath = rootOrigin + rootDir;
+ //var zcJsUrl = rootPath + "dist/ZeroClipboard.Core.js";
+ var swfPathBasedOnZeroClipboardJsPath = rootPath + "dist/ZeroClipboard.swf";
+
+ // Test that the client has the expected default URL [even if it's not correct]
+ assert.strictEqual(ZeroClipboard.config("swfPath"), swfPathBasedOnZeroClipboardJsPath);
+ });
+
+
+ test("`destroy` destroys the bridge", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+
+ // Assert, arrange, assert, act, assert
+ assert.equal(TestUtils.getHtmlBridge(), null, "The bridge does not exist before creating a client");
+ ZeroClipboard.create();
+ assert.notEqual(TestUtils.getHtmlBridge(), null, "The bridge does exist after creating a client");
+ ZeroClipboard.destroy();
+ assert.equal(TestUtils.getHtmlBridge(), null, "The bridge does not exist after calling `destroy`");
+ });
+
+})(QUnit.module, QUnit.test);
diff --git a/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js.html b/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js.html
new file mode 100755
index 0000000..18cfe80
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/built/ZeroClipboard.Core.tests.js.html
@@ -0,0 +1,43 @@
+
+
+
+
+ ZeroClipboard.Core.js unit tests
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js b/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js
new file mode 100755
index 0000000..243a9ee
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js
@@ -0,0 +1,228 @@
+/*global ZeroClipboard */
+
+(function(module, test) {
+ "use strict";
+
+ var originalConfig, originalFlashDetect;
+
+ // Helper functions
+ var TestUtils = {
+ getHtmlBridge: function() {
+ return document.getElementById(ZeroClipboard.config("containerId"));
+ }
+ };
+
+
+ module("ZeroClipboard.js (built) unit tests - Core", {
+ setup: function() {
+ // Store
+ originalConfig = ZeroClipboard.config();
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ // Modify
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ // Restore
+ ZeroClipboard.destroy();
+ ZeroClipboard.config(originalConfig);
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ }
+ });
+
+
+ test("`swfPath` finds the expected default URL", function(assert) {
+ assert.expect(1);
+
+ // Assert, act, assert
+ var rootOrigin = window.location.protocol + "//" + window.location.host + "/";
+ var indexOfTest = window.location.pathname.toLowerCase().indexOf("/test/");
+ var rootDir = window.location.pathname.slice(1, indexOfTest + 1);
+ var rootPath = rootOrigin + rootDir;
+ //var zcJsUrl = rootPath + "dist/ZeroClipboard.js";
+ var swfPathBasedOnZeroClipboardJsPath = rootPath + "dist/ZeroClipboard.swf";
+
+ // Test that the client has the expected default URL [even if it's not correct]
+ assert.strictEqual(ZeroClipboard.config("swfPath"), swfPathBasedOnZeroClipboardJsPath);
+ });
+
+
+ test("`destroy` destroys the bridge", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+
+ // Assert, arrange, assert, act, assert
+ assert.equal(TestUtils.getHtmlBridge(), null, "The bridge does not exist before creating a client");
+ /*jshint nonew:false */
+ new ZeroClipboard();
+ assert.notEqual(TestUtils.getHtmlBridge(), null, "The bridge does exist after creating a client");
+ ZeroClipboard.destroy();
+ assert.equal(TestUtils.getHtmlBridge(), null, "The bridge does not exist after calling `destroy`");
+ });
+
+
+
+
+ module("ZeroClipboard.js (built) unit tests - Client", {
+ setup: function() {
+ // Store
+ originalConfig = ZeroClipboard.config();
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ // Modify
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ // Restore
+ ZeroClipboard.destroy();
+ ZeroClipboard.config(originalConfig);
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ }
+ });
+
+ test("`ZeroClipboard` exists", function(assert) {
+ assert.expect(1);
+
+ // Arrange -> N/A
+
+ // Act -> N/A
+
+ // Assert
+ assert.ok(ZeroClipboard);
+ });
+
+ test("Client is created properly", function(assert) {
+ assert.expect(2);
+
+ // Arrange & Act
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(client);
+ assert.ok(client.id);
+ });
+
+ test("Client without selector doesn't have elements", function(assert) {
+ assert.expect(2);
+
+ // Arrange & Act
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(client);
+ assert.deepEqual(client.elements(), []);
+ });
+
+ test("Object has a title", function(assert) {
+ assert.expect(1);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+
+ // Act
+ client.clip(currentEl);
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ assert.strictEqual(TestUtils.getHtmlBridge().getAttribute("title"), "Click me to copy to clipboard.");
+
+ // Revert
+ ZeroClipboard.deactivate();
+ });
+
+ test("Object has no title", function(assert) {
+ assert.expect(1);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button_no_title");
+
+ // Act
+ client.clip(currentEl);
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ assert.ok(!TestUtils.getHtmlBridge().getAttribute("title"));
+ });
+
+ test("Object doesn't have data-clipboard-text", function(assert) {
+ assert.expect(1);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button_no_text");
+
+ // Act
+ client.clip(currentEl);
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ assert.ok(!TestUtils.getHtmlBridge().getAttribute("data-clipboard-text"));
+ });
+
+ test("New client is not the same client (no singleton) but does share the same bridge", function(assert) {
+ assert.expect(6);
+
+ // Assert, arrange, assert, act, assert
+ var containerClass = "." + ZeroClipboard.config("containerClass");
+ assert.strictEqual($(containerClass).length, 0);
+ var client1 = new ZeroClipboard();
+ assert.ok(client1.id);
+ assert.strictEqual($(containerClass).length, 1);
+ var client2 = new ZeroClipboard();
+ assert.strictEqual($(containerClass).length, 1);
+ assert.notEqual(client2.id, client1.id);
+ assert.notEqual(client2, client1);
+ });
+
+ test("Calculations based on borderWidth never return NaN", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+
+ // Act
+ client.clip(currentEl);
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ var htmlBridge = TestUtils.getHtmlBridge();
+ assert.strictEqual(/^-?[0-9\.]+px$/.test(htmlBridge.style.top), true);
+ assert.strictEqual(/^-?[0-9\.]+px$/.test(htmlBridge.style.left), true);
+ assert.strictEqual(/^-?[0-9\.]+px$/.test(htmlBridge.style.width), true);
+ assert.strictEqual(/^-?[0-9\.]+px$/.test(htmlBridge.style.height), true);
+ });
+
+ test("No more client singleton!", function(assert) {
+ assert.expect(7);
+
+ // Arrange
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+
+ // Assert, arrange, assert, act, assert
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype before creating a client");
+ var client1 = new ZeroClipboard();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after creating a client");
+ assert.ok(!client1._singleton, "The client singleton does not exist on the client instance after creating a client");
+ var client2 = new ZeroClipboard();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after creating a second client");
+ assert.ok(!client1._singleton, "The client singleton does not exist on the first client instance after creating a second client");
+ assert.ok(!client2._singleton, "The client singleton does not exist on the second client instance after creating a second client");
+ ZeroClipboard.destroy();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after calling `destroy`");
+ });
+
+
+})(QUnit.module, QUnit.test);
diff --git a/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js.html b/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js.html
new file mode 100755
index 0000000..7ea24a5
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/built/ZeroClipboard.tests.js.html
@@ -0,0 +1,73 @@
+
+
+
+
+ ZeroClipboard.js unit tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
diff --git a/showcase/js/zeroclipboard/test/client/api.tests.js b/showcase/js/zeroclipboard/test/client/api.tests.js
new file mode 100755
index 0000000..6286bbc
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/client/api.tests.js
@@ -0,0 +1,1132 @@
+/*global ZeroClipboard, _currentElement:true, _flashState:true, _extend, _clipData, _clipDataFormatMap */
+
+(function(module, test) {
+ "use strict";
+
+ var originalFlashState, originalConfig, originalFlashDetect;
+
+
+ module("client/api.js unit tests - constructor and bridge", {
+ setup: function() {
+ // Store
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ originalConfig = ZeroClipboard.config();
+ // Modify
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ // Restore
+ ZeroClipboard.destroy();
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ ZeroClipboard.config(originalConfig);
+ }
+ });
+
+
+ test("Client is created properly by `ZeroClipboard`", function(assert) {
+ assert.expect(3);
+
+ // Arrange & Act
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(client);
+ assert.ok(client.id);
+ assert.strictEqual(client instanceof ZeroClipboard, true);
+ });
+
+
+ test("New client is not the same client (no singleton) but does share the same bridge", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var containerClass = "." + ZeroClipboard.config("containerClass");
+
+ // Assert, arrange, assert, act, assert
+ assert.strictEqual($(containerClass).length, 0);
+ var client1 = new ZeroClipboard();
+ assert.ok(client1.id);
+ assert.strictEqual($(containerClass).length, 1);
+ var client2 = new ZeroClipboard();
+ assert.strictEqual($(containerClass).length, 1);
+ assert.notEqual(client2.id, client1.id);
+ assert.notEqual(client2, client1);
+ });
+
+
+ test("No more client singleton!", function(assert) {
+ assert.expect(7);
+
+ // Arrange
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+
+ // Assert, arrange, assert, act, assert
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype before creating a client");
+ var client1 = new ZeroClipboard();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after creating a client");
+ assert.ok(!client1._singleton, "The client singleton does not exist on the client instance after creating a client");
+ var client2 = new ZeroClipboard();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after creating a second client");
+ assert.ok(!client1._singleton, "The client singleton does not exist on the first client instance after creating a second client");
+ assert.ok(!client2._singleton, "The client singleton does not exist on the second client instance after creating a second client");
+ ZeroClipboard.destroy();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist on the prototype after calling `destroy`");
+ });
+
+
+ test("`destroy` clears up the client", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var containerId = "#" + ZeroClipboard.config("containerId");
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+
+ // Assert, arrange, assert, act, assert
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist before creating a client");
+ assert.equal($(containerId)[0], null, "The HTML bridge does not exist before creating a client");
+ /*jshint nonew:false */
+ new ZeroClipboard();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does exist after creating a client");
+ assert.notEqual($(containerId)[0], null, "The HTML bridge does exist after creating a client");
+ ZeroClipboard.destroy();
+ assert.ok(!ZeroClipboard.prototype._singleton, "The client singleton does not exist after calling `destroy`");
+ assert.equal($(containerId)[0], null, "The HTML bridge does not exist after calling `destroy`");
+ });
+
+
+ module("client/api.js unit tests - clipboard", {
+ setup: function() {
+ // Store
+ originalConfig = ZeroClipboard.config();
+ // Modify
+ ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ ZeroClipboard.destroy();
+ ZeroClipboard.config(originalConfig);
+ }
+ });
+
+
+ test("`setText` works", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert, Act, repeat ad nauseam
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ client.setText("zc4evar");
+ assert.deepEqual(_clipData, { "text/plain": "zc4evar" }, "`_clipData` contains expected text");
+
+ client.setText("ZeroClipboard");
+ assert.deepEqual(_clipData, { "text/plain": "ZeroClipboard" }, "`_clipData` contains expected updated text");
+
+ _clipData["text/html"] = "Win";
+ client.setText("goodbye");
+ assert.deepEqual(_clipData, { "text/plain": "goodbye", "text/html": "Win" }, "`_clipData` contains expected updated text AND the other data");
+ });
+
+
+ test("`setHtml` works", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert, Act, repeat ad nauseam
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ client.setHtml("zc4evar");
+ assert.deepEqual(_clipData, { "text/html": "zc4evar" }, "`_clipData` contains expected HTML");
+
+ client.setHtml("ZeroClipboard");
+ assert.deepEqual(_clipData, { "text/html": "ZeroClipboard" }, "`_clipData` contains expected updated HTML");
+
+ _clipData["text/plain"] = "blah";
+ client.setHtml("goodbye");
+ assert.deepEqual(_clipData, { "text/html": "goodbye", "text/plain": "blah" }, "`_clipData` contains expected updated HTML AND the other data");
+ });
+
+
+ test("`setRichText` works", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert, Act, repeat ad nauseam
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ client.setRichText("zc4evar");
+ assert.deepEqual(_clipData, { "application/rtf": "zc4evar" }, "`_clipData` contains expected RTF");
+
+ client.setRichText("{\\rtf1\\ansi\n{\\b ZeroClipboard}}");
+ assert.deepEqual(_clipData, { "application/rtf": "{\\rtf1\\ansi\n{\\b ZeroClipboard}}" }, "`_clipData` contains expected updated RTF");
+
+ _clipData["text/plain"] = "blah";
+ client.setRichText("{\\rtf1\\ansi\n{\\i Foo}}");
+ assert.deepEqual(_clipData, { "application/rtf": "{\\rtf1\\ansi\n{\\i Foo}}", "text/plain": "blah" }, "`_clipData` contains expected updated RTF AND the other data");
+ });
+
+
+ test("`setData` works", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert, Act, repeat ad nauseam
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ client.setData("text/plain", "zc4evar");
+ assert.deepEqual(_clipData, { "text/plain": "zc4evar" }, "`_clipData` contains expected text");
+
+ client.setData("text/html", "ZeroClipboard");
+ assert.deepEqual(_clipData, { "text/plain": "zc4evar", "text/html": "ZeroClipboard" }, "`_clipData` contains expected text and custom format");
+
+ client.setData({ "text/html": "Win" });
+ assert.deepEqual(_clipData, { "text/html": "Win" }, "`_clipData` contains expected HTML and cleared out old data because an object was passed in");
+ });
+
+
+ test("`clearData` works", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ // Arrange & Assert
+ _clipData["text/plain"] = "zc4evar";
+ _clipData["application/rtf"] = "{\\rtf1\\ansi\n{\\i Foo}}";
+ _clipData["text/html"] = "Win";
+ assert.deepEqual(_clipData, {
+ "text/plain": "zc4evar",
+ "application/rtf": "{\\rtf1\\ansi\n{\\i Foo}}",
+ "text/html": "Win"
+ }, "`_clipData` contains all expected data");
+
+ // Act & Assert
+ client.clearData("application/rtf");
+ assert.deepEqual(_clipData, {
+ "text/plain": "zc4evar",
+ "text/html": "Win"
+ }, "`_clipData` had 'application/rtf' successfully removed");
+
+ // Act & Assert
+ client.clearData();
+ assert.deepEqual(_clipData, {}, "`_clipData` had all data successfully removed");
+ });
+
+
+ test("`setText` overrides the data-clipboard-text attribute", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+
+ // Act
+ client.clip(currentEl);
+ client.setText("This is the new text");
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.deepEqual(_clipData, { "text/plain": "This is the new text" });
+ assert.deepEqual(pendingText, { "text": "This is the new text" });
+ assert.deepEqual(_clipDataFormatMap, { "text": "text/plain" });
+ });
+
+
+ test("`setText` overrides data-clipboard-target pre", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button_pre_text");
+
+ // Act
+ client.clip(currentEl);
+ client.setText("This is the new text");
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.deepEqual(_clipData, { "text/plain": "This is the new text" });
+ assert.deepEqual(pendingText, { "text": "This is the new text" });
+ assert.deepEqual(_clipDataFormatMap, { "text": "text/plain" });
+ });
+
+
+ test("`setHtml` overrides data-clipboard-target pre", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button_pre_text");
+
+ // Act
+ client.clip(currentEl);
+ client.setHtml("This is the new HTML");
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.deepEqual(_clipData, { "text/html": "This is the new HTML" });
+ assert.deepEqual(pendingText, { "html": "This is the new HTML" });
+ assert.deepEqual(_clipDataFormatMap, { "html": "text/html" });
+ });
+
+
+ test("`setText` AND `setHtml` override data-clipboard-target pre", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button_pre_text");
+
+ // Act
+ client.clip(currentEl);
+ client.setText("This is the new text");
+ client.setHtml("This is the new HTML");
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.deepEqual(_clipData, {
+ "text/plain": "This is the new text",
+ "text/html": "This is the new HTML"
+ });
+ assert.deepEqual(pendingText, {
+ "text": "This is the new text",
+ "html": "This is the new HTML"
+ });
+ assert.deepEqual(_clipDataFormatMap, { "text": "text/plain", "html": "text/html" });
+ });
+
+
+ module("client/api.js unit tests - event", {
+ setup: function() {
+ // Store
+ originalFlashState = _extend({}, _flashState);
+ originalConfig = ZeroClipboard.config();
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ // Modify
+ _currentElement = null;
+ _flashState = {
+ bridge: null,
+ version: "0.0.0",
+ disabled: null,
+ outdated: null,
+ unavailable: null,
+ deactivated: null,
+ ready: null
+ };
+ //ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ ZeroClipboard.destroy();
+ _currentElement = null;
+ _flashState = originalFlashState;
+ ZeroClipboard.config(originalConfig);
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ }
+ });
+
+
+ test("Clip element after new client", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var target = document.getElementById("d_clip_button");
+
+ // Assert, Act, Assert
+ assert.strictEqual("zcClippingId" in target, false);
+ assert.deepEqual(client.elements(), []);
+ client.clip(target);
+ assert.strictEqual("zcClippingId" in target, true);
+ assert.deepEqual(client.elements(), [target]);
+ });
+
+
+ test("unclip element removes items", function(assert) {
+ assert.expect(12);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var targets = [
+ document.getElementById("d_clip_button"),
+ document.getElementById("d_clip_button2"),
+ document.getElementById("d_clip_button3")
+ ];
+
+ // Assert pre-conditions
+ assert.strictEqual("zcClippingId" in targets[0], false);
+ assert.strictEqual("zcClippingId" in targets[1], false);
+ assert.strictEqual("zcClippingId" in targets[2], false);
+ assert.deepEqual(client.elements(), []);
+
+ // Act
+ client.clip(targets);
+
+ // Assert initial state
+ assert.strictEqual("zcClippingId" in targets[0], true);
+ assert.strictEqual("zcClippingId" in targets[1], true);
+ assert.strictEqual("zcClippingId" in targets[2], true);
+ assert.deepEqual(client.elements(), targets);
+
+ // Act more
+ client.unclip([
+ document.getElementById("d_clip_button3"),
+ document.getElementById("d_clip_button2")
+ ]);
+
+ // Assert end state
+ assert.strictEqual("zcClippingId" in targets[0], true);
+ assert.strictEqual("zcClippingId" in targets[1], false);
+ assert.strictEqual("zcClippingId" in targets[2], false);
+ assert.deepEqual(client.elements(), [targets[0]]);
+ });
+
+
+ test("Element won't be clipped twice", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+
+ // Assert, act, assert
+ assert.deepEqual(client.elements(), []);
+ client.clip(currentEl);
+ assert.deepEqual(client.elements(), [currentEl]);
+ client.clip(currentEl);
+ assert.deepEqual(client.elements(), [currentEl]);
+ });
+
+
+ test("Registering Events", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Act
+ client.on("ready", function(){});
+ client.on("onError", function(){});
+ client.on("onCustomEvent", function(){});
+
+ // Assert
+ assert.ok(client.handlers().ready);
+ assert.ok(client.handlers().error);
+ assert.ok(client.handlers().customevent);
+ assert.strictEqual(client.handlers().ready.length, 1);
+ assert.strictEqual(client.handlers().error.length, 1);
+ assert.strictEqual(client.handlers().customevent.length, 1);
+ });
+
+
+ test("Registering Events with Maps", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Act
+ client.on({
+ "ready": function(){},
+ "onError": function(){},
+ "onCustomEvent": function(){}
+ });
+
+ // Assert
+ assert.ok(client.handlers().ready);
+ assert.ok(client.handlers().error);
+ assert.ok(client.handlers().customevent);
+ assert.strictEqual(client.handlers().ready.length, 1);
+ assert.strictEqual(client.handlers().error.length, 1);
+ assert.strictEqual(client.handlers().customevent.length, 1);
+ });
+
+
+ test("Unregistering Events", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var ready = function(){};
+ var onError = function(){};
+ var onCustomEvent = function(){};
+
+ // Act
+ client.on("ready", ready);
+ client.on("onError", onError);
+ client.on("onCustomEvent", onCustomEvent);
+
+ // Assert
+ assert.deepEqual(client.handlers().ready, [ready]);
+ assert.deepEqual(client.handlers().error, [onError]);
+ assert.deepEqual(client.handlers().customevent, [onCustomEvent]);
+
+ // Act & Assert
+ client.off("ready", ready);
+ assert.deepEqual(client.handlers().ready, []);
+
+ // Act & Assert
+ client.off("onError", onError);
+ assert.deepEqual(client.handlers().error, []);
+
+ // Act & Assert
+ client.off("onCustomEvent", onCustomEvent);
+ assert.deepEqual(client.handlers().customevent, []);
+ });
+
+
+ test("Unregistering Events with Maps", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var ready = function(){};
+ var onError = function(){};
+ var onCustomEvent = function(){};
+
+ // Act
+ client.on("ready", ready);
+ client.on("onError", onError);
+ client.on("onCustomEvent", onCustomEvent);
+
+ // Assert
+ assert.deepEqual(client.handlers().ready, [ready]);
+ assert.deepEqual(client.handlers().error, [onError]);
+ assert.deepEqual(client.handlers().customevent, [onCustomEvent]);
+
+ // Act & Assert
+ client.off({ "ready": ready });
+ assert.deepEqual(client.handlers().ready, []);
+
+ // Act & Assert
+ client.off({ "onError": onError });
+ assert.deepEqual(client.handlers().error, []);
+
+ // Act & Assert
+ client.off({ "onCustomEvent": onCustomEvent });
+ assert.deepEqual(client.handlers().customevent, []);
+ });
+
+
+ test("Registering two events works", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+ assert.ok(!client.handlers().aftercopy);
+
+ // Act
+ client.on("ready onaftercopy", function(){});
+
+ // Assert more
+ assert.ok(client.handlers().ready);
+ assert.ok(client.handlers().aftercopy);
+ assert.strictEqual(client.handlers().ready.length, 1);
+ assert.strictEqual(client.handlers().aftercopy.length, 1);
+ });
+
+
+ test("Registering two events with a map works", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+ assert.ok(!client.handlers().aftercopy);
+
+ // Act
+ client.on({
+ "ready onaftercopy": function(){}
+ });
+
+ // Assert more
+ assert.ok(client.handlers().ready);
+ assert.ok(client.handlers().aftercopy);
+ assert.strictEqual(client.handlers().ready.length, 1);
+ assert.strictEqual(client.handlers().aftercopy.length, 1);
+ });
+
+
+ test("Unregistering two events works", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+ assert.ok(!client.handlers().aftercopy);
+
+ // Act
+ client.on("ready onaftercopy", func);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func]);
+ assert.deepEqual(client.handlers().aftercopy, [func]);
+
+ // Act more
+ client.off("ready onaftercopy", func);
+
+ // Assert even more
+ assert.deepEqual(client.handlers().ready, []);
+ assert.deepEqual(client.handlers().aftercopy, []);
+ });
+
+
+ test("Unregistering two events with a map works", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+ assert.ok(!client.handlers().aftercopy);
+
+ // Act
+ client.on("ready onaftercopy", func);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func]);
+ assert.deepEqual(client.handlers().aftercopy, [func]);
+
+ // Act more
+ client.off({
+ "ready onaftercopy": func
+ });
+
+ // Assert even more
+ assert.deepEqual(client.handlers().ready, []);
+ assert.deepEqual(client.handlers().aftercopy, []);
+ });
+
+
+ test("`on` can add multiple handlers for the same event", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+ var func2 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+
+ // Act
+ client.on("ready", func1);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1]);
+
+ // Act more
+ client.on("ready", func2);
+
+ // Assert even more
+ assert.deepEqual(client.handlers().ready, [func1, func2]);
+ });
+
+
+ test("`off` can remove multiple handlers for the same event", function(assert) {
+ assert.expect(5);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+ var func2 = function() {};
+ var func3 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+
+ // Act
+ client.on("ready", func1);
+ client.on("ready", func2);
+ client.on("ready", func3);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1, func2, func3]);
+
+ // Act and assert even more
+ client.off("ready", func3); // Remove from the end
+ assert.deepEqual(client.handlers().ready, [func1, func2]);
+
+ client.off("ready", func1); // Remove from the start
+ assert.deepEqual(client.handlers().ready, [func2]);
+
+ client.off("ready", func2); // Remove the last one
+ assert.deepEqual(client.handlers().ready, []);
+ });
+
+
+ test("`on` can add more than one entry of the same handler function for the same event", function(assert) {
+ assert.expect(2);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+
+ // Act
+ client.on("ready", func1);
+ client.on("ready", func1);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1, func1]);
+ });
+
+
+ test("`off` will remove all entries of the same handler function for the same event", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+
+ // Act
+ client.on("ready", func1);
+ client.on("ready", func1);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1, func1]);
+
+ // Act more
+ client.off("ready", func1);
+
+ // Assert even more
+ assert.deepEqual(client.handlers().ready, []);
+ });
+
+
+ test("`off` will remove all handler functions for an event if no function is specified", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+ var func2 = function() {};
+ var func3 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+
+ // Act
+ client.on("ready", func1);
+ client.on("ready", func2);
+ client.on("ready", func3);
+ client.on("ready", func1);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1, func2, func3, func1]);
+
+ // Act and assert even more
+ client.off("ready"); // Remove all
+ assert.deepEqual(client.handlers().ready, []);
+ });
+
+
+ test("`off` will remove all handler functions for all events if no event type is specified", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var func1 = function() {};
+ var func2 = function() {};
+ var func3 = function() {};
+
+ // Assert
+ assert.ok(!client.handlers().ready);
+ assert.ok(!client.handlers().error);
+
+ // Act
+ client.on("ready", func1);
+ client.on("ready", func2);
+ client.on("error", func3);
+
+ // Assert more
+ assert.deepEqual(client.handlers().ready, [func1, func2]);
+ assert.deepEqual(client.handlers().error, [func3]);
+
+ // Act and assert even more
+ client.off(); // Remove all handlers for all types
+ assert.deepEqual(client.handlers().ready, []);
+ assert.deepEqual(client.handlers().error, []);
+ });
+
+
+ test("Test disabledFlash Event", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ _flashState.disabled = true;
+ var client = new ZeroClipboard();
+ var id = client.id;
+
+ // Act (should auto-fire immediately but the handler will be invoked asynchronously)
+ client.on( "error", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(_flashState.disabled, true);
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-disabled");
+ assert.strictEqual(event.target, null);
+ QUnit.start();
+ } );
+ QUnit.stop();
+ });
+
+
+ test("Test outdatedFlash Event", function(assert) {
+ assert.expect(8);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = true;
+ _flashState.version = "10.0.0";
+ var client = new ZeroClipboard();
+ var id = client.id;
+
+ // Act
+ client.on( "ready", function(/* event */) {
+ assert.ok(false, "The `ready` event should NOT have fired!");
+ } );
+ client.on( "error", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(_flashState.outdated, true);
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-outdated");
+ assert.strictEqual(event.target, null);
+ assert.strictEqual(event.version, "10.0.0");
+ assert.strictEqual(event.minimumVersion, "11.0.0");
+ QUnit.start();
+ } );
+ QUnit.stop();
+ });
+
+
+ test("Test deactivatedFlash Event", function(assert) {
+ assert.expect(10);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = false;
+ _flashState.version = "11.0.0";
+ ZeroClipboard.config({ flashLoadTimeout: 2000 });
+ var client = new ZeroClipboard();
+ var id = client.id;
+ client.on( "ready", function(/* event */) {
+ assert.ok(false, "The `ready` event should NOT have fired!");
+ } );
+ client.on( "error", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(_flashState.deactivated, true);
+ assert.strictEqual(_flashState.ready, false);
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-deactivated");
+ assert.strictEqual(event.target, null);
+ assert.strictEqual(event.version, "11.0.0");
+ assert.strictEqual(event.minimumVersion, "11.0.0");
+ QUnit.start();
+ } );
+
+ // Act
+ setTimeout(function() {
+ assert.strictEqual(_flashState.deactivated, null);
+ }, 500);
+ QUnit.stop();
+ // The "deactivatedFlash" event will automatically fire in 2 seconds if the `ready` event does not fire first
+ });
+
+
+ test("Test deactivatedFlash Event after first resolution", function(assert) {
+ assert.expect(8);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = false;
+ _flashState.version = "11.0.0";
+ _flashState.deactivated = true;
+ var client = new ZeroClipboard();
+ var id = client.id;
+ client.on( "ready", function(/* event */) {
+ assert.ok(false, "The `ready` event should NOT have fired!");
+ } );
+ client.on( "error", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(_flashState.deactivated, true);
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-deactivated");
+ assert.strictEqual(event.target, null);
+ assert.strictEqual(event.version, "11.0.0");
+ assert.strictEqual(event.minimumVersion, "11.0.0");
+ QUnit.start();
+ } );
+
+ // Act
+ QUnit.stop();
+ // The "deactivatedFlash" event will automatically fire in 0 seconds (when the event loop gets to it)
+ });
+
+
+ test("Test ready Event", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = false;
+ _flashState.version = "11.9.0";
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+ var id = client.id;
+ client.clip(currentEl);
+ client.on( "ready", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(event.type, "ready");
+ assert.strictEqual(event.target, null);
+ assert.strictEqual(_flashState.deactivated, false);
+ assert.strictEqual(event.version, "11.9.0");
+ QUnit.start();
+ } );
+
+ // Act
+ QUnit.stop();
+ ZeroClipboard.emit("ready");
+ });
+
+
+ test("Test ready Event after first load", function(assert) {
+ assert.expect(6);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = false;
+ _flashState.deactivated = false;
+ _flashState.version = "11.9.0";
+ _flashState.ready = true;
+ _flashState.bridge = {};
+ var client = new ZeroClipboard();
+ var id = client.id;
+
+ // Act (should auto-fire immediately but the handler will be invoked asynchronously)
+ client.on( "ready", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(event.type, "ready");
+ assert.strictEqual(event.target, null);
+ assert.strictEqual(_flashState.deactivated, false);
+ assert.strictEqual(event.version, "11.9.0");
+ QUnit.start();
+ } );
+ QUnit.stop();
+ });
+
+
+ test("Test overdueFlash Event", function(assert) {
+ assert.expect(15);
+
+ // Arrange
+ _flashState.disabled = false;
+ _flashState.outdated = false;
+ _flashState.version = "11.0.0";
+ _flashState.deactivated = true;
+ var client = new ZeroClipboard();
+ var id = client.id;
+ client.on( "ready", function(/* event */) {
+ assert.ok(false, "The `ready` event should NOT have fired!");
+ } );
+ client.on( "error", function(event) {
+ // Assert
+ assert.strictEqual(this, client);
+ assert.strictEqual(this.id, id);
+ if (event.name === "flash-deactivated") {
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-deactivated");
+ assert.strictEqual(_flashState.deactivated, true);
+ assert.strictEqual(event.version, "11.0.0");
+ assert.strictEqual(event.minimumVersion, "11.0.0");
+ }
+ else if (event.name === "flash-overdue") {
+ assert.strictEqual(event.type, "error");
+ assert.strictEqual(event.name, "flash-overdue");
+ assert.strictEqual(_flashState.deactivated, false);
+ assert.strictEqual(_flashState.overdue, true);
+ assert.strictEqual(event.version, "11.0.0");
+ assert.strictEqual(event.minimumVersion, "11.0.0");
+
+ QUnit.start();
+ }
+ } );
+
+ // Act
+ QUnit.stop();
+ // The "deactivatedFlash" event will automatically fire in 0 seconds (when the event loop gets to it)
+
+ setTimeout(function() {
+ // Emit a "ready" event (as if from the SWF) to trigger an "overdueFlash" event
+ ZeroClipboard.emit("ready");
+ }, 1000);
+ });
+
+
+ test("Test string function name as handler", function(assert) {
+ assert.expect(2);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+ var id = client.id;
+ client.clip(currentEl);
+ window.zcLoadCallback = function(event) {
+ // Assert
+ assert.strictEqual(this.id, id);
+ assert.strictEqual(event.type, "ready");
+ QUnit.start();
+ delete window.zcLoadCallback;
+ };
+ client.on( "ready", "zcLoadCallback" );
+
+ // Act
+ QUnit.stop();
+ ZeroClipboard.emit("ready");
+ });
+
+
+ test("Test EventListener object as handler", function(assert) {
+ assert.expect(4);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+ var id = client.id;
+ client.clip(currentEl);
+ var eventListenerObj = {
+ handleEvent: function(event) {
+ // Assert
+ assert.strictEqual(event.type, "ready");
+ assert.strictEqual(event.client, client);
+ assert.strictEqual(event.client.id, id);
+ assert.strictEqual(this, eventListenerObj);
+ QUnit.start();
+ }
+ };
+ client.on( "ready", eventListenerObj );
+
+ // Act
+ QUnit.stop();
+ ZeroClipboard.emit("ready");
+ });
+
+
+ test("Test for appropriate context inside of invoked event handlers", function(assert) {
+ assert.expect(12);
+
+ // Arrange
+ var client = new ZeroClipboard();
+ var currentEl = document.getElementById("d_clip_button");
+ assert.ok(currentEl);
+ assert.strictEqual(currentEl.id, "d_clip_button");
+
+ client.clip(currentEl);
+ ZeroClipboard.activate(currentEl);
+
+ client.on( "ready error", function(/* event */) {
+ // Assert
+ assert.strictEqual(this, client);
+ } );
+ client.on( "beforecopy", function(event) {
+ // Assert
+ assert.strictEqual(event.target, currentEl);
+ } );
+ client.on( "copy", function(event) {
+ // Assert
+ assert.strictEqual(event.target, currentEl);
+ assert.ok(_clipData["text/plain"]);
+ } );
+ client.on( "aftercopy", function(event) {
+ // Assert
+ assert.strictEqual(event.target, currentEl);
+ assert.ok(!_clipData["text/plain"]);
+ QUnit.start();
+ } );
+
+ // Act
+ QUnit.stop();
+ ZeroClipboard.emit("ready");
+ ZeroClipboard.emit({"type":"error", "name":"flash-disabled"});
+ ZeroClipboard.emit({"type":"error", "name":"flash-outdated"});
+ ZeroClipboard.emit({"type":"error", "name":"flash-deactivated"});
+ ZeroClipboard.emit({"type":"error", "name":"flash-overdue"});
+ ZeroClipboard.emit("beforecopy");
+ ZeroClipboard.emit("copy");
+ ZeroClipboard.emit("aftercopy");
+ });
+
+
+ module("client/api.js unit tests - element clipping", {
+ setup: function() {
+ // Store
+ originalConfig = ZeroClipboard.config();
+ // Modify
+ ZeroClipboard.config({ swfPath: originalConfig.swfPath.replace(/\/(?:src|test)\/.*$/i, "/dist/ZeroClipboard.swf") });
+ },
+ teardown: function() {
+ ZeroClipboard.destroy();
+ ZeroClipboard.config(originalConfig);
+ }
+ });
+
+
+ test("Client without selector doesn't have elements", function(assert) {
+ assert.expect(2);
+
+ // Arrange & Act
+ var client = new ZeroClipboard();
+
+ // Assert
+ assert.ok(client);
+ assert.deepEqual(client.elements(), []);
+ });
+
+
+})(QUnit.module, QUnit.test);
diff --git a/showcase/js/zeroclipboard/test/client/api.tests.js.html b/showcase/js/zeroclipboard/test/client/api.tests.js.html
new file mode 100755
index 0000000..be24dc9
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/client/api.tests.js.html
@@ -0,0 +1,83 @@
+
+
+
+
+ ZeroClipboard unit tests: client/api.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
diff --git a/showcase/js/zeroclipboard/test/core/api.tests.js b/showcase/js/zeroclipboard/test/core/api.tests.js
new file mode 100755
index 0000000..75e4fad
--- /dev/null
+++ b/showcase/js/zeroclipboard/test/core/api.tests.js
@@ -0,0 +1,441 @@
+/*global ZeroClipboard, _globalConfig:true, _flashState, _clipData, _clipDataFormatMap, _deleteOwnProperties */
+
+(function(module, test) {
+ "use strict";
+
+ // Helper functions
+ var TestUtils = {
+ getHtmlBridge: function() {
+ return document.getElementById("global-zeroclipboard-html-bridge");
+ }
+ };
+
+ var originalConfig, originalFlashDetect;
+
+
+ module("core/api.js unit tests - state");
+
+
+ test("`state` produces expected result", function(assert) {
+ assert.expect(8);
+
+ // Act
+ var result = ZeroClipboard.state();
+
+ // Assert
+ assert.deepEqual(Object.keys(result), ["browser", "flash", "zeroclipboard"], "Has all expected keys");
+ assert.strictEqual(typeof result.browser, "object", ".browser is an object");
+ assert.notStrictEqual(result.browser, null, ".browser is a non-null object");
+ assert.strictEqual(typeof result.flash, "object", ".flash is an object");
+ assert.notStrictEqual(result.flash, null, ".flash is a non-null object");
+ assert.strictEqual(typeof result.zeroclipboard, "object", ".zeroclipboard is an object");
+ assert.notStrictEqual(result.zeroclipboard, null, ".zeroclipboard is a non-null object");
+ assert.deepEqual(Object.keys(result.zeroclipboard), ["version", "config"], ".zeroclipboard has all expected keys");
+ });
+
+
+ module("core/api.js unit tests - config", {
+ setup: function() {
+ originalConfig = ZeroClipboard.config();
+ },
+ teardown: function() {
+ _globalConfig = originalConfig;
+ }
+ });
+
+
+ test("`swfPath` finds the expected default URL", function(assert) {
+ assert.expect(1);
+
+ // Assert, act, assert
+ var rootOrigin = window.location.protocol + "//" + window.location.host + "/";
+ var indexOfTest = window.location.pathname.toLowerCase().indexOf("/test/");
+ var rootDir = window.location.pathname.slice(1, indexOfTest + 1);
+ var rootPath = rootOrigin + rootDir;
+ //var stateJsUrl = rootPath + "src/js/core/state.js";
+ // This is, for the record, a totally incorrect path due to being the development
+ // file structure but it IS the correct URL based on calculated assumption of using
+ // the built distributable versions of the library
+ var swfPathBasedOnStateJsPath = rootPath + "src/js/core/ZeroClipboard.swf";
+
+ // Test that the client has the expected default URL [even if it's not correct]
+ assert.strictEqual(ZeroClipboard.config("swfPath"), swfPathBasedOnStateJsPath);
+ });
+
+
+ test("Changing `trustedDomains` works", function(assert) {
+ assert.expect(5);
+
+ // Arrange
+ var currentHost = window.location.host;
+ var originalValue = currentHost ? [currentHost] : [];
+ var updatedValue = currentHost ? [currentHost, "otherDomain.com"] : ["otherDomain.com"];
+
+ // Assert, act, assert
+ // Test that the client has the default value
+ assert.deepEqual(ZeroClipboard.config("trustedDomains"), originalValue);
+ assert.deepEqual(ZeroClipboard.config().trustedDomains, originalValue);
+ // Change the value
+ var updatedConfig = ZeroClipboard.config({ trustedDomains: updatedValue });
+ // Test that the client has the changed value
+ assert.deepEqual(updatedConfig.trustedDomains, updatedValue);
+ assert.deepEqual(ZeroClipboard.config("trustedDomains"), updatedValue);
+ assert.deepEqual(ZeroClipboard.config().trustedDomains, updatedValue);
+ });
+
+
+ test("Some config values are ignored if SWF is actively embedded", function(assert) {
+ assert.expect(2);
+
+ // Arrange
+ var _swfPath = ZeroClipboard.config("swfPath");
+ var expectedBefore = {
+ swfPath: _swfPath,
+ trustedDomains: window.location.host ? [window.location.host] : [],
+ cacheBust: true,
+ forceEnhancedClipboard: false,
+ flashLoadTimeout: 30000,
+ autoActivate: true,
+ containerId: "global-zeroclipboard-html-bridge",
+ containerClass: "global-zeroclipboard-container",
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+ hoverClass: "zeroclipboard-is-hover",
+ activeClass: "zeroclipboard-is-active",
+
+ // These configuration values CAN be modified while a SWF is actively embedded.
+ bubbleEvents: true,
+ forceHandCursor: false,
+ title: null,
+ zIndex: 999999999
+ };
+ var expectedAfter = {
+ swfPath: _swfPath,
+ trustedDomains: window.location.host ? [window.location.host] : [],
+ cacheBust: true,
+ forceEnhancedClipboard: false,
+ flashLoadTimeout: 30000,
+ autoActivate: true,
+ containerId: "global-zeroclipboard-html-bridge",
+ containerClass: "global-zeroclipboard-container",
+ swfObjectId: "global-zeroclipboard-flash-bridge",
+ hoverClass: "zeroclipboard-is-hover",
+ activeClass: "zeroclipboard-is-active",
+
+ // These configuration values CAN be modified while a SWF is actively embedded.
+ bubbleEvents: false,
+ forceHandCursor: true,
+ title: "test",
+ zIndex: 1000
+ };
+
+ // Act
+ var actualBefore = ZeroClipboard.config();
+
+ _flashState.bridge = {};
+
+ var actualAfter = ZeroClipboard.config({
+ swfPath: "/path/to/test.swf",
+ trustedDomains: ["test.domain.com"],
+ cacheBust: false,
+ forceEnhancedClipboard: true,
+ flashLoadTimeout: 15000,
+ autoActivate: false,
+ containerId: "test-id",
+ containerClass: "test-class",
+ swfObjectId: "test-swf",
+ hoverClass: "test-hover",
+ activeClass: "test-active",
+
+ // These configuration values CAN be modified while a SWF is actively embedded.
+ bubbleEvents: false,
+ forceHandCursor: true,
+ title: "test",
+ zIndex: 1000
+ });
+
+ // Assert
+ assert.deepEqual(actualBefore, expectedBefore, "Original config is as expected");
+ assert.deepEqual(actualAfter, expectedAfter, "Updated config is as expected");
+ });
+
+
+ module("core/api.js unit tests - clipboard", {
+ teardown: function() {
+ _deleteOwnProperties(_clipData);
+ }
+ });
+
+
+ test("`setData` works", function(assert) {
+ assert.expect(4);
+
+ // Assert, Act, repeat ad nauseam
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ ZeroClipboard.setData("text/plain", "zc4evar");
+ assert.deepEqual(_clipData, { "text/plain": "zc4evar" }, "`_clipData` contains expected text");
+
+ ZeroClipboard.setData("text/x-markdown", "**ZeroClipboard**");
+ assert.deepEqual(_clipData, { "text/plain": "zc4evar", "text/x-markdown": "**ZeroClipboard**" }, "`_clipData` contains expected text and custom format");
+
+ ZeroClipboard.setData({ "text/html": "Win" });
+ assert.deepEqual(_clipData, { "text/html": "Win" }, "`_clipData` contains expected HTML and cleared out old data because an object was passed in");
+ });
+
+
+ test("`clearData` works", function(assert) {
+ assert.expect(4);
+
+ // Assert
+ assert.deepEqual(_clipData, {}, "`_clipData` is empty");
+
+ // Arrange & Assert
+ _clipData["text/plain"] = "zc4evar";
+ _clipData["text/html"] = "Win";
+ _clipData["text/x-markdown"] = "**ZeroClipboard**";
+ assert.deepEqual(_clipData, {
+ "text/plain": "zc4evar",
+ "text/html": "Win",
+ "text/x-markdown": "**ZeroClipboard**"
+ }, "`_clipData` contains all expected data");
+
+ // Act & Assert
+ ZeroClipboard.clearData("text/html");
+ assert.deepEqual(_clipData, {
+ "text/plain": "zc4evar",
+ "text/x-markdown": "**ZeroClipboard**"
+ }, "`_clipData` had 'text/html' successfully removed");
+
+ // Act & Assert
+ ZeroClipboard.clearData();
+ assert.deepEqual(_clipData, {}, "`_clipData` had all data successfully removed");
+ });
+
+
+ module("core/api.js unit tests - flash", {
+ setup: function() {
+ // Store
+ originalFlashDetect = ZeroClipboard.isFlashUnusable;
+ // Modify
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ },
+ teardown: function() {
+ // Restore
+ ZeroClipboard.isFlashUnusable = originalFlashDetect;
+ ZeroClipboard.destroy();
+ }
+ });
+
+
+ test("Flash object is ready after emitting `ready`", function(assert) {
+ assert.expect(2);
+
+ // Arrange
+ ZeroClipboard.isFlashUnusable = function() {
+ return false;
+ };
+ ZeroClipboard.create();
+
+ // Assert, act, assert
+ assert.strictEqual(_flashState.ready, false);
+ // `emit`-ing event handlers are async (generally) but the internal `ready` state is set synchronously
+ ZeroClipboard.emit("ready");
+ assert.strictEqual(_flashState.ready, true);
+ });
+
+
+ test("Object has a title", function(assert) {
+ assert.expect(1);
+
+ // Arrange
+ var currentEl = document.getElementById("d_clip_button");
+ ZeroClipboard.create();
+
+ // Act
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ assert.strictEqual(TestUtils.getHtmlBridge().getAttribute("title"), "Click me to copy to clipboard.");
+
+ // Revert
+ ZeroClipboard.deactivate();
+ });
+
+
+ test("Object has no title", function(assert) {
+ assert.expect(1);
+
+ // Arrange
+ var currentEl = document.getElementById("d_clip_button_no_title");
+ ZeroClipboard.create();
+
+ // Act
+ ZeroClipboard.activate(currentEl);
+
+ // Assert
+ assert.ok(!TestUtils.getHtmlBridge().getAttribute("title"));
+
+ // Revert
+ ZeroClipboard.deactivate();
+ });
+
+
+ test("Object has data-clipboard-text", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var currentEl = document.getElementById("d_clip_button");
+ ZeroClipboard.create();
+
+ // Act
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.deepEqual(_clipData, { "text/plain": "Copy me!" });
+ assert.deepEqual(pendingText, { "text": "Copy me!" });
+ assert.deepEqual(_clipDataFormatMap, { "text": "text/plain" });
+
+ // Revert
+ ZeroClipboard.deactivate();
+ });
+
+
+ test("Object has data-clipboard-target textarea", function(assert) {
+ assert.expect(3);
+
+ // Arrange
+ var currentEl = document.getElementById("d_clip_button_textarea_text");
+ var expectedText =
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n"+
+ "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"+
+ "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n"+
+ "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n"+
+ "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n"+
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
+ ZeroClipboard.create();
+
+ // Act
+ ZeroClipboard.activate(currentEl);
+ var pendingText = ZeroClipboard.emit("copy");
+
+ // Assert
+ assert.strictEqual(_clipData["text/plain"].replace(/\r\n/g, "\n"), expectedText);
+ assert.strictEqual(pendingText.text.replace(/\r\n/g, "\n"), expectedText);
+ assert.deepEqual(_clipDataFormatMap, { "text": "text/plain" });
+
+ // Revert
+ ZeroClipboard.deactivate();
+ });
+
+
+ test("Object has data-clipboard-target pre", function(assert) {
+ assert.expect(5);
+
+ // Arrange
+ var currentEl = document.getElementById("d_clip_button_pre_text");
+ var expectedText =
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n"+
+ "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"+
+ "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n"+
+ "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n"+
+ "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n"+
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ var expectedHtml =
+ "
"+
+ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n"+
+ "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"+
+ "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\n"+
+ "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\n"+
+ "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\n"+
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."+
+ "
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.