diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
--- a/rhodecode/config/routing.py
+++ b/rhodecode/config/routing.py
@@ -436,6 +436,9 @@ def make_map(config):
controller='changeset', action='raw_changeset',
revision='tip', conditions=dict(function=check_repo))
+ rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
+ controller='changeset', action='changeset_info')
+
rmap.connect('compare_url',
'/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
controller='compare', action='index',
diff --git a/rhodecode/controllers/changeset.py b/rhodecode/controllers/changeset.py
--- a/rhodecode/controllers/changeset.py
+++ b/rhodecode/controllers/changeset.py
@@ -26,7 +26,7 @@
import logging
import traceback
from collections import defaultdict
-from webob.exc import HTTPForbidden
+from webob.exc import HTTPForbidden, HTTPBadRequest
from pylons import tmpl_context as c, url, request, response
from pylons.i18n.translation import _
@@ -445,3 +445,10 @@ class ChangesetController(BaseRepoContro
return True
else:
raise HTTPForbidden()
+
+ @jsonify
+ def changeset_info(self, repo_name, revision):
+ if request.is_xhr or 1:
+ return c.rhodecode_repo.get_changeset(revision)
+ else:
+ raise HTTPBadRequest()
diff --git a/rhodecode/lib/helpers.py b/rhodecode/lib/helpers.py
--- a/rhodecode/lib/helpers.py
+++ b/rhodecode/lib/helpers.py
@@ -532,7 +532,8 @@ def action_parser(user_log, feed=False,
title = _('Changeset not found')
if parse_cs:
return link_to(lbl, _url, title=title, class_='tooltip')
- return link_to(lbl, _url, raw_id=rev.raw_id, class_='journal-cs')
+ return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name,
+ class_='lazy-cs')
revs = []
if len(filter(lambda v: v != '', revs_ids)) > 0:
diff --git a/rhodecode/lib/vcs/backends/base.py b/rhodecode/lib/vcs/backends/base.py
--- a/rhodecode/lib/vcs/backends/base.py
+++ b/rhodecode/lib/vcs/backends/base.py
@@ -372,6 +372,15 @@ class BaseChangeset(object):
def __eq__(self, other):
return self.raw_id == other.raw_id
+ def __json__(self):
+ return dict(
+ short_id=self.short_id,
+ raw_id=self.raw_id,
+ message=self.message,
+ date=self.date,
+ author=self.author,
+ )
+
@LazyProperty
def last(self):
if self.repository is None:
diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css
--- a/rhodecode/public/css/style.css
+++ b/rhodecode/public/css/style.css
@@ -3001,22 +3001,24 @@ table.code-browser .submodule-dir {
z-index: 2;
}
-.yui-tt {
- visibility: hidden;
+#tip-box {
position: absolute;
- color: #666;
+
background-color: #FFF;
border: 2px solid #003367;
font: 100% sans-serif;
width: auto;
opacity: 1px;
padding: 8px;
+
white-space: pre-wrap;
-webkit-border-radius: 8px 8px 8px 8px;
-khtml-border-radius: 8px 8px 8px 8px;
-moz-border-radius: 8px 8px 8px 8px;
border-radius: 8px 8px 8px 8px;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
+ -moz-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
+ -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.6);
}
.mentions-container{
diff --git a/rhodecode/public/js/rhodecode.js b/rhodecode/public/js/rhodecode.js
--- a/rhodecode/public/js/rhodecode.js
+++ b/rhodecode/public/js/rhodecode.js
@@ -250,6 +250,24 @@ function ypjax(url,container,s_call,f_ca
};
+var ajaxGET = function(url,success) {
+ // Set special header for ajax == HTTP_X_PARTIAL_XHR
+ YUC.initHeader('X-PARTIAL-XHR',true);
+
+ var sUrl = url;
+ var callback = {
+ success: success,
+ failure: function (o) {
+ alert("error");
+ },
+ };
+
+ var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
+ return request;
+};
+
+
+
var ajaxPOST = function(url,postData,success) {
// Set special header for ajax == HTTP_X_PARTIAL_XHR
YUC.initHeader('X-PARTIAL-XHR',true);
@@ -282,27 +300,8 @@ var ajaxPOST = function(url,postData,suc
* tooltip activate
*/
var tooltip_activate = function(){
- function toolTipsId(){
- var ids = [];
- var tts = YUQ('.tooltip');
- for (var i = 0; i < tts.length; i++) {
- // if element doesn't not have and id
- // autogenerate one for tooltip
- if (!tts[i].id){
- tts[i].id='tt'+((i*100)+tts.length);
- }
- ids.push(tts[i].id);
- }
- return ids
- };
- var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
- context: [[toolTipsId()],"tl","bl",null,[0,5]],
- monitorresize:false,
- xyoffset :[0,0],
- autodismissdelay:300000,
- hidedelay:5,
- showdelay:20,
- });
+ yt = YAHOO.yuitip.main;
+ YUE.onDOMReady(yt.init);
};
/**
@@ -316,6 +315,140 @@ var show_more_event = function(){
});
};
+/**
+ * show changeset tooltip
+ */
+var show_changeset_tooltip = function(){
+ YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
+ var target = e.currentTarget;
+ var rid = YUD.getAttribute(target,'raw_id');
+ var repo_name = YUD.getAttribute(target,'repo_name');
+ var ttid = 'tt-'+rid;
+ var success = function(o){
+ console.log(o.responseText);
+ var json = JSON.parse(o.responseText);
+ YUD.addClass(target,'tooltip')
+ YUD.setAttribute(target, 'title',json['message']);
+ YAHOO.yuitip.main.show_yuitip(e, target);
+ }
+ if(rid && !YUD.hasClass(target, 'tooltip')){
+ YUD.setAttribute(target,'id',ttid);
+ ajaxGET('/changeset_info/{0}/{1}'.format(repo_name,rid), success)
+ }
+ });
+};
+
+
+/**
+ * TOOLTIP IMPL.
+ */
+YAHOO.namespace('yuitip');
+YAHOO.yuitip.main = {
+
+ YE: YAHOO.util.Event,
+ Dom: YAHOO.util.Dom,
+ $: YAHOO.util.Dom.get,
+
+ bgColor: '#000',
+ speed: 0.3,
+ opacity: 0.9,
+ offset: [15,15],
+ useAnim: false,
+ maxWidth: 200,
+ add_links: true,
+
+ init: function(){
+ yt._tooltip = '';
+ yt.tipBox = yt.$('tip-box');
+ if(!yt.tipBox){
+ yt.tipBox = document.createElement('div');
+ document.body.appendChild(yt.tipBox);
+ yt.tipBox.id = 'tip-box';
+ }
+
+ yt.Dom.setStyle(yt.tipBox, 'display', 'none');
+ yt.Dom.setStyle(yt.tipBox, 'position', 'absolute');
+ if(yt.maxWidth !== null){
+ yt.Dom.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
+ }
+
+ var yuitips = yt.Dom.getElementsByClassName('tooltip');
+
+ if(yt.add_links === true){
+ var links = document.getElementsByTagName('a');
+ var linkLen = links.length;
+ for(i=0;i