<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>marcis / blog &#187; flash</title>
	<atom:link href="http://blog.marcis.es/etiqueta/flash/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.marcis.es</link>
	<description>Desarrollo Web</description>
	<lastBuildDate>Fri, 08 Jul 2011 19:04:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Acceder al portapapeles desde Javascript</title>
		<link>http://blog.marcis.es/acceder-al-portapapeles-desde-javascript/</link>
		<comments>http://blog.marcis.es/acceder-al-portapapeles-desde-javascript/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 19:23:44 +0000</pubDate>
		<dc:creator>marcis</dc:creator>
				<category><![CDATA[desarrollo web]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[internet explorer]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[usabilidad]]></category>

		<guid isPermaLink="false">http://blog.marcis.es/?p=17</guid>
		<description><![CDATA[Estoy trabajando en un proyecto en el que hemos decidido mostrarle al usuario, para facilitarle la tarea, un botón o enlace que le permita copiar un texto al portapapeles. De esa forma puede realizar dos acciones con un solo click: seleccionar y copiar. Empecé la investigación (léase búsqueda en Google) y obtuve resultados demasiado buenos [...]]]></description>
			<content:encoded><![CDATA[<div style="height:33px;" class="really_simple_share robots-nocontent snap_nopreview"><div class="really_simple_share_facebook_like" style="width:100px;">
				<iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fblog.marcis.es%2Facceder-al-portapapeles-desde-javascript%2F&amp;layout=button_count&amp;show_faces=false&amp;width=&amp;action=like&amp;colorscheme=light&amp;send=false&amp;height=27" 
						scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:px; height:27px;" allowTransparency="true"></iframe>
				</div><div class="really_simple_share_twitter" style="width:110px;">
					<a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" 
						data-text="Acceder al portapapeles desde Javascript" data-url="http://blog.marcis.es/acceder-al-portapapeles-desde-javascript/" 
						data-via="" ></a> 
				</div></div>
		<div style="clear:both;"></div><p>Estoy trabajando en un proyecto en el que hemos decidido mostrarle al usuario, para facilitarle la tarea, un botón o enlace que le permita <strong>copiar un texto al portapapeles</strong>. De esa forma puede realizar dos acciones con un solo <em>click</em>: seleccionar y copiar.</p>
<p><span id="more-17"></span>Empecé la investigación (léase búsqueda en Google) y obtuve resultados demasiado buenos para ser verdad. Gracias al método &#8220;setData&#8221; del objeto &#8220;clipboardData&#8221; era sencillo cumplir mi objetivo:</p>
<pre style="padding-left: 30px;">clipboardData.setData("Text", s)
// siendo "s" una variable que contiene el texto a copiar</pre>
<p>Pero no podía ser tan fácil. El código anterior sólo funciona en Internet Explorer, así que seguí buscando una solución <em>cross-browser</em> o, como mínimo, <strong>compatible</strong> con Firefox.</p>
<p>Después de <a title="Dynamic Tools - Javascript Copy To Clipboard" href="http://www.dynamic-tools.net/toolbox/copyToClipboard/" target="_blank">algún intento infructuoso</a>, encontré <a title="Javascript copy to Firefox and Mozilla clipboard" href="http://www.febooti.com/support/website-help/website-javascript-copy-clipboard.html" target="_blank">este ejemplo</a> que funcionaba&#8230; con ciertas <strong>restricciones</strong>.</p>
<p>Por lo visto, las <a title="Security Policies - MozillaZine Knowledge Base" href="http://kb.mozillazine.org/Security_Policies" target="_blank">políticas de seguridad de Mozilla</a> impiden la ejecución de dicho código. Y puede que con motivo, ya que de lo contrario <a title="Retrieve Clipboard Text To A Web Page With Javascript - Make Sure You Do Not Allow Someone To Gather This Information, It Is An Exploit In Many Eyes" href="http://www.friendlycanadian.com/applications/clipboard.htm" target="_blank">cualquier página podría tener acceso a tu portapapeles</a> y, por lo tanto, a <strong>información potencialmente delicada</strong> (direcciones de e-mail, teléfonos, números de tarjeta de crédito,&#8230; o cualquier otro dato que hayas copiado).</p>
<p>Llegados a este punto, existían dos opciones para <strong>poder copiar el texto en Firefox</strong>:</p>
<ul>
<li><a title="JavaScript Security: Signed Scripts" href="http://www.mozilla.org/projects/security/components/signed-scripts.html" target="_blank">Firmar el script</a>, demasiado costoso para lo que queremos hacer</li>
<li>Confiar en que el usuario tenga activada la opción &#8220;signed.applets.codebase_principal_support&#8221; en su configuración (<a title="About:config entries - MozillaZine Knowledge Base" href="http://kb.mozillazine.org/Firefox_:_FAQs_:_About:config_Entries" target="_blank">about:config</a>), o bien esté dispuesto a habilitarla</li>
</ul>
<p>Si estamos pensando en hacer la aplicación más fácil de usar, <strong>no podemos pedirle al usuario</strong> que configure su navegador de una forma especial, que además conlleva cierto riesgo.</p>
<p>A todo esto hay que unir por un lado un <a title="Febooti Software" href="http://www.febooti.com/scripts/febooti.js" target="_blank">código fuente</a> bastante &#8220;farragoso&#8221;, lleno de capturas de excepciones y avisos al usuario. Y por otro, las <strong>alertas de seguridad</strong> que tanto IE como FF muestran antes de permitir la copia de contenidos al portapapeles. Seguro, pero poco práctico.</p>
<p>Me estaba replanteando la decisión de incluir esta funcionalidad cuando encontré <strong>una solución algo más limpia</strong>. Inicialmente había descartado la opción de implementarla usando Flash, pero al ver <a title="AddThis - Get Your Button Code" href="http://www.addthis.com/web-button?type=bm&amp;where=website&amp;url=&amp;bm=bn3&amp;analytics=0" target="_blank">la alternativa de AddThis</a> volví a cambiar de opinión.</p>
<p>La propuesta se basa en 4 elementos:</p>
<ol>
<li>La <a title="jQuery: The Write Less, Do More, JavaScript Library" href="http://jquery.com/" target="_blank">librería jQuery</a>, versión 1.3.2 reducida, como apoyo.</li>
<li>Un archivo de Flash, <a title="ZeroClipboard.swf" href="http://www.addthis.com/lib/zeroclipboard/ZeroClipboard.swf" target="_blank">ZeroClipboard.swf</a>, que fundamentalmente consiste en una imagen transparente que &#8220;captura&#8221; los eventos del ratón y copia un texto predefinido al portapapeles. Si teneis curiosidad por saber cómo está implementado, os recomiendo <a title="SWF Decompiler, Flash Decompiler, SWF to Flex,  SWF To FLA Converter, Recover FLA" href="http://www.sothink.com/product/flashdecompiler/" target="_blank">Sothink SWF Decompiler</a>.</li>
<li>Una clase, <a title="Simple Set Clipboard System" href="http://cache.addthis.com/www/q0004/lib/zeroclipboard/ZeroClipboard.js" target="_blank">ZeroClipboard</a>, que sirve de enlace entre el Flash y el Javascript de la página.</li>
<li>El <a title="ZeroClipboard example" href="http://cache.addthis.com/www/q0004/js/web-button.js" target="_blank">código Javascript que se ejecuta en la página</a> y que analizaremos a continuación:</li>
</ol>
<pre style="padding-left: 30px;">// Inicializamos el objeto ZeroClipboard con la ruta del fichero Flash
ZeroClipboard.setMoviePath('/lib/zeroclipboard/ZeroClipboard.swf');
// Usaremos como "disparadores" los elementos con class="copybtn"
$('.copybtn').each(function(){
    // Para diferenciarlos deben llamarse copybtn[X], siendo [X] un entero
    c = this.id.substr(7);
    // Se crea un "cliente" para cada disparador
    clips[c] = new ZeroClipboard.Client();
    // El texto se tomará del elemento con id="txt[X]"
    clips[c].setText($('#txt'+c).val());
    // Al terminar de copiar, ejecutar la función "cplt" (más abajo)
    clips[c].addEventListener('onComplete', cplt);
    // Si no está oculto, "pegar" el Flash al elemento actual
    // (es decir, colocarlo justo encima ocupando toda su extensión)
    if (!$(this).hasClass('hidebtn')) clips[c].glue(this);
}).bind('toggle',function(e){
    // Comportamiento cuando se cambie de un elemento a otro
    ...
});

// Al terminar, selecciona el texto copiado
// y muestra un mensaje de confirmación durante unos segundos
var cplt = function(client, text)
{
    c = client.domElement.id.substr(7);
    // Selecciona el contenido del "textarea"
    // (si usamos otro elemento, habrá que modificar esta línea)
    $('#txt'+c).get(0).select();
    $('#copyfdbk'+c).html('&amp;nbsp;&lt;em&gt;Copied&lt;/em&gt;').show();
    setTimeout("$('#copyfdbk"+c+"').fadeOut()", 2500);
}</pre>
<p>En cuanto al HTML, destacar sólo un par de detalles:</p>
<pre style="padding-left: 30px;">&lt;!-- El contenedor del texto es un textarea con el id adecuado --&gt;
&lt;<span>textarea</span><span> rows</span>=<span>"5" </span><strong><span>id</span>=</strong><span><strong>"txt0"</strong> </span><span>wrap</span>=<span>"off"...</span>
&lt;!-- El disparador es un botón con el id y la clase indicados --&gt;
&lt;<span>input</span><span> <strong>id</strong></span><strong>=</strong><span><strong>"copybtn0"</strong> </span><span>type</span>=<span>"button" </span><span>value</span>=<span>"Copy Code" </span><span>class</span>=<span>"<strong>copybtn</strong> lgbtn" </span><span><span>/</span></span>&gt;
&lt;!-- El elemento donde se mostrará el "feedback" --&gt;
&lt;<span>span</span><span> <strong>id</strong></span><strong>=<span>"copyfdbk0"</span></strong>&gt;&lt;/<span>span</span>&gt;</pre>
<p>Lo único que falta es ejecutar el código Javascript al terminar de cargar la página.</p>
<p>Más información sobre <a title="zeroclipboard - Project Hosting on Google Code" href="http://code.google.com/p/zeroclipboard/" target="_blank">Zero Clipboard en Google Code</a>.</p>
<h3>Seleccionar el contenido de un <em>span</em></h3>
<p>Como comentaba más arriba, si queremos <strong>seleccionar el texto</strong> de un elemento que no sea un <em>textarea</em> tendremos que modificar la llamada:</p>
<pre style="padding-left: 30px;">// $('#txt'+c).get(0).select();
selectElement($('#text_to_copy_'+c).get(0))

// Siendo la función:
function selectElement (element)
{
    if (document.selection) {
        var range = document.body.createTextRange();
        range.moveToElementText(element);
        range.select();
    } else if (window.getSelection) {
        var range = document.createRange();
        range.selectNodeContents(element);
        var selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }
}</pre>
<p>Os dejo un <a title="FAQTs - Knowledge Base - View Entry - I want to select some span text dynamically using Javascipt. Is there any way to do that?" href="http://www.faqts.com/knowledge_base/view.phtml/aid/37741" target="_blank">enlace a la fuente original</a> con su correspondiente explicación.</p>
<h3>¿Y si el usuario no tiene instalado el plugin de Flash?</h3>
<p>Por último, si no queremos confiar el funcionamiento del <em>script</em> en la carga del Flash podemos hacer una pequeña modificación:</p>
<ul>
<li>Retomamos la función Javascript que <a title="Febooti Software" href="http://www.febooti.com/scripts/febooti.js" target="_blank">copia un texto al portapapeles</a>. Llamémosla &#8220;copyToClipboard&#8221;.</li>
<li>En el bucle que recorre los disparadores, le asignamos a cada uno de ellos un gestor del evento &#8220;click&#8221; que llame a &#8220;copyToClipboard&#8221;.</li>
<li>Hacemos un cambio en el método &#8220;glue&#8221; de la clase &#8220;ZeroClipboard.Client&#8221; para que coloque el Flash fuera de lugar:</li>
</ul>
<pre style="padding-left: 30px;">// style.left = '' + box.left + 'px';
style.left = '0px';
// style.top = '' + box.top + 'px';
style.top = '0px';</pre>
<ul>
<li>Y añadimos otro gestor de eventos al objeto cliente, para comprobar que se carga correctamente:</li>
</ul>
<pre style="padding-left: 30px;">clips[c].addEventListener('onLoad', loaded);</pre>
<pre style="padding-left: 30px;">// Colocar en su sitio si todo va bien
var loaded = function(client) {
    c = client.domElement.id.substr(12);
    clips[c].reposition();
}</pre>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 1667px; width: 1px; height: 1px;">
<pre style="padding-left: 30px;">style.top = '' + box.top + 'px';</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.marcis.es/acceder-al-portapapeles-desde-javascript/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

