diff options
-rw-r--r-- | askbot/skins/common/media/js/autocompleter.js | 28 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 2 | ||||
-rw-r--r-- | askbot/skins/common/media/js/utils.js | 803 | ||||
-rw-r--r-- | askbot/skins/default/media/bootstrap/css/bootstrap.css | 2 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 17 | ||||
-rw-r--r-- | askbot/skins/default/templates/macros.html | 10 | ||||
-rw-r--r-- | askbot/skins/default/templates/question/sidebar.html | 92 |
7 files changed, 913 insertions, 41 deletions
diff --git a/askbot/skins/common/media/js/autocompleter.js b/askbot/skins/common/media/js/autocompleter.js index a7c54315..8121d2ea 100644 --- a/askbot/skins/common/media/js/autocompleter.js +++ b/askbot/skins/common/media/js/autocompleter.js @@ -10,6 +10,7 @@ var AutoCompleter = function(options) { * Default options for autocomplete plugin */ var defaults = { + promptText: '', autocompleteMultiple: true, multipleSeparator: ' ',//a single character inputClass: 'acInput', @@ -147,6 +148,11 @@ AutoCompleter.prototype.decorate = function(element){ this._element.attr('autocomplete', 'off'); /** + * Set prompt text + */ + this.setPrompt(); + + /** * Create DOM element to hold results */ this._results = $('<div></div>').hide(); @@ -161,6 +167,21 @@ AutoCompleter.prototype.decorate = function(element){ this.setEventHandlers(); }; +AutoCompleter.prototype.setPrompt = function() { + this._element.val(this.options['promptText']); + this._element.addClass('prompt'); +}; + +AutoCompleter.prototype.removePrompt = function() { + if (this._element.hasClass('prompt')) { + this._element.removeClass('prompt'); + var val = this._element.val(); + if (val === this.options['promptText']) { + this._element.val(''); + } + } +}; + AutoCompleter.prototype.setEventHandlers = function(){ /** * Shortcut to self @@ -171,6 +192,9 @@ AutoCompleter.prototype.setEventHandlers = function(){ * Attach keyboard monitoring to $elem */ self._element.keydown(function(e) { + + self.removePrompt(); + self.lastKeyPressed_ = e.keyCode; switch(self.lastKeyPressed_) { @@ -204,6 +228,10 @@ AutoCompleter.prototype.setEventHandlers = function(){ break; case 27: // escape + if ($.trim(self._element.val()) === '') { + self.setPrompt(); + return false; + } if (self.active_) { e.preventDefault(); self.finish(); diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index cafc6840..59074e1b 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -3905,6 +3905,7 @@ $(document).ready(function() { var groupsAc = new AutoCompleter({ url: askbot['urls']['getGroupsList'], preloadData: true, + promptText: gettext('Group name:'), minChars: 1, useCache: false, matchInside: true, @@ -3918,6 +3919,7 @@ $(document).ready(function() { var usersAc = new AutoCompleter({ url: '/get-users-info/', preloadData: true, + promptText: gettext('User name:'), minChars: 1, useCache: false, matchInside: true, diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js index b8a716ec..b9c80b97 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -1481,8 +1481,807 @@ Hilite={elementid:"content",exact:true,max_nodes:1000,onload:true,style_name:"hi if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==="string"){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}()); //jquery fieldselection (function(){var a={getSelection:function(){var b=this.jquery?this[0]:this;return(("selectionStart" in b&&function(){var c=b.selectionEnd-b.selectionStart;return{start:b.selectionStart,end:b.selectionEnd,length:c,text:b.value.substr(b.selectionStart,c)}})||(document.selection&&function(){b.focus();var d=document.selection.createRange();if(d==null){return{start:0,end:b.value.length,length:0}}var c=b.createTextRange();var e=c.duplicate();c.moveToBookmark(d.getBookmark());e.setEndPoint("EndToStart",c);return{start:e.text.length,end:e.text.length+d.text.length,length:d.text.length,text:d.text}})||function(){return{start:0,end:b.value.length,length:0}})()},replaceSelection:function(){var b=this.jquery?this[0]:this;var c=arguments[0]||"";return(("selectionStart" in b&&function(){b.value=b.value.substr(0,b.selectionStart)+c+b.value.substr(b.selectionEnd,b.value.length);return this})||(document.selection&&function(){b.focus();document.selection.createRange().text=c;return this})||function(){b.value+=c;return this})()}};jQuery.each(a,function(b){jQuery.fn[b]=this})})(); -//our custom autocompleter -var AutoCompleter=function(a){var b={autocompleteMultiple:true,multipleSeparator:" ",inputClass:"acInput",loadingClass:"acLoading",resultsClass:"acResults",selectClass:"acSelect",queryParamName:"q",limitParamName:"limit",extraParams:{},lineSeparator:"\n",cellSeparator:"|",minChars:2,maxItemsToShow:10,delay:400,useCache:true,maxCacheLength:10,matchSubset:true,matchCase:false,matchInside:true,mustMatch:false,preloadData:false,selectFirst:false,stopCharRegex:/\s+/,selectOnly:false,formatItem:null,onItemSelect:false,autoFill:false,filterResults:true,sortResults:true,sortFunction:false,onNoMatch:false};this.options=$.extend({},b,a);this.cacheData_={};this.cacheLength_=0;this.selectClass_="jquery-autocomplete-selected-item";this.keyTimeout_=null;this.lastKeyPressed_=null;this.lastProcessedValue_=null;this.lastSelectedValue_=null;this.active_=false;this.finishOnBlur_=true;this.options.minChars=parseInt(this.options.minChars,10);if(isNaN(this.options.minChars)||this.options.minChars<1){this.options.minChars=2}this.options.maxItemsToShow=parseInt(this.options.maxItemsToShow,10);if(isNaN(this.options.maxItemsToShow)||this.options.maxItemsToShow<1){this.options.maxItemsToShow=10}this.options.maxCacheLength=parseInt(this.options.maxCacheLength,10);if(isNaN(this.options.maxCacheLength)||this.options.maxCacheLength<1){this.options.maxCacheLength=10}if(this.options.preloadData===true){this.fetchRemoteData("",function(){})}};inherits(AutoCompleter,WrappedElement);AutoCompleter.prototype.decorate=function(a){this._element=a;this._element.attr("autocomplete","off");this._results=$("<div></div>").hide();if(this.options.resultsClass){this._results.addClass(this.options.resultsClass)}this._results.css({position:"absolute"});$("body").append(this._results);this.setEventHandlers()};AutoCompleter.prototype.setEventHandlers=function(){var a=this;a._element.keydown(function(b){a.lastKeyPressed_=b.keyCode;switch(a.lastKeyPressed_){case 38:b.preventDefault();if(a.active_){a.focusPrev()}else{a.activate()}return false;break;case 40:b.preventDefault();if(a.active_){a.focusNext()}else{a.activate()}return false;break;case 9:case 13:if(a.active_){b.preventDefault();a.selectCurrent();return false}break;case 27:if(a.active_){b.preventDefault();a.finish();return false}break;default:a.activate()}});a._element.blur(function(){if(a.finishOnBlur_){setTimeout(function(){a.finish()},200)}})};AutoCompleter.prototype.position=function(){var a=this._element.offset();this._results.css({top:a.top+this._element.outerHeight(),left:a.left})};AutoCompleter.prototype.cacheRead=function(d){var f,c,b,a,e;if(this.options.useCache){d=String(d);f=d.length;if(this.options.matchSubset){c=1}else{c=f}while(c<=f){if(this.options.matchInside){a=f-c}else{a=0}e=0;while(e<=a){b=d.substr(0,c);if(this.cacheData_[b]!==undefined){return this.cacheData_[b]}e++}c++}}return false};AutoCompleter.prototype.cacheWrite=function(a,b){if(this.options.useCache){if(this.cacheLength_>=this.options.maxCacheLength){this.cacheFlush()}a=String(a);if(this.cacheData_[a]!==undefined){this.cacheLength_++}return this.cacheData_[a]=b}return false};AutoCompleter.prototype.cacheFlush=function(){this.cacheData_={};this.cacheLength_=0};AutoCompleter.prototype.callHook=function(c,b){var a=this.options[c];if(a&&$.isFunction(a)){return a(b,this)}return false};AutoCompleter.prototype.activate=function(){var b=this;var a=function(){b.activateNow()};var c=parseInt(this.options.delay,10);if(isNaN(c)||c<=0){c=250}if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}this.keyTimeout_=setTimeout(a,c)};AutoCompleter.prototype.activateNow=function(){var a=this.getValue();if(a!==this.lastProcessedValue_&&a!==this.lastSelectedValue_){if(a.length>=this.options.minChars){this.active_=true;this.lastProcessedValue_=a;this.fetchData(a)}}};AutoCompleter.prototype.fetchData=function(b){if(this.options.data){this.filterAndShowResults(this.options.data,b)}else{var a=this;this.fetchRemoteData(b,function(c){a.filterAndShowResults(c,b)})}};AutoCompleter.prototype.fetchRemoteData=function(c,e){var d=this.cacheRead(c);if(d){e(d)}else{var a=this;if(this._element){this._element.addClass(this.options.loadingClass)}var b=function(g){var f=false;if(g!==false){f=a.parseRemoteData(g);a.options.data=f;a.cacheWrite(c,f)}if(a._element){a._element.removeClass(a.options.loadingClass)}e(f)};$.ajax({url:this.makeUrl(c),success:b,error:function(){b(false)}})}};AutoCompleter.prototype.setOption=function(a,b){this.options[a]=b};AutoCompleter.prototype.setExtraParam=function(b,c){var a=$.trim(String(b));if(a){if(!this.options.extraParams){this.options.extraParams={}}if(this.options.extraParams[a]!==c){this.options.extraParams[a]=c;this.cacheFlush()}}};AutoCompleter.prototype.makeUrl=function(e){var a=this;var b=this.options.url;var d=$.extend({},this.options.extraParams);if(this.options.queryParamName===false){b+=encodeURIComponent(e)}else{d[this.options.queryParamName]=e}if(this.options.limitParamName&&this.options.maxItemsToShow){d[this.options.limitParamName]=this.options.maxItemsToShow}var c=[];$.each(d,function(f,g){c.push(a.makeUrlParam(f,g))});if(c.length){b+=b.indexOf("?")==-1?"?":"&";b+=c.join("&")}return b};AutoCompleter.prototype.makeUrlParam=function(a,b){return String(a)+"="+encodeURIComponent(b)};AutoCompleter.prototype.splitText=function(a){return String(a).replace(/(\r\n|\r|\n)/g,"\n").split(this.options.lineSeparator)};AutoCompleter.prototype.parseRemoteData=function(c){var h,b,f,d,g;var e=[];var b=this.splitText(c);for(f=0;f<b.length;f++){var a=b[f].split(this.options.cellSeparator);g=[];for(d=0;d<a.length;d++){g.push(unescape(a[d]))}h=g.shift();e.push({value:unescape(h),data:g})}return e};AutoCompleter.prototype.filterAndShowResults=function(a,b){this.showResults(this.filterResults(a,b),b)};AutoCompleter.prototype.filterResults=function(d,b){var f=[];var l,c,e,m,j,a;var k,h,g;for(e=0;e<d.length;e++){m=d[e];j=typeof m;if(j==="string"){l=m;c={}}else{if($.isArray(m)){l=m[0];c=m.slice(1)}else{if(j==="object"){l=m.value;c=m.data}}}l=String(l);if(l>""){if(typeof c!=="object"){c={}}if(this.options.filterResults){h=String(b);g=String(l);if(!this.options.matchCase){h=h.toLowerCase();g=g.toLowerCase()}a=g.indexOf(h);if(this.options.matchInside){a=a>-1}else{a=a===0}}else{a=true}if(a){f.push({value:l,data:c})}}}if(this.options.sortResults){f=this.sortResults(f,b)}if(this.options.maxItemsToShow>0&&this.options.maxItemsToShow<f.length){f.length=this.options.maxItemsToShow}return f};AutoCompleter.prototype.sortResults=function(c,d){var b=this;var a=this.options.sortFunction;if(!$.isFunction(a)){a=function(g,e,h){return b.sortValueAlpha(g,e,h)}}c.sort(function(f,e){return a(f,e,d)});return c};AutoCompleter.prototype.sortValueAlpha=function(d,c,e){d=String(d.value);c=String(c.value);if(!this.options.matchCase){d=d.toLowerCase();c=c.toLowerCase()}if(d>c){return 1}if(d<c){return -1}return 0};AutoCompleter.prototype.showResults=function(e,b){var k=this;var g=$("<ul></ul>");var f,l,j,a,h=false,d=false;var c=e.length;for(f=0;f<c;f++){l=e[f];j=$("<li>"+this.showResult(l.value,l.data)+"</li>");j.data("value",l.value);j.data("data",l.data);j.click(function(){var i=$(this);k.selectItem(i)}).mousedown(function(){k.finishOnBlur_=false}).mouseup(function(){k.finishOnBlur_=true});g.append(j);if(h===false){h=String(l.value);d=j;j.addClass(this.options.firstItemClass)}if(f==c-1){j.addClass(this.options.lastItemClass)}}this.position();this._results.html(g).show();a=this._results.outerWidth()-this._results.width();this._results.width(this._element.outerWidth()-a);$("li",this._results).hover(function(){k.focusItem(this)},function(){});if(this.autoFill(h,b)){this.focusItem(d)}};AutoCompleter.prototype.showResult=function(b,a){if($.isFunction(this.options.showResult)){return this.options.showResult(b,a)}else{return b}};AutoCompleter.prototype.autoFill=function(e,c){var b,a,d,f;if(this.options.autoFill&&this.lastKeyPressed_!=8){b=String(e).toLowerCase();a=String(c).toLowerCase();d=e.length;f=c.length;if(b.substr(0,f)===a){this._element.val(e);this.selectRange(f,d);return true}}return false};AutoCompleter.prototype.focusNext=function(){this.focusMove(+1)};AutoCompleter.prototype.focusPrev=function(){this.focusMove(-1)};AutoCompleter.prototype.focusMove=function(a){var b,c=$("li",this._results);a=parseInt(a,10);for(var b=0;b<c.length;b++){if($(c[b]).hasClass(this.selectClass_)){this.focusItem(b+a);return}}this.focusItem(0)};AutoCompleter.prototype.focusItem=function(b){var a,c=$("li",this._results);if(c.length){c.removeClass(this.selectClass_).removeClass(this.options.selectClass);if(typeof b==="number"){b=parseInt(b,10);if(b<0){b=0}else{if(b>=c.length){b=c.length-1}}a=$(c[b])}else{a=$(b)}if(a){a.addClass(this.selectClass_).addClass(this.options.selectClass)}}};AutoCompleter.prototype.selectCurrent=function(){var a=$("li."+this.selectClass_,this._results);if(a.length==1){this.selectItem(a)}else{this.finish()}};AutoCompleter.prototype.selectItem=function(d){var c=d.data("value");var b=d.data("data");var a=this.displayValue(c,b);this.lastProcessedValue_=a;this.lastSelectedValue_=a;this.setValue(a);this.setCaret(a.length);this.callHook("onItemSelect",{value:c,data:b});this.finish()};AutoCompleter.prototype.isContentChar=function(a){if(a.match(this.options.stopCharRegex)){return false}else{if(a===this.options.multipleSeparator){return false}else{return true}}};AutoCompleter.prototype.getValue=function(){var c=this._element.getSelection();var d=this._element.val();var f=c.start;var e=f;for(cpos=f;cpos>=0;cpos=cpos-1){if(cpos===d.length){continue}var b=d.charAt(cpos);if(!this.isContentChar(b)){break}e=cpos}var a=f;for(cpos=f;cpos<d.length;cpos=cpos+1){if(cpos===0){continue}var b=d.charAt(cpos);if(!this.isContentChar(b)){break}a=cpos}this._selection_start=e;this._selection_end=a;return d.substring(e,a)};AutoCompleter.prototype.setValue=function(b){var a=this._element.val().substring(0,this._selection_start);var c=this._element.val().substring(this._selection_end+1);this._element.val(a+b+c)};AutoCompleter.prototype.displayValue=function(b,a){if($.isFunction(this.options.displayValue)){return this.options.displayValue(b,a)}else{return b}};AutoCompleter.prototype.finish=function(){if(this.keyTimeout_){clearTimeout(this.keyTimeout_)}if(this._element.val()!==this.lastSelectedValue_){if(this.options.mustMatch){this._element.val("")}this.callHook("onNoMatch")}this._results.hide();this.lastKeyPressed_=null;this.lastProcessedValue_=null;if(this.active_){this.callHook("onFinish")}this.active_=false};AutoCompleter.prototype.selectRange=function(d,a){var c=this._element.get(0);if(c.setSelectionRange){c.focus();c.setSelectionRange(d,a)}else{if(this.createTextRange){var b=this.createTextRange();b.collapse(true);b.moveEnd("character",a);b.moveStart("character",d);b.select()}}};AutoCompleter.prototype.setCaret=function(a){this.selectRange(a,a)}; +/** + * AutoCompleter Object, refactored closure style from + * jQuery autocomplete plugin + * @param {Object=} options Settings + * @constructor + */ +var AutoCompleter = function(options) { + + /** + * Default options for autocomplete plugin + */ + var defaults = { + promptText: '', + autocompleteMultiple: true, + multipleSeparator: ' ',//a single character + inputClass: 'acInput', + loadingClass: 'acLoading', + resultsClass: 'acResults', + selectClass: 'acSelect', + queryParamName: 'q', + limitParamName: 'limit', + extraParams: {}, + lineSeparator: '\n', + cellSeparator: '|', + minChars: 2, + maxItemsToShow: 10, + delay: 400, + useCache: true, + maxCacheLength: 10, + matchSubset: true, + matchCase: false, + matchInside: true, + mustMatch: false, + preloadData: false, + selectFirst: false, + stopCharRegex: /\s+/, + selectOnly: false, + formatItem: null, // TBD + onItemSelect: false, + autoFill: false, + filterResults: true, + sortResults: true, + sortFunction: false, + onNoMatch: false + }; + + /** + * Options dictionary + * @type Object + * @private + */ + this.options = $.extend({}, defaults, options); + + /** + * Cached data + * @type Object + * @private + */ + this.cacheData_ = {}; + + /** + * Number of cached data items + * @type number + * @private + */ + this.cacheLength_ = 0; + + /** + * Class name to mark selected item + * @type string + * @private + */ + this.selectClass_ = 'jquery-autocomplete-selected-item'; + + /** + * Handler to activation timeout + * @type ?number + * @private + */ + this.keyTimeout_ = null; + + /** + * Last key pressed in the input field (store for behavior) + * @type ?number + * @private + */ + this.lastKeyPressed_ = null; + + /** + * Last value processed by the autocompleter + * @type ?string + * @private + */ + this.lastProcessedValue_ = null; + + /** + * Last value selected by the user + * @type ?string + * @private + */ + this.lastSelectedValue_ = null; + + /** + * Is this autocompleter active? + * @type boolean + * @private + */ + this.active_ = false; + + /** + * Is it OK to finish on blur? + * @type boolean + * @private + */ + this.finishOnBlur_ = true; + + this.options.minChars = parseInt(this.options.minChars, 10); + if (isNaN(this.options.minChars) || this.options.minChars < 1) { + this.options.minChars = 2; + } + + this.options.maxItemsToShow = parseInt(this.options.maxItemsToShow, 10); + if (isNaN(this.options.maxItemsToShow) || this.options.maxItemsToShow < 1) { + this.options.maxItemsToShow = 10; + } + + this.options.maxCacheLength = parseInt(this.options.maxCacheLength, 10); + if (isNaN(this.options.maxCacheLength) || this.options.maxCacheLength < 1) { + this.options.maxCacheLength = 10; + } + + if (this.options['preloadData'] === true){ + this.fetchRemoteData('', function(){}); + } +}; +inherits(AutoCompleter, WrappedElement); + +AutoCompleter.prototype.decorate = function(element){ + + /** + * Init DOM elements repository + */ + this._element = element; + + /** + * Switch off the native autocomplete + */ + this._element.attr('autocomplete', 'off'); + + /** + * Set prompt text + */ + this.setPrompt(); + + /** + * Create DOM element to hold results + */ + this._results = $('<div></div>').hide(); + if (this.options.resultsClass) { + this._results.addClass(this.options.resultsClass); + } + this._results.css({ + position: 'absolute' + }); + $('body').append(this._results); + + this.setEventHandlers(); +}; + +AutoCompleter.prototype.setPrompt = function() { + this._element.val(this.options['promptText']); + this._element.addClass('prompt'); +}; + +AutoCompleter.prototype.removePrompt = function() { + if (this._element.hasClass('prompt')) { + this._element.removeClass('prompt'); + var val = this._element.val(); + if (val === this.options['promptText']) { + this._element.val(''); + } + } +}; + +AutoCompleter.prototype.setEventHandlers = function(){ + /** + * Shortcut to self + */ + var self = this; + + /** + * Attach keyboard monitoring to $elem + */ + self._element.keydown(function(e) { + + self.removePrompt(); + + self.lastKeyPressed_ = e.keyCode; + switch(self.lastKeyPressed_) { + + case 38: // up + e.preventDefault(); + if (self.active_) { + self.focusPrev(); + } else { + self.activate(); + } + return false; + break; + + case 40: // down + e.preventDefault(); + if (self.active_) { + self.focusNext(); + } else { + self.activate(); + } + return false; + break; + + case 9: // tab + case 13: // return + if (self.active_) { + e.preventDefault(); + self.selectCurrent(); + return false; + } + break; + + case 27: // escape + if ($.trim(self._element.val()) === '') { + self._element.blur(); + return false; + } + if (self.active_) { + e.preventDefault(); + self.finish(); + return false; + } + break; + + default: + self.activate(); + + } + }); + self._element.focus(function() { + self.removePrompt(); + }); + self._element.blur(function() { + if ($.trim(self._element.val()) === '') { + self.setPrompt(); + return true; + } + if (self.finishOnBlur_) { + setTimeout(function() { self.finish(); }, 200); + } + }); +}; + +AutoCompleter.prototype.position = function() { + var offset = this._element.offset(); + this._results.css({ + top: offset.top + this._element.outerHeight(), + left: offset.left + }); +}; + +AutoCompleter.prototype.cacheRead = function(filter) { + var filterLength, searchLength, search, maxPos, pos; + if (this.options.useCache) { + filter = String(filter); + filterLength = filter.length; + if (this.options.matchSubset) { + searchLength = 1; + } else { + searchLength = filterLength; + } + while (searchLength <= filterLength) { + if (this.options.matchInside) { + maxPos = filterLength - searchLength; + } else { + maxPos = 0; + } + pos = 0; + while (pos <= maxPos) { + search = filter.substr(0, searchLength); + if (this.cacheData_[search] !== undefined) { + return this.cacheData_[search]; + } + pos++; + } + searchLength++; + } + } + return false; +}; + +AutoCompleter.prototype.cacheWrite = function(filter, data) { + if (this.options.useCache) { + if (this.cacheLength_ >= this.options.maxCacheLength) { + this.cacheFlush(); + } + filter = String(filter); + if (this.cacheData_[filter] !== undefined) { + this.cacheLength_++; + } + return this.cacheData_[filter] = data; + } + return false; +}; + +AutoCompleter.prototype.cacheFlush = function() { + this.cacheData_ = {}; + this.cacheLength_ = 0; +}; + +AutoCompleter.prototype.callHook = function(hook, data) { + var f = this.options[hook]; + if (f && $.isFunction(f)) { + return f(data, this); + } + return false; +}; + +AutoCompleter.prototype.activate = function() { + var self = this; + var activateNow = function() { + self.activateNow(); + }; + var delay = parseInt(this.options.delay, 10); + if (isNaN(delay) || delay <= 0) { + delay = 250; + } + if (this.keyTimeout_) { + clearTimeout(this.keyTimeout_); + } + this.keyTimeout_ = setTimeout(activateNow, delay); +}; + +AutoCompleter.prototype.activateNow = function() { + var value = this.getValue(); + if (value !== this.lastProcessedValue_ && value !== this.lastSelectedValue_) { + if (value.length >= this.options.minChars) { + this.active_ = true; + this.lastProcessedValue_ = value; + this.fetchData(value); + } + } +}; + +AutoCompleter.prototype.fetchData = function(value) { + if (this.options.data) { + this.filterAndShowResults(this.options.data, value); + } else { + var self = this; + this.fetchRemoteData(value, function(remoteData) { + self.filterAndShowResults(remoteData, value); + }); + } +}; + +AutoCompleter.prototype.fetchRemoteData = function(filter, callback) { + var data = this.cacheRead(filter); + if (data) { + callback(data); + } else { + var self = this; + if (this._element){ + this._element.addClass(this.options.loadingClass); + } + var ajaxCallback = function(data) { + var parsed = false; + if (data !== false) { + parsed = self.parseRemoteData(data); + self.options.data = parsed;//cache data forever - E.F. + self.cacheWrite(filter, parsed); + } + if (self._element){ + self._element.removeClass(self.options.loadingClass); + } + callback(parsed); + }; + $.ajax({ + url: this.makeUrl(filter), + success: ajaxCallback, + error: function() { + ajaxCallback(false); + } + }); + } +}; + +AutoCompleter.prototype.setOption = function(name, value){ + this.options[name] = value; +}; + +AutoCompleter.prototype.setExtraParam = function(name, value) { + var index = $.trim(String(name)); + if (index) { + if (!this.options.extraParams) { + this.options.extraParams = {}; + } + if (this.options.extraParams[index] !== value) { + this.options.extraParams[index] = value; + this.cacheFlush(); + } + } +}; + +AutoCompleter.prototype.makeUrl = function(param) { + var self = this; + var url = this.options.url; + var params = $.extend({}, this.options.extraParams); + // If options.queryParamName === false, append query to url + // instead of using a GET parameter + if (this.options.queryParamName === false) { + url += encodeURIComponent(param); + } else { + params[this.options.queryParamName] = param; + } + + if (this.options.limitParamName && this.options.maxItemsToShow) { + params[this.options.limitParamName] = this.options.maxItemsToShow; + } + + var urlAppend = []; + $.each(params, function(index, value) { + urlAppend.push(self.makeUrlParam(index, value)); + }); + if (urlAppend.length) { + url += url.indexOf('?') == -1 ? '?' : '&'; + url += urlAppend.join('&'); + } + return url; +}; + +AutoCompleter.prototype.makeUrlParam = function(name, value) { + return String(name) + '=' + encodeURIComponent(value); +}; + +/** + * Sanitize CR and LF, then split into lines + */ +AutoCompleter.prototype.splitText = function(text) { + return String(text).replace(/(\r\n|\r|\n)/g, '\n').split(this.options.lineSeparator); +}; + +AutoCompleter.prototype.parseRemoteData = function(remoteData) { + var value, lines, i, j, data; + var results = []; + var lines = this.splitText(remoteData); + for (i = 0; i < lines.length; i++) { + var line = lines[i].split(this.options.cellSeparator); + data = []; + for (j = 0; j < line.length; j++) { + data.push(unescape(line[j])); + } + value = data.shift(); + results.push({ value: unescape(value), data: data }); + } + return results; +}; + +AutoCompleter.prototype.filterAndShowResults = function(results, filter) { + this.showResults(this.filterResults(results, filter), filter); +}; + +AutoCompleter.prototype.filterResults = function(results, filter) { + + var filtered = []; + var value, data, i, result, type, include; + var regex, pattern, testValue; + + for (i = 0; i < results.length; i++) { + result = results[i]; + type = typeof result; + if (type === 'string') { + value = result; + data = {}; + } else if ($.isArray(result)) { + value = result[0]; + data = result.slice(1); + } else if (type === 'object') { + value = result.value; + data = result.data; + } + value = String(value); + if (value > '') { + if (typeof data !== 'object') { + data = {}; + } + if (this.options.filterResults) { + pattern = String(filter); + testValue = String(value); + if (!this.options.matchCase) { + pattern = pattern.toLowerCase(); + testValue = testValue.toLowerCase(); + } + include = testValue.indexOf(pattern); + if (this.options.matchInside) { + include = include > -1; + } else { + include = include === 0; + } + } else { + include = true; + } + if (include) { + filtered.push({ value: value, data: data }); + } + } + } + + if (this.options.sortResults) { + filtered = this.sortResults(filtered, filter); + } + + if (this.options.maxItemsToShow > 0 && this.options.maxItemsToShow < filtered.length) { + filtered.length = this.options.maxItemsToShow; + } + + return filtered; + +}; + +AutoCompleter.prototype.sortResults = function(results, filter) { + var self = this; + var sortFunction = this.options.sortFunction; + if (!$.isFunction(sortFunction)) { + sortFunction = function(a, b, f) { + return self.sortValueAlpha(a, b, f); + }; + } + results.sort(function(a, b) { + return sortFunction(a, b, filter); + }); + return results; +}; + +AutoCompleter.prototype.sortValueAlpha = function(a, b, filter) { + a = String(a.value); + b = String(b.value); + if (!this.options.matchCase) { + a = a.toLowerCase(); + b = b.toLowerCase(); + } + if (a > b) { + return 1; + } + if (a < b) { + return -1; + } + return 0; +}; + +AutoCompleter.prototype.showResults = function(results, filter) { + var self = this; + var $ul = $('<ul></ul>'); + var i, result, $li, extraWidth, first = false, $first = false; + var numResults = results.length; + for (i = 0; i < numResults; i++) { + result = results[i]; + $li = $('<li>' + this.showResult(result.value, result.data) + '</li>'); + $li.data('value', result.value); + $li.data('data', result.data); + $li.click(function() { + var $this = $(this); + self.selectItem($this); + }).mousedown(function() { + self.finishOnBlur_ = false; + }).mouseup(function() { + self.finishOnBlur_ = true; + }); + $ul.append($li); + if (first === false) { + first = String(result.value); + $first = $li; + $li.addClass(this.options.firstItemClass); + } + if (i == numResults - 1) { + $li.addClass(this.options.lastItemClass); + } + } + + // Alway recalculate position before showing since window size or + // input element location may have changed. This fixes #14 + this.position(); + + this._results.html($ul).show(); + extraWidth = this._results.outerWidth() - this._results.width(); + this._results.width(this._element.outerWidth() - extraWidth); + $('li', this._results).hover( + function() { self.focusItem(this); }, + function() { /* void */ } + ); + if (this.autoFill(first, filter)) { + this.focusItem($first); + } +}; + +AutoCompleter.prototype.showResult = function(value, data) { + if ($.isFunction(this.options.showResult)) { + return this.options.showResult(value, data); + } else { + return value; + } +}; + +AutoCompleter.prototype.autoFill = function(value, filter) { + var lcValue, lcFilter, valueLength, filterLength; + if (this.options.autoFill && this.lastKeyPressed_ != 8) { + lcValue = String(value).toLowerCase(); + lcFilter = String(filter).toLowerCase(); + valueLength = value.length; + filterLength = filter.length; + if (lcValue.substr(0, filterLength) === lcFilter) { + this._element.val(value); + this.selectRange(filterLength, valueLength); + return true; + } + } + return false; +}; + +AutoCompleter.prototype.focusNext = function() { + this.focusMove(+1); +}; + +AutoCompleter.prototype.focusPrev = function() { + this.focusMove(-1); +}; + +AutoCompleter.prototype.focusMove = function(modifier) { + var i, $items = $('li', this._results); + modifier = parseInt(modifier, 10); + for (var i = 0; i < $items.length; i++) { + if ($($items[i]).hasClass(this.selectClass_)) { + this.focusItem(i + modifier); + return; + } + } + this.focusItem(0); +}; + +AutoCompleter.prototype.focusItem = function(item) { + var $item, $items = $('li', this._results); + if ($items.length) { + $items.removeClass(this.selectClass_).removeClass(this.options.selectClass); + if (typeof item === 'number') { + item = parseInt(item, 10); + if (item < 0) { + item = 0; + } else if (item >= $items.length) { + item = $items.length - 1; + } + $item = $($items[item]); + } else { + $item = $(item); + } + if ($item) { + $item.addClass(this.selectClass_).addClass(this.options.selectClass); + } + } +}; + +AutoCompleter.prototype.selectCurrent = function() { + var $item = $('li.' + this.selectClass_, this._results); + if ($item.length == 1) { + this.selectItem($item); + } else { + this.finish(); + } +}; + +AutoCompleter.prototype.selectItem = function($li) { + var value = $li.data('value'); + var data = $li.data('data'); + var displayValue = this.displayValue(value, data); + this.lastProcessedValue_ = displayValue; + this.lastSelectedValue_ = displayValue; + + this.setValue(displayValue); + + this.setCaret(displayValue.length); + this.callHook('onItemSelect', { value: value, data: data }); + this.finish(); +}; + +/** + * @return {boolean} true if the symbol matches something that is + * considered content and false otherwise + * @param {string} symbol - a single char string + */ +AutoCompleter.prototype.isContentChar = function(symbol){ + if (symbol.match(this.options['stopCharRegex'])){ + return false; + } else if (symbol === this.options['multipleSeparator']){ + return false; + } else { + return true; + } +}; + +/** + * takes value from the input box + * and saves _selection_start and _selection_end coordinates + * respects settings autocompleteMultiple and + * multipleSeparator + * @return {string} the current word in the + * autocompletable word + */ +AutoCompleter.prototype.getValue = function(){ + var sel = this._element.getSelection(); + var text = this._element.val(); + var pos = sel.start;//estimated start + //find real start + var start = pos; + for (cpos = pos; cpos >= 0; cpos = cpos - 1){ + if (cpos === text.length){ + continue; + } + var symbol = text.charAt(cpos); + if (!this.isContentChar(symbol)){ + break; + } + start = cpos; + } + //find real end + var end = pos; + for (cpos = pos; cpos < text.length; cpos = cpos + 1){ + if (cpos === 0){ + continue; + } + var symbol = text.charAt(cpos); + if (!this.isContentChar(symbol)){ + break; + } + end = cpos; + } + this._selection_start = start; + this._selection_end = end; + return text.substring(start, end); +} + +/** + * sets value of the input box + * by replacing the previous selection + * with the value from the autocompleter + */ +AutoCompleter.prototype.setValue = function(val){ + var prefix = this._element.val().substring(0, this._selection_start); + var postfix = this._element.val().substring(this._selection_end + 1); + this._element.val(prefix + val + postfix); +}; + +AutoCompleter.prototype.displayValue = function(value, data) { + if ($.isFunction(this.options.displayValue)) { + return this.options.displayValue(value, data); + } else { + return value; + } +}; + +AutoCompleter.prototype.finish = function() { + if (this.keyTimeout_) { + clearTimeout(this.keyTimeout_); + } + if (this._element.val() !== this.lastSelectedValue_) { + if (this.options.mustMatch) { + this._element.val(''); + } + this.callHook('onNoMatch'); + } + this._results.hide(); + this.lastKeyPressed_ = null; + this.lastProcessedValue_ = null; + if (this.active_) { + this.callHook('onFinish'); + } + this.active_ = false; +}; + +AutoCompleter.prototype.selectRange = function(start, end) { + var input = this._element.get(0); + if (input.setSelectionRange) { + input.focus(); + input.setSelectionRange(start, end); + } else if (this.createTextRange) { + var range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + range.select(); + } +}; + +AutoCompleter.prototype.setCaret = function(pos) { + this.selectRange(pos, pos); +}; + (function($){function isRGBACapable(){var $script=$("script:first"),color=$script.css("color"),result=false;if(/^rgba/.test(color)){result=true}else{try{result=(color!=$script.css("color","rgba(0, 0, 0, 0.5)").css("color"));$script.css("color",color)}catch(e){}}return result}$.extend(true,$,{support:{rgba:isRGBACapable()}});var properties=["color","backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","outlineColor"];$.each(properties,function(i,property){$.fx.step[property]=function(fx){if(!fx.init){fx.begin=parseColor($(fx.elem).css(property));fx.end=parseColor(fx.end);fx.init=true}fx.elem.style[property]=calculateColor(fx.begin,fx.end,fx.pos)}});$.fx.step.borderColor=function(fx){if(!fx.init){fx.end=parseColor(fx.end)}var borders=properties.slice(2,6);$.each(borders,function(i,property){if(!fx.init){fx[property]={begin:parseColor($(fx.elem).css(property))}}fx.elem.style[property]=calculateColor(fx[property].begin,fx.end,fx.pos)});fx.init=true};function calculateColor(begin,end,pos){var color="rgb"+($.support.rgba?"a":"")+"("+parseInt((begin[0]+pos*(end[0]-begin[0])),10)+","+parseInt((begin[1]+pos*(end[1]-begin[1])),10)+","+parseInt((begin[2]+pos*(end[2]-begin[2])),10);if($.support.rgba){color+=","+(begin&&end?parseFloat(begin[3]+pos*(end[3]-begin[3])):1)}color+=")";return color}function parseColor(color){var match,triplet;if(match=/#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/.exec(color)){triplet=[parseInt(match[1],16),parseInt(match[2],16),parseInt(match[3],16),1]}else{if(match=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/.exec(color)){triplet=[parseInt(match[1],16)*17,parseInt(match[2],16)*17,parseInt(match[3],16)*17,1]}else{if(match=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)){triplet=[parseInt(match[1]),parseInt(match[2]),parseInt(match[3]),1]}else{if(match=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9\.]*)\s*\)/.exec(color)){triplet=[parseInt(match[1],10),parseInt(match[2],10),parseInt(match[3],10),parseFloat(match[4])]}else{if(color=="transparent"){triplet=[0,0,0,0]}}}}}return triplet}})(jQuery); /** diff --git a/askbot/skins/default/media/bootstrap/css/bootstrap.css b/askbot/skins/default/media/bootstrap/css/bootstrap.css index 3e829732..898e657a 100644 --- a/askbot/skins/default/media/bootstrap/css/bootstrap.css +++ b/askbot/skins/default/media/bootstrap/css/bootstrap.css @@ -965,7 +965,7 @@ input[type="button"], input[type="reset"], input[type="submit"] { width: auto; - height: auto; + /*height: auto;*/ } select, input[type="file"] { diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index cc7a4431..51a9d77a 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -39,6 +39,12 @@ input, select { margin-left:0px; } +input[type="text"].prompt, +input[type="password"].prompt { + font-style: italic; + color: @info-text; +} + textarea:focus, input:focus{ outline: none; } @@ -531,6 +537,7 @@ body.anon { margin-bottom: 4px; color: @info-text; font-family:@main-font; + font-size: 14px; } p.info-box-follow-up-links { @@ -564,6 +571,10 @@ body.anon { background: #eceeeb url(../images/contributorsback.png) no-repeat center left; } + form { + margin: 0px; + } + label { color: @info-text; font-size:15px; @@ -1474,11 +1485,17 @@ ul#related-tags li { .rounded-corners(4px); } +.share-input-col { + width: 160px; + text-align: center; +} + .add-everyone-group { text-align: center; margin: auto; display: block; padding: 0 10px; + height: 25px; } .add-groups:hover { diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index c03980aa..6199aebb 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -206,11 +206,15 @@ poor design of the data or methods on data objects #} </ul> {%- endmacro -%} +{%- macro user_group_link(group) -%} + <a class="group-name" + href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}" + >{{ group.name|escape }}</a> +{%- endmacro -%} + {%- macro user_group(group, membership_info) -%} <td> - <a class="group-name" - href="{% url users_by_group group.id, group.name|replace('-', ' ')|slugify %}" - >{{ group.name|escape }}</a> + {{ user_group_link(group) }} </td> <td> <span class="group-description"> diff --git a/askbot/skins/default/templates/question/sidebar.html b/askbot/skins/default/templates/question/sidebar.html index 837dfbe2..f595634b 100644 --- a/askbot/skins/default/templates/question/sidebar.html +++ b/askbot/skins/default/templates/question/sidebar.html @@ -51,51 +51,21 @@ <div class="clearfix"></div> <div class="box sharing-widget"> {% if thread.is_private() %} - <h2>{% trans %}Share{% endtrans %}</h2> - <label for="share_user_name">{% trans %}Share with users{% endtrans %}</label> - <p> - <a href="{{ request.user.get_profile_url() }}"> - {% trans %}You{% endtrans %} - </a> - </p> - {% for user in sharing_info['users'] %} - <p>{{ user.get_profile_link() }}<p> - {% endfor %} - {% if sharing_info['more_users_count'] > 0 %} - <p><a - class="see-related-users" - data-url="{% url get_thread_shared_users %}" - data-thread-id="{{ thread.id }}" - >{% trans more_count=sharing_info['more_users_count'] %}... and {{ more_count }} more{% endtrans %} - </a></p> - {% endif %} + <h2>{% trans %}Invite{% endtrans %}</h2> + <p style="margin: 16px 0" + >Invite others to help answer this question</p> <form action="{% url share_question_with_user %}" method="post">{% csrf_token %} <input id="share_user_name" type="text" class="groups-input" name="recipient_name" /> <input type="hidden" name="thread_id" value="{{ thread.id }}"/> <input type="submit" class="add-groups" value="{% trans %}add{% endtrans %}"/> </form> - {% if sharing_info['groups'].count() > 0 %} - <label for="group_name">{% trans %}Shared with groups:{% endtrans %}</label> - {% else %} - <label for="group_name">{% trans %}Share with a group{% endtrans %}</label> - {% endif %} - {% for group in sharing_info['groups'] %} - <p>{{ macros.user_group(group) }}</p> - {% endfor %} - {% if sharing_info['more_groups_count'] > 0 %} - <p><a - class="see-related-groups" - data-url="{% url get_thread_shared_groups %}" - data-thread-id="{{ thread.id }}" - >{% trans more_count=sharing_info['more_groups_count'] %}... and {{ more_count }} more{% endtrans %} - </a></p> - {% endif %} + <p class="share-input-col">{% trans %}- or -{% endtrans %}</p> <form action="{% url share_question_with_group %}" method="post">{% csrf_token %} <input id="share_group_name" type="text" class="groups-input" name="recipient_name" /> <input type="hidden" name="thread_id" value="{{ thread.id }}"/> <input type="submit" class="add-groups" value="{% trans %}add{% endtrans %}"/> </form> - <p style="text-align: center">{% trans %}or{% endtrans %}</p> + <p class="share-input-col">{% trans %}- or -{% endtrans %}</p> <form action="{% url share_question_with_group %}" method="post">{% csrf_token %} <input type="hidden" @@ -103,13 +73,65 @@ value="{{ settings.GLOBAL_GROUP_NAME }}" /> <input type="hidden" name="thread_id" value="{{ thread.id }}"/> + <p class="share-input-col"> <input type="submit" class="add-groups add-everyone-group" value="{% trans %}share with everyone{% endtrans %}" /> + </p> </form> + {% set shared_users_count = sharing_info['users'].count() %} + {% set shared_groups_count = sharing_info['groups'].count() %} + + {% if shared_users_count or shared_groups_count %} + <p + style="margin:16px 0 4px 0" + >{% trans %}This question is currently shared only with:{% endtrans %}</p> + {% endif %} + <h3>{% trans %}Individual users{% endtrans %}</h3> + {% set comma = joiner(',') %} + {{ comma() }} + <p> + <a href="{{ request.user.get_profile_url() }}"> + {% trans %}You{% endtrans -%} + </a>{%- if shared_users_count -%} + {%- for user in sharing_info['users'] %}{{ comma() }} + {{ user.get_profile_link() }} + {%- endfor -%} + {% endif -%} + {%- if sharing_info['more_users_count'] > 0 %} + {% trans %}and{% endtrans %} + <a + class="see-related-users" + data-url="{% url get_thread_shared_users %}" + data-thread-id="{{ thread.id }}" + >{% trans + more_count=sharing_info['more_users_count'] + %}{{ more_count }} more{% endtrans %} + </a> + {% endif %} + </p> + + {% if shared_groups_count %} + <h3>{% trans %}Groups{% endtrans %}</h3> + <p> + {% set comma = joiner(',') %} + {%- for group in sharing_info['groups'] -%}{{ comma() }} + {{ macros.user_group_link(group) }} + {%- endfor -%} + {% if sharing_info['more_groups_count'] > 0 %} + {% trans %}and{% endtrans %} + <a + class="see-related-groups" + data-url="{% url get_thread_shared_groups %}" + data-thread-id="{{ thread.id }}" + >{% trans more_count=sharing_info['more_groups_count'] %}{{ more_count }} more{% endtrans %} + </a> + {% endif %} + </p> + {% endif %} {% else %} <h2>{% trans %}Public thread{% endtrans %}</h2> <p>{% trans site_name=settings.APP_SHORT_NAME %}This thread is public, all members of {{ site_name }} can read this page.{% endtrans %}</p> |