]> git.openstreetmap.org Git - osqa.git/commitdiff
We use ZeroClipboard to copy the permanent URL to the clipboard, this JS library...
authorjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Thu, 5 May 2011 14:22:25 +0000 (14:22 +0000)
committerjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Thu, 5 May 2011 14:22:25 +0000 (14:22 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@1023 0cfe37f9-358a-4d5e-be75-b63607b5c754

forum/skins/default/media/js/ZeroClipboard.js [new file with mode: 0755]
forum/skins/default/media/js/ZeroClipboard.swf [new file with mode: 0755]
forum/skins/default/media/js/osqa.main.js
forum/skins/default/templates/base_content.html
forum/skins/default/templates/node/permanent_link.html
forum/templatetags/node_tags.py

diff --git a/forum/skins/default/media/js/ZeroClipboard.js b/forum/skins/default/media/js/ZeroClipboard.js
new file mode 100755 (executable)
index 0000000..5adde95
--- /dev/null
@@ -0,0 +1,311 @@
+// Simple Set Clipboard System
+// Author: Joseph Huckaby
+
+var ZeroClipboard = {
+       
+       version: "1.0.7",
+       clients: {}, // registered upload clients on page, indexed by id
+       moviePath: 'ZeroClipboard.swf', // URL to movie
+       nextId: 1, // ID of next movie
+       
+       $: function(thingy) {
+               // simple DOM lookup utility function
+               if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
+               if (!thingy.addClass) {
+                       // extend element with a few useful methods
+                       thingy.hide = function() { this.style.display = 'none'; };
+                       thingy.show = function() { this.style.display = ''; };
+                       thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
+                       thingy.removeClass = function(name) {
+                               var classes = this.className.split(/\s+/);
+                               var idx = -1;
+                               for (var k = 0; k < classes.length; k++) {
+                                       if (classes[k] == name) { idx = k; k = classes.length; }
+                               }
+                               if (idx > -1) {
+                                       classes.splice( idx, 1 );
+                                       this.className = classes.join(' ');
+                               }
+                               return this;
+                       };
+                       thingy.hasClass = function(name) {
+                               return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
+                       };
+               }
+               return thingy;
+       },
+       
+       setMoviePath: function(path) {
+               // set path to ZeroClipboard.swf
+               this.moviePath = path;
+       },
+       
+       dispatch: function(id, eventName, args) {
+               // receive event from flash movie, send to client               
+               var client = this.clients[id];
+               if (client) {
+                       client.receiveEvent(eventName, args);
+               }
+       },
+       
+       register: function(id, client) {
+               // register new client to receive events
+               this.clients[id] = client;
+       },
+       
+       getDOMObjectPosition: function(obj, stopObj) {
+               // get absolute coordinates for dom element
+               var info = {
+                       left: 0, 
+                       top: 0, 
+                       width: obj.width ? obj.width : obj.offsetWidth, 
+                       height: obj.height ? obj.height : obj.offsetHeight
+               };
+
+               while (obj && (obj != stopObj)) {
+                       info.left += obj.offsetLeft;
+                       info.top += obj.offsetTop;
+                       obj = obj.offsetParent;
+               }
+
+               return info;
+       },
+       
+       Client: function(elem) {
+               // constructor for new simple upload client
+               this.handlers = {};
+               
+               // unique ID
+               this.id = ZeroClipboard.nextId++;
+               this.movieId = 'ZeroClipboardMovie_' + this.id;
+               
+               // register client with singleton to receive flash events
+               ZeroClipboard.register(this.id, this);
+               
+               // create movie
+               if (elem) this.glue(elem);
+       }
+};
+
+ZeroClipboard.Client.prototype = {
+       
+       id: 0, // unique ID for us
+       ready: false, // whether movie is ready to receive events or not
+       movie: null, // reference to movie object
+       clipText: '', // text to copy to clipboard
+       handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
+       cssEffects: true, // enable CSS mouse effects on dom container
+       handlers: null, // user event handlers
+       
+       glue: function(elem, appendElem, stylesToAdd) {
+               // glue to DOM element
+               // elem can be ID or actual DOM element object
+               this.domElement = ZeroClipboard.$(elem);
+               
+               // float just above object, or zIndex 99 if dom element isn't set
+               var zIndex = 99;
+               if (this.domElement.style.zIndex) {
+                       zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
+               }
+               
+               if (typeof(appendElem) == 'string') {
+                       appendElem = ZeroClipboard.$(appendElem);
+               }
+               else if (typeof(appendElem) == 'undefined') {
+                       appendElem = document.getElementsByTagName('body')[0];
+               }
+               
+               // find X/Y position of domElement
+               var box = ZeroClipboard.getDOMObjectPosition(this.domElement, appendElem);
+               
+               // create floating DIV above element
+               this.div = document.createElement('div');
+               var style = this.div.style;
+               style.position = 'absolute';
+               style.left = '' + box.left + 'px';
+               style.top = '' + box.top + 'px';
+               style.width = '' + box.width + 'px';
+               style.height = '' + box.height + 'px';
+               style.zIndex = zIndex;
+               
+               if (typeof(stylesToAdd) == 'object') {
+                       for (addedStyle in stylesToAdd) {
+                               style[addedStyle] = stylesToAdd[addedStyle];
+                       }
+               }
+               
+               // style.backgroundColor = '#f00'; // debug
+               
+               appendElem.appendChild(this.div);
+               
+               this.div.innerHTML = this.getHTML( box.width, box.height );
+       },
+       
+       getHTML: function(width, height) {
+               // return HTML for movie
+               var html = '';
+               var flashvars = 'id=' + this.id + 
+                       '&width=' + width + 
+                       '&height=' + height;
+                       
+               if (navigator.userAgent.match(/MSIE/)) {
+                       // IE gets an OBJECT tag
+                       var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
+                       html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
+               }
+               else {
+                       // all other browsers get an EMBED tag
+                       html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
+               }
+               return html;
+       },
+       
+       hide: function() {
+               // temporarily hide floater offscreen
+               if (this.div) {
+                       this.div.style.left = '-2000px';
+               }
+       },
+       
+       show: function() {
+               // show ourselves after a call to hide()
+               this.reposition();
+       },
+       
+       destroy: function() {
+               // destroy control and floater
+               if (this.domElement && this.div) {
+                       this.hide();
+                       this.div.innerHTML = '';
+                       
+                       var body = document.getElementsByTagName('body')[0];
+                       try { body.removeChild( this.div ); } catch(e) {;}
+                       
+                       this.domElement = null;
+                       this.div = null;
+               }
+       },
+       
+       reposition: function(elem) {
+               // reposition our floating div, optionally to new container
+               // warning: container CANNOT change size, only position
+               if (elem) {
+                       this.domElement = ZeroClipboard.$(elem);
+                       if (!this.domElement) this.hide();
+               }
+               
+               if (this.domElement && this.div) {
+                       var box = ZeroClipboard.getDOMObjectPosition(this.domElement);
+                       var style = this.div.style;
+                       style.left = '' + box.left + 'px';
+                       style.top = '' + box.top + 'px';
+               }
+       },
+       
+       setText: function(newText) {
+               // set text to be copied to clipboard
+               this.clipText = newText;
+               if (this.ready) this.movie.setText(newText);
+       },
+       
+       addEventListener: function(eventName, func) {
+               // add user event listener for event
+               // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
+               eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+               if (!this.handlers[eventName]) this.handlers[eventName] = [];
+               this.handlers[eventName].push(func);
+       },
+       
+       setHandCursor: function(enabled) {
+               // enable hand cursor (true), or default arrow cursor (false)
+               this.handCursorEnabled = enabled;
+               if (this.ready) this.movie.setHandCursor(enabled);
+       },
+       
+       setCSSEffects: function(enabled) {
+               // enable or disable CSS effects on DOM container
+               this.cssEffects = !!enabled;
+       },
+       
+       receiveEvent: function(eventName, args) {
+               // receive event from flash
+               eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+                               
+               // special behavior for certain events
+               switch (eventName) {
+                       case 'load':
+                               // movie claims it is ready, but in IE this isn't always the case...
+                               // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
+                               this.movie = document.getElementById(this.movieId);
+                               if (!this.movie) {
+                                       var self = this;
+                                       setTimeout( function() { self.receiveEvent('load', null); }, 1 );
+                                       return;
+                               }
+                               
+                               // firefox on pc needs a "kick" in order to set these in certain cases
+                               if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
+                                       var self = this;
+                                       setTimeout( function() { self.receiveEvent('load', null); }, 100 );
+                                       this.ready = true;
+                                       return;
+                               }
+                               
+                               this.ready = true;
+                               this.movie.setText( this.clipText );
+                               this.movie.setHandCursor( this.handCursorEnabled );
+                               break;
+                       
+                       case 'mouseover':
+                               if (this.domElement && this.cssEffects) {
+                                       this.domElement.addClass('hover');
+                                       if (this.recoverActive) this.domElement.addClass('active');
+                               }
+                               break;
+                       
+                       case 'mouseout':
+                               if (this.domElement && this.cssEffects) {
+                                       this.recoverActive = false;
+                                       if (this.domElement.hasClass('active')) {
+                                               this.domElement.removeClass('active');
+                                               this.recoverActive = true;
+                                       }
+                                       this.domElement.removeClass('hover');
+                               }
+                               break;
+                       
+                       case 'mousedown':
+                               if (this.domElement && this.cssEffects) {
+                                       this.domElement.addClass('active');
+                               }
+                               break;
+                       
+                       case 'mouseup':
+                               if (this.domElement && this.cssEffects) {
+                                       this.domElement.removeClass('active');
+                                       this.recoverActive = false;
+                               }
+                               break;
+               } // switch eventName
+               
+               if (this.handlers[eventName]) {
+                       for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
+                               var func = this.handlers[eventName][idx];
+                       
+                               if (typeof(func) == 'function') {
+                                       // actual function reference
+                                       func(this, args);
+                               }
+                               else if ((typeof(func) == 'object') && (func.length == 2)) {
+                                       // PHP style object + method, i.e. [myObject, 'myMethod']
+                                       func[0][ func[1] ](this, args);
+                               }
+                               else if (typeof(func) == 'string') {
+                                       // name of function
+                                       window[func](this, args);
+                               }
+                       } // foreach event handler defined
+               } // user defined handler for event
+       }
+       
+};
diff --git a/forum/skins/default/media/js/ZeroClipboard.swf b/forum/skins/default/media/js/ZeroClipboard.swf
new file mode 100755 (executable)
index 0000000..13bf8e3
Binary files /dev/null and b/forum/skins/default/media/js/ZeroClipboard.swf differ
index 2c72aa18042e1440b5c63968905ab707f2f3f5e7..ebc9cf072415f83d9169977b192694fc5a246827 100644 (file)
@@ -159,7 +159,6 @@ var response_commands = {
     },\r
 \r
     copy_url: function(url) {\r
-        $.copy(url);\r
     }\r
 }\r
 \r
