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"; + } + yt.tipBox.innerHTML = yt.tipText; + yt.Dom.setStyle(yt.tipBox, 'display', 'block'); + if(yt.useAnim === true){ + yt.Dom.setStyle(yt.tipBox, 'opacity', '0'); + var newAnim = new YAHOO.util.Anim(yt.tipBox, + { + opacity: { to: yt.opacity } + }, yt.speed, YAHOO.util.Easing.easeOut + ); + newAnim.animate(); + } + } + }, + + move_yuitip: function(e, el){ + yt.YE.stopEvent(e); + var movePos = yt.YE.getXY(e); + yt.Dom.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px'); + yt.Dom.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px'); + }, + + close_yuitip: function(e, el){ + yt.YE.stopEvent(e); + + if(yt.useAnim === true){ + var newAnim = new YAHOO.util.Anim(yt.tipBox, + { + opacity: { to: 0 } + }, yt.speed, YAHOO.util.Easing.easeOut + ); + newAnim.animate(); + } else { + yt.Dom.setStyle(yt.tipBox, 'display', 'none'); + } + YUD.setAttribute(el,'title', yt._tooltip); + } +} /** * Quick filter widget diff --git a/rhodecode/templates/base/root.html b/rhodecode/templates/base/root.html --- a/rhodecode/templates/base/root.html +++ b/rhodecode/templates/base/root.html @@ -130,6 +130,7 @@ YUE.onDOMReady(function(){ tooltip_activate(); show_more_event(); + show_changeset_tooltip(); YUE.on('quick_login_link','click',function(e){ // make sure we don't redirect diff --git a/rhodecode/templates/followers/followers_data.html b/rhodecode/templates/followers/followers_data.html --- a/rhodecode/templates/followers/followers_data.html +++ b/rhodecode/templates/followers/followers_data.html @@ -19,7 +19,11 @@ +
${c.journal_data}