src/cm/media/js/client/c_icomment.js
changeset 0 40c8f766c9b8
child 8 1a495fcc40f9
equal deleted inserted replaced
-1:000000000000 0:40c8f766c9b8
       
     1 // globals used: gConf, gPrefs, gLayout, gSync
       
     2 
       
     3 
       
     4 // IComment == IHM comment class 
       
     5 IComment = function() {
       
     6 	
       
     7 	this.commentId = null ;
       
     8 	
       
     9 	var iCommentWidth = gLayout.getTopICommentsWidth() ;	
       
    10 	var iCommentLeft = gConf['iCommentLeftPadding'] ;
       
    11 	
       
    12 	var changeToPending = gettext("change comment state to pending") ;
       
    13 	var changeToApprove = gettext("change comment state to approved") ;
       
    14 	var changeToUnapprove= gettext("change comment state to unapproved") ;
       
    15 	var cancelChange = gettext("cancel changing the state of this comment") ;
       
    16 	var pending = gettext("pending") ;
       
    17 	var approved = gettext("approved") ;
       
    18 	var unapproved = gettext("unapproved") ;
       
    19 	var cancel = gettext("cancel") ;
       
    20 	var showReplies = gettext("show replies") ;
       
    21 	var changeTo= gettext("change to:") ;
       
    22 	var reply = gettext("reply") ;
       
    23 	var editComment = gettext("edit comment") ;
       
    24 	var deleteComment = gettext("delete comment") ;
       
    25 	var edit = gettext("edit") ;
       
    26 	var del = gettext("delete") ;
       
    27 	var close = gettext("close") ;
       
    28 	
       
    29 	// no header, no body yet 
       
    30 	this.overlay = new CY.Overlay( {
       
    31 		zIndex :3,
       
    32 		shim :false, /* until we really need it, no shim */
       
    33 		visible :false,
       
    34 		width : iCommentWidth,
       
    35 		xy : [ iCommentLeft, 0 ],
       
    36 		headerContent :	'<div class="icomment-header">' +
       
    37 							'<div class="c-iactions">' +  
       
    38 								'<a class="c-moderate c-action" title="">'+ "vis" +'</a>' + " " +  
       
    39 								'<a class="c-edit c-action" title="'+ editComment +'" alt="' + editComment + '">' + edit + '</a>' + " " +   
       
    40 								'<a class="c-delete c-action" title="'+ deleteComment +'" alt="' + deleteComment + '">' + del + '</a>' + " " +   
       
    41 							'</div>' +
       
    42 							'<div class="c-state-actions displaynone">' +
       
    43 								changeTo + '&nbsp;' +
       
    44 								'<a class="c-state-pending c-action" title="' + changeToPending + '" alt="' + changeToPending + '">'+ pending +'</a>' + " " +  
       
    45 								'<a class="c-state-approved c-action" title="' + changeToApprove + '" alt="' + changeToApprove + '">'+ approved +'</a>' + " " +  
       
    46 								'<a class="c-state-unapproved c-action" title="' + changeToUnapprove + '" alt="' + changeToUnapprove + '">'+ unapproved +'</a>' + " " +  
       
    47 								'<a class="c-state-cancel c-action" title="' + cancelChange + '" alt="' + cancelChange + '">' + cancel +'</a>' + " " +  
       
    48 							'</div>' + 
       
    49 							'<a class="c-close c-iclose c-action" title="'+ close + '" alt="' + close + '"><em>X</em></a>' +
       
    50 						'</div>',
       
    51 		bodyContent :	'<div class="icomment-body">' +
       
    52 						'<span class="c-content"></span>' +
       
    53 						'<span class="c-ireplyactions">' +
       
    54 						'<a class="c-readreplies c-action" title="'+ showReplies +'" alt="' + showReplies + '">' + showReplies +'</a>' + " " +  
       
    55 						'<a class="c-reply c-action" title="'+ reply +'" alt="' + reply + '">' + reply +'</a>' + "&nbsp;" +  
       
    56 						'</span>' +
       
    57 						'</div>'
       
    58 	});
       
    59 	
       
    60 	this.overlay.get('contentBox').addClass("c-comment") ;
       
    61 	
       
    62 	// attach to DOM
       
    63 	this.overlay.render('#leftcolumn');
       
    64 	
       
    65 	this.animation = new CY.Anim({
       
    66         node: this.overlay.get('boundingBox'),
       
    67         duration: gPrefs.get('general','animduration'),
       
    68         easing: CY.Easing.easeOut
       
    69     });		
       
    70 
       
    71 	// CY.on won't work 
       
    72 	this.overlay.get('contentBox').query(".c-close").on("click", this.onCloseCommentClick, this);
       
    73 	this.overlay.get('contentBox').query(".c-moderate").on("click", this.onModerateCommentClick, this);
       
    74 	this.overlay.get('contentBox').query(".c-state-pending").on("click", this.onPendingCommentClick, this);
       
    75 	this.overlay.get('contentBox').query(".c-state-approved").on("click", this.onApprovedCommentClick, this);
       
    76 	this.overlay.get('contentBox').query(".c-state-unapproved").on("click", this.onUnapprovedCommentClick, this);
       
    77 	this.overlay.get('contentBox').query(".c-state-cancel").on("click", this.onCancelStateChangeClick, this);
       
    78 	this.overlay.get('contentBox').query(".c-edit").on("click", this.onEditCommentClick, this);
       
    79 	this.overlay.get('contentBox').query(".c-delete").on("click", this.onDeleteCommentClick, this);
       
    80 	this.overlay.get('contentBox').query(".c-reply").on("click", this.onReplyCommentClick, this);
       
    81 	this.overlay.get('contentBox').query(".c-readreplies").on("click", this.onReadRepliesCommentClick, this);
       
    82 
       
    83 	this.overlay.get('contentBox').query(".icomment-header").on("mouseenter", this.onMouseEnterHeader, this);
       
    84 	this.overlay.get('contentBox').query(".icomment-header").on("mouseleave", this.onMouseLeaveHeader, this);
       
    85 	
       
    86 	this.overlay.get('contentBox').on("click", this.onCommentClick, this);
       
    87 }
       
    88 
       
    89 IComment.prototype = {
       
    90 	// checking readyForAction is not enough because handler could be called after animation end in the case : 
       
    91 	// close btn is clicked before animation end (so before overlay gets hidden) but handler is called after so preventClickOn is false and close is called on an invisible overlay.
       
    92 	// (whan clicking very fast on the close button while close animation is taking place).
       
    93 	// SO : SHOULD ALWAYS CHECK FOR VISIBLE OVERLAY IN HANDLERS THAT COULD BE CALLED FROM AN ANIMATED OVERLAY
       
    94 	onCloseCommentClick : function (e) {
       
    95 		e.halt() ; // prevent click triggered on content box
       
    96 		if (readyForAction() && this.isVisible()) 
       
    97 			gSync.closeComment(this) ;
       
    98 	},
       
    99 	onModerateCommentClick : function (e) {
       
   100 		e.halt() ; // prevent click triggered on content box
       
   101 		if (readyForAction() && this.isVisible()) {
       
   102 			this.overlay.get('contentBox').query(".c-iactions").addClass("displaynone") ;
       
   103 			this.overlay.get('contentBox').query(".c-state-actions").removeClass("displaynone") ;
       
   104 		}
       
   105 	},
       
   106 	onPendingCommentClick : function (e) {
       
   107 		e.halt() ; // prevent click triggered on content box
       
   108 		if (readyForAction() && this.isVisible()) {
       
   109 			gSync.moderateComment(this, 'pending') ;
       
   110 		}
       
   111 	},
       
   112 	onApprovedCommentClick : function (e) {
       
   113 		e.halt() ; // prevent click triggered on content box
       
   114 		if (readyForAction() && this.isVisible()) {
       
   115 			gSync.moderateComment(this, 'approved') ;
       
   116 		}
       
   117 	},
       
   118 	onUnapprovedCommentClick : function (e) {
       
   119 		e.halt() ; // prevent click triggered on content box
       
   120 		if (readyForAction() && this.isVisible()) {
       
   121 			gSync.moderateComment(this, 'unapproved') ;
       
   122 		}
       
   123 	},
       
   124 	onCancelStateChangeClick : function (e) {
       
   125 		e.halt() ; // prevent click triggered on content box
       
   126 		if (readyForAction() && this.isVisible()) {
       
   127 			this.overlay.get('contentBox').query(".c-iactions").removeClass("displaynone") ;
       
   128 			this.overlay.get('contentBox').query(".c-state-actions").addClass("displaynone") ;
       
   129 		}
       
   130 	},
       
   131 	onDeleteCommentClick : function (e) {
       
   132 		e.halt() ; // prevent click triggered on content box
       
   133 		if (readyForAction() && this.isVisible()) {
       
   134 			gSync.removeComment(this) ;
       
   135 		}
       
   136 	},
       
   137 	onEditCommentClick : function (e) {
       
   138 		e.halt() ; // prevent click triggered on content box
       
   139 		if (readyForAction() && this.isVisible()) 
       
   140 			gSync.showEditForm(this) ;
       
   141 	},
       
   142 	onReplyCommentClick : function (e) {
       
   143 		e.halt() ; // prevent click triggered on content box
       
   144 		if (readyForAction() && this.isVisible()) 
       
   145 			gSync.showReplyForm(this) ;
       
   146 	},
       
   147 	onReadRepliesCommentClick : function (e) {
       
   148 		e.halt() ; // prevent click triggered on content box
       
   149 		if (readyForAction() && this.isVisible()) 
       
   150 			gSync.openComment(this) ;
       
   151 	},
       
   152 	onCommentClick : function (e) {
       
   153 		if (readyForAction() && this.isVisible()) {
       
   154 			// first condition here is checking it's not clicked via a 'show comments' 	link
       
   155 			if (e.target.get("target") == "_blank") {
       
   156 				var link = e.target ;
       
   157 				var showCommentUrl = sv_site_url + sv_text_view_show_comment_url ;
       
   158 				if (link.get('href').indexOf(showCommentUrl) == 0) { 
       
   159 					var res = (new RegExp('comment_key=([^&]*)', "g")).exec(link.get('href')) ;
       
   160 					if (res != null) {
       
   161 						// open new comment .... we'll suppose it satisfies the filter for now
       
   162 						// TODO : should we reset the filter in this case ? instead of having the link open in a new window
       
   163 						var key = res[1] ;
       
   164 						var comment = gDb.getCommentByKey(key) ;
       
   165 						if (comment != null) {
       
   166 							e.halt() ;
       
   167 							if (!link.hasClass("c-permalink")) {// clicking on the permalink itself should do anything
       
   168 								checkForOpenedDialog(null, function() {
       
   169 									gSync.showSingleComment(comment) ;
       
   170 								}) ;
       
   171 							}
       
   172 						}
       
   173 					}
       
   174 				}
       
   175 			}
       
   176 			else {
       
   177 				if (gShowingAllComments) {
       
   178 					// next special dirty case test explained : when editing/replying to a comment with gShowingAllComments a click in the edit/reply form also is a click on the iComment, in this case we don't want to showSingleComment .... 
       
   179 					if (!this._isHostingAForm()) {
       
   180 						var comment = gDb.getComment(this.commentId) ;
       
   181 						checkForOpenedDialog(null, function() {
       
   182 							if (comment != null) 
       
   183 								gSync.showSingleComment(comment) ;
       
   184 						})
       
   185 					}
       
   186 				}
       
   187 				else 
       
   188 					gSync.activate(this) ;
       
   189 			}
       
   190 		}
       
   191 	},	
       
   192 	
       
   193 	onMouseEnterHeader : function () {
       
   194 		if (readyForAction() && this.isVisible()) {
       
   195 			this.overlay.get('contentBox').query(".c-permalink").removeClass('displaynone');
       
   196 		}
       
   197 	},
       
   198 	
       
   199 	onMouseLeaveHeader : function () {
       
   200 		if (readyForAction() && this.isVisible()) {
       
   201 			this.overlay.get('contentBox').query(".c-permalink").addClass('displaynone');
       
   202 		}
       
   203 	},	
       
   204 	
       
   205 	setWidth : function(width) {
       
   206 	    this.overlay.get('boundingBox').setStyle("width", width + 'px');
       
   207 	},
       
   208 	
       
   209 	activate:function() {
       
   210 		// debug !!
       
   211 //		CY.log('activate' + this.commentId) ;
       
   212 		this.overlay.get('boundingBox').addClass('c-focus-comment') ;
       
   213 		this.overlay.get('contentBox').query(".c-close").addClass('c-iclose-focus') ;
       
   214 		this.overlay.get('contentBox').query(".c-close").removeClass('c-iclose') ;
       
   215 		
       
   216 	},
       
   217 	
       
   218 	deactivate:function() {
       
   219 		// debug !!
       
   220 //		CY.log('deactivate' + this.commentId) ;
       
   221 		this.overlay.get('boundingBox').removeClass('c-focus-comment') ;
       
   222 		this.overlay.get('contentBox').query(".c-close").addClass('c-iclose') ;
       
   223 		this.overlay.get('contentBox').query(".c-close").removeClass('c-iclose-focus') ;
       
   224 		
       
   225 	},
       
   226 	hide:function() {
       
   227 		// is IComment the top active one ?
       
   228 		if (gIComments.isTopActive(this.commentId)) { // then try to activate next in displayed list
       
   229 			if (!gIComments.activateVisibleNext())
       
   230 				gIComments.deactivate() ;
       
   231 		}
       
   232 
       
   233 		if (this.isVisible()) {
       
   234 			this.overlay.hide();
       
   235 			this.overlay.blur() ;
       
   236 		}
       
   237 	},
       
   238 	hideContent:function() {
       
   239 		this.overlay.get('contentBox').query(".icomment-header").addClass('displaynone') ;
       
   240 		this.overlay.get('contentBox').query(".icomment-body").addClass('displaynone') ;
       
   241 	},
       
   242 	showContent:function() {
       
   243 		this.overlay.get('contentBox').query(".icomment-header").removeClass('displaynone') ;
       
   244 		this.overlay.get('contentBox').query(".icomment-body").removeClass('displaynone') ;
       
   245 	},
       
   246 	isVisible:function() {
       
   247 		return this.overlay.get('visible') ;
       
   248 	},
       
   249 	show:function() {
       
   250 		this.hideReadRepliesLnk() ; // for now
       
   251 		return this.overlay.show() ;
       
   252 	},
       
   253 	showReadRepliesLnk:function() {
       
   254 		this.overlay.get('contentBox').query(".c-readreplies").removeClass('displaynone') ;
       
   255 	},
       
   256 	hideReadRepliesLnk:function() {
       
   257 		this.overlay.get('contentBox').query(".c-readreplies").addClass('displaynone') ;
       
   258 	},
       
   259 	changeModeration:function(comment) {
       
   260 		var moderationLnk = this.overlay.get('contentBox').query(".c-moderate") ;
       
   261 		moderationLnk.set("innerHTML", gettext(comment.state)) ;
       
   262 		
       
   263 		moderationLnk.removeClass("c-state-approved") ;
       
   264 		moderationLnk.removeClass("c-state-pending") ;
       
   265 		moderationLnk.removeClass("c-state-unapproved") ;
       
   266 		moderationLnk.addClass("c-state-" + comment.state) ;
       
   267 
       
   268 		this.overlay.get('contentBox').query(".c-iactions").removeClass("displaynone") ;
       
   269 		this.overlay.get('contentBox').query(".c-state-actions").addClass("displaynone") ;
       
   270 	},
       
   271 	isfetched : function() {
       
   272 		return (this.commentId != null) ;
       
   273 	},
       
   274 	unfetch : function() {
       
   275 		this.commentId = null ;
       
   276 	},
       
   277 	fetch : function(comment) {
       
   278 		this.commentId = comment.id ;
       
   279 		var boundingBoxNode = this.overlay.get('boundingBox') ;
       
   280 		
       
   281 		// TITLE
       
   282 		var titleInfos = interpolate(gettext('last modified on %(date)s'),{'date':comment.modified_user_str}, true) ;
       
   283 		
       
   284 		var modifDateTooltip = (comment.modified == comment.created) ? '' : '<a title="' + titleInfos + '"> * </a>' ;
       
   285 		var permaTitle = gettext('Permalink to this comment') ;
       
   286 		var permalink = '<a class="c-permalink displaynone c-action" target="_blank" title="'+ permaTitle +'" href="" >ΒΆ&nbsp;</a>' ;
       
   287 		
       
   288 		var infos = interpolate(gettext('by %(name)s, created on %(date)s'),{'name':comment.name, 'date':comment.created_user_str}, true) ;
       
   289 		
       
   290 		var newTitleContent = '<span class="c-header"><div class="c-header-title">' + comment.title + permalink + '</div><div class="c-infos">' + infos + '</div></span>' ;
       
   291 		
       
   292 		var newTitleNode = CY.Node.create(newTitleContent) ;
       
   293 		var prevTitleNode = boundingBoxNode.query(".c-header") ;
       
   294 		if (prevTitleNode == null) // first time, no title yet
       
   295 			boundingBoxNode.query('.icomment-header').insertBefore(newTitleNode, boundingBoxNode.one('.c-iactions')) ;
       
   296 		else 
       
   297 			prevTitleNode.get('parentNode').replaceChild(newTitleNode, prevTitleNode) ;
       
   298 
       
   299 		// TAG
       
   300 		var newTagNode = CY.Node.create('<div class="c-tags"><span class="c-tags-infos">' + 'tags:' + '</span>' + comment.tags + '</div>') ;
       
   301 		var prevTagNode = boundingBoxNode.query(".c-tags") ;
       
   302 		if (prevTagNode == null) 
       
   303 			boundingBoxNode.query('.icomment-header').appendChild(newTagNode) ;
       
   304 		else 
       
   305 			prevTagNode.get('parentNode').replaceChild(newTagNode, prevTagNode) ;
       
   306 		// NO TAG ?
       
   307 		if (comment.tags == "")
       
   308 			newTagNode.addClass('displaynone') ;
       
   309 
       
   310 		// CONTENT
       
   311 		var newContentNode = CY.Node.create('<span class="c-content">' + comment.content_html + '</span>') ;
       
   312 		var prevContentNode = boundingBoxNode.query(".c-content") ;
       
   313 		if (prevContentNode == null) 
       
   314 			boundingBoxNode.query('.icomment-body').appendChild(newContentNode) ;
       
   315 		else 
       
   316 			prevContentNode.get('parentNode').replaceChild(newContentNode, prevContentNode) ;
       
   317 
       
   318 		// PERMALINK
       
   319 		boundingBoxNode.query(".c-permalink").set("href",sv_site_url + comment.permalink) ;
       
   320 
       
   321 		// MODERATION
       
   322 		this.changeModeration(comment) ;
       
   323 		// also change link title to give users the possibility to know comment id (to be able to reference this exact comment in GET arguments) 
       
   324 		var moderationLnk = this.overlay.get('contentBox').query(".c-moderate") ;
       
   325 		//var cid = (comment.reply_to_id == null) ? this.commentId : "" ;
       
   326 		moderationLnk.set("title", "click to change comment ID visibility".replace(/ID/, this.commentId).replace(/  /, " ")) ;
       
   327 		
       
   328 		// open links in new window :
       
   329 		var links = boundingBoxNode.queryAll(".c-content a") ;
       
   330 		if (links != null)
       
   331 			links.setAttribute( "target" , "_blank" ) ;
       
   332 		links = boundingBoxNode.queryAll(".c-header-title a") ;
       
   333 		if (links != null)
       
   334 			links.setAttribute( "target" , "_blank" ) ;
       
   335 		
       
   336 		this.permAdapt(comment) ;
       
   337 	},
       
   338 
       
   339 	permAdapt : function(comment) {
       
   340 		// this comment permissions 
       
   341 		var delLnk = this.overlay.get('contentBox').query(".c-delete") ; 
       
   342 		if (delLnk) {	// there will be a server side check anyway
       
   343 			if (!comment.can_delete)
       
   344 				delLnk.addClass('displaynone') ;
       
   345 			else 
       
   346 				delLnk.removeClass('displaynone') ;
       
   347 		}
       
   348 
       
   349 		var editLnk = this.overlay.get('contentBox').query(".c-edit") ; 
       
   350 		if (editLnk) {	
       
   351 			if (!comment.can_edit) 
       
   352 				editLnk.addClass('displaynone') ;
       
   353 			else 
       
   354 				editLnk.removeClass('displaynone') ;
       
   355 		}
       
   356 		
       
   357 		var replyLnk = this.overlay.get('contentBox').query(".c-reply") ;
       
   358 		if (replyLnk) {
       
   359 			if (!hasPerm("can_create_comment"))
       
   360 				replyLnk.addClass('displaynone') ;
       
   361 			else 
       
   362 				replyLnk.removeClass('displaynone') ;
       
   363 		}
       
   364 
       
   365 		var moderateLnk = this.overlay.get('contentBox').query(".c-moderate") ; 
       
   366 		if (moderateLnk) {	
       
   367 			if (!comment.can_moderate)  
       
   368 				moderateLnk.addClass('displaynone') ;
       
   369 			else 
       
   370 				moderateLnk.removeClass('displaynone') ;
       
   371 		}
       
   372 	},
       
   373 	setThreadPad : function(pad) { // TODO review ...
       
   374 		this.overlay.get('contentBox').query('.yui-widget-hd').setStyle('paddingLeft', pad + 'px') ;
       
   375 		this.overlay.get('contentBox').query('.yui-widget-bd').setStyle('paddingLeft', pad + 'px') ;
       
   376 		
       
   377 	},
       
   378 	setPosition : function(xy) {
       
   379 		var boundingBoxNode = this.overlay.get('boundingBox') ;
       
   380 
       
   381 	    boundingBoxNode.setStyle("opacity", 1); // TODO check this is still usefull
       
   382 	   	boundingBoxNode.setXY(xy) ;
       
   383 	},
       
   384 	getPosition : function(xy) {
       
   385 		var boundingBoxNode = this.overlay.get('boundingBox') ;
       
   386 
       
   387 	   	return boundingBoxNode.getXY() ;
       
   388 	},
       
   389 	onAnimationEnd : function() {
       
   390 		if (!CY.Lang.isUndefined(this['animation-handle']) && !CY.Lang.isNull(this['animation-handle'])) { 
       
   391 			this['animation-handle'].detach() ;
       
   392 			this['animation-handle'] = null ;
       
   393 //			CY.log('detached...') ;
       
   394 		}
       
   395 		gIComments.signalAnimationEnd() ;
       
   396 		if (gIComments.animationsEnded())
       
   397 			gIComments.whenAnimationsEnd() ;		
       
   398 	},
       
   399 
       
   400 	//aa = new CY.Anim({node:gIComments._c[0].overlay.get('boundingBox'), to:{xy : [0,0]}, duration:2.0})
       
   401 	setAnimationToPosition : function(toXY) {
       
   402 		var boundingBoxNode = this.overlay.get('boundingBox') ;
       
   403 		
       
   404 		// ANIMATION
       
   405 	    // 2 lines of optim that could be removed (0.011)
       
   406 		if (gPrefs.get('general','animduration') < 0.011) 
       
   407 	    	boundingBoxNode.setXY(toXY) ;
       
   408 		
       
   409 		this.animation.set('to', { xy: toXY});
       
   410 		this.animation.set('duration', gPrefs.get('general','animduration')) ; // shouldn't be here really ...
       
   411 		this['animation-handle'] = this.animation.on('end', this.onAnimationEnd, this);
       
   412 		
       
   413 		return this.animation ;
       
   414 	},
       
   415 	setWidth : function(width) {
       
   416 		var boundingBoxNode = this.overlay.get('boundingBox') ;
       
   417 
       
   418 		// POSITION
       
   419 	    boundingBoxNode.setStyle("width", width + 'px');
       
   420 	},
       
   421 	getHeight : function() {
       
   422 		return this.overlay.get('boundingBox').get('offsetHeight') ;
       
   423 	},
       
   424 	scrollIntoView : function() {
       
   425 		//this.isVisible() && 
       
   426 		if (!this.overlay.get('contentBox').inViewportRegion())
       
   427 			this.overlay.get('contentBox').scrollIntoView(true) ;
       
   428 	},
       
   429 	_isHostingAForm : function() {
       
   430 		return (this.isVisible() && ((gNewReplyHost != null && gNewReplyHost == this) || (gEditICommentHost != null && gEditICommentHost == this)));
       
   431 	}
       
   432 
       
   433 }