@@ -181,11 +180,17 @@ function show_dialog (extern) {
         yes_callback: default_close_function,\r
         no_text: messages.cancel,\r
         show_no: false,\r
-        close_on_clickoutside: false\r
+        close_on_clickoutside: false,\r
+        copy: false\r
     }\r
 \r
     $.extend(options, extern);\r
 \r
+    var copy_id = '';\r
+    if (options.copy) {\r
+        copy_id = ' id="copy_clip_button"'\r
+    }\r
+\r
     if (options.event != undefined) {\r
         options.pos = {x: options.event.pageX, y: options.event.pageY};\r
     }\r
@@ -197,8 +202,7 @@ function show_dialog (extern) {
         html += '<button class="dialog-no">' + options.no_text + '</button>';\r
     }\r
 \r
-    html += '<button class="dialog-yes">' + options.yes_text + '</button>'\r
-            + '</div></div>';\r
+    html += '<button class="dialog-yes"' + copy_id + '>' + options.yes_text + '</button>' + '</div></div>';\r
 \r
     $dialog = $(html);\r
     $('body').append($dialog);\r
@@ -304,7 +308,12 @@ function load_prompt(evt, el, url) {
                     process_ajax_response(data, evt);\r
                 }, 'json');\r
             },\r
-            show_no: true\r
+            show_no: true,\r
+            copy: false\r
+        }\r
+\r
+        if (el.hasClass('copy')) {\r
+            $.extend(doptions, { yes_text : 'Copy', copy: true});\r
         }\r
 \r
         if (!el.is('.centered')) {\r
index 3dd0a8152c6760a6e2cd7242735fe698a42894d2..b33cbf58835df5e65a8e82fe6baf8c3d9807c398 100644 (file)
         </style>
         <![endif]-->
         {% block forestyle %}{% endblock %}
+
         <script src="http://www.google.com/jsapi" type="text/javascript"></script>
         <script type="text/javascript">
             google.load("jquery", "1.4.2");
             google.load("jqueryui", "1", {autoload:true});
-        </script>
-        <script type="text/javascript">
-        /* <![CDATA[ */
+
             var i18nLang = '{{settings.LANGUAGE_CODE}}';
             var scriptUrl = '{{settings.FORCE_SCRIPT_NAME}}/'
             var osqaSkin = '{{settings.OSQA_SKIN}}';
                 character: "{% trans "character" %}",
                 characters: "{% trans "characters" %}"
             }
-        /* ]]> */
         </script>
         <script type="text/javascript" src="{% media  "/media/js/osqa.main.js" %}"></script>
-        <script type="text/javascript" src="{% media  "/media/js/jquery.copy.js" %}"></script>
         {% if user_messages %}
         <style type="text/css">
             body { margin-top:2.4em; }
index b85249bd21b62a26d2a4fa5ede4039ad3e2769d5..307b73305d21a34b353e70e693fb4a3175c9dc91 100644 (file)
@@ -1,4 +1,4 @@
-{% load i18n %}
+{% load i18n extra_tags %}
 
 <p>
 {% blocktrans %}
@@ -11,11 +11,38 @@ Would you like to copy the permanent link to your clipboard?
 <p><input id="permanent_link_url" name="permanent_link_url" size="64" style="font-size: 12px; width: 330px" readonly="readonly" type="text" value="{{ url }}" /></p>
 {% endspaceless %}
 
+<script type="text/javascript" src="{% media  "/media/js/ZeroClipboard.js" %}"></script>
 <script type="text/javascript">
 $(document).ready(function() {
     // We highlight the content of the text field on click event
     $('#permanent_link_url').click(function() {
         $(this).select();
-    })
-})
+    });
+
+    // We use ZeroClipboard to copy the URL
+    ZeroClipboard.setMoviePath('{% media '/media/js/ZeroClipboard.swf' %}');
+
+    // Create the Client Clip
+    var clip = new ZeroClipboard.Client();
+
+    // Create the glue when on mouse-over event
+    $('#copy_clip_button').live('mouseover', function() {
+        // Take the URL from the input
+        clip.setText($('#permanent_link_url').val());
+
+        // Create the glue
+        clip.glue('copy_clip_button');
+
+        // Simulate click on the OK button when we hear a moseDown event on the glue
+        clip.addEventListener('mouseDown', function() {
+            $('#copy_clip_button').click();
+        });
+
+        // Reposition the clip
+        clip.addEventListener('complete', function() {
+            clip.reposition();
+        });
+    });
+
+});
 </script>
index 6a88355233f682556eb148f0b0364e89351e9fc3..c7fcba03a47a2772efc4e0e38a8cdfd5de6c2322 100644 (file)
@@ -75,8 +75,9 @@ def post_classes(post):
 \r
     return " ".join(classes)\r
 \r
-def post_control(text, url, command=False, withprompt=False, confirm=False, title=""):\r
-    classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ")\r
+def post_control(text, url, command=False, withprompt=False, confirm=False, title="", copy=False):\r
+    classes = (command and "ajax-command" or " ") + (withprompt and " withprompt" or " ") + (confirm and " confirm" or " ") + \\r
+        (copy and " copy" or " ")\r
     return {'text': text, 'url': url, 'classes': classes, 'title': title}\r
 \r
 @register.inclusion_tag('node/post_controls.html')\r
@@ -88,8 +89,8 @@ def post_controls(post, user):
     # We show the link tool if the post is an Answer. It is visible to Guests too.\r
     if post_type == "answer":\r
         # Answer permanent link tool\r
-        controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id}),\r
-                                     title=_("answer permanent link"), command=True, withprompt=True))\r
+        controls.append(post_control(_('permanent link'), reverse('answer_permanent_link', kwargs={'id' : post.id,}),\r
+                                     title=_("answer permanent link"), command=True, withprompt=True, copy=True))\r
 \r
         # Users should be able to award points for an answer. Users cannot award their own answers\r
         if user != post.author and user.is_authenticated():\r