diff -r 000000000000 -r 40c8f766c9b8 src/cm/media/js/client/c_icomment.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/client/c_icomment.js Mon Nov 23 15:14:29 2009 +0100 @@ -0,0 +1,433 @@ +// globals used: gConf, gPrefs, gLayout, gSync + + +// IComment == IHM comment class +IComment = function() { + + this.commentId = null ; + + var iCommentWidth = gLayout.getTopICommentsWidth() ; + var iCommentLeft = gConf['iCommentLeftPadding'] ; + + var changeToPending = gettext("change comment state to pending") ; + var changeToApprove = gettext("change comment state to approved") ; + var changeToUnapprove= gettext("change comment state to unapproved") ; + var cancelChange = gettext("cancel changing the state of this comment") ; + var pending = gettext("pending") ; + var approved = gettext("approved") ; + var unapproved = gettext("unapproved") ; + var cancel = gettext("cancel") ; + var showReplies = gettext("show replies") ; + var changeTo= gettext("change to:") ; + var reply = gettext("reply") ; + var editComment = gettext("edit comment") ; + var deleteComment = gettext("delete comment") ; + var edit = gettext("edit") ; + var del = gettext("delete") ; + var close = gettext("close") ; + + // no header, no body yet + this.overlay = new CY.Overlay( { + zIndex :3, + shim :false, /* until we really need it, no shim */ + visible :false, + width : iCommentWidth, + xy : [ iCommentLeft, 0 ], + headerContent : '
' + + '
' + + ''+ "vis" +'' + " " + + '' + edit + '' + " " + + '' + del + '' + " " + + '
' + + '
' + + changeTo + ' ' + + ''+ pending +'' + " " + + ''+ approved +'' + " " + + ''+ unapproved +'' + " " + + '' + cancel +'' + " " + + '
' + + 'X' + + '
', + bodyContent : '
' + + '' + + '' + + '' + showReplies +'' + " " + + '' + reply +'' + " " + + '' + + '
' + }); + + this.overlay.get('contentBox').addClass("c-comment") ; + + // attach to DOM + this.overlay.render('#leftcolumn'); + + this.animation = new CY.Anim({ + node: this.overlay.get('boundingBox'), + duration: gPrefs.get('general','animduration'), + easing: CY.Easing.easeOut + }); + + // CY.on won't work + this.overlay.get('contentBox').query(".c-close").on("click", this.onCloseCommentClick, this); + this.overlay.get('contentBox').query(".c-moderate").on("click", this.onModerateCommentClick, this); + this.overlay.get('contentBox').query(".c-state-pending").on("click", this.onPendingCommentClick, this); + this.overlay.get('contentBox').query(".c-state-approved").on("click", this.onApprovedCommentClick, this); + this.overlay.get('contentBox').query(".c-state-unapproved").on("click", this.onUnapprovedCommentClick, this); + this.overlay.get('contentBox').query(".c-state-cancel").on("click", this.onCancelStateChangeClick, this); + this.overlay.get('contentBox').query(".c-edit").on("click", this.onEditCommentClick, this); + this.overlay.get('contentBox').query(".c-delete").on("click", this.onDeleteCommentClick, this); + this.overlay.get('contentBox').query(".c-reply").on("click", this.onReplyCommentClick, this); + this.overlay.get('contentBox').query(".c-readreplies").on("click", this.onReadRepliesCommentClick, this); + + this.overlay.get('contentBox').query(".icomment-header").on("mouseenter", this.onMouseEnterHeader, this); + this.overlay.get('contentBox').query(".icomment-header").on("mouseleave", this.onMouseLeaveHeader, this); + + this.overlay.get('contentBox').on("click", this.onCommentClick, this); +} + +IComment.prototype = { + // checking readyForAction is not enough because handler could be called after animation end in the case : + // 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. + // (whan clicking very fast on the close button while close animation is taking place). + // SO : SHOULD ALWAYS CHECK FOR VISIBLE OVERLAY IN HANDLERS THAT COULD BE CALLED FROM AN ANIMATED OVERLAY + onCloseCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) + gSync.closeComment(this) ; + }, + onModerateCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + this.overlay.get('contentBox').query(".c-iactions").addClass("displaynone") ; + this.overlay.get('contentBox').query(".c-state-actions").removeClass("displaynone") ; + } + }, + onPendingCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + gSync.moderateComment(this, 'pending') ; + } + }, + onApprovedCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + gSync.moderateComment(this, 'approved') ; + } + }, + onUnapprovedCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + gSync.moderateComment(this, 'unapproved') ; + } + }, + onCancelStateChangeClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + this.overlay.get('contentBox').query(".c-iactions").removeClass("displaynone") ; + this.overlay.get('contentBox').query(".c-state-actions").addClass("displaynone") ; + } + }, + onDeleteCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) { + gSync.removeComment(this) ; + } + }, + onEditCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) + gSync.showEditForm(this) ; + }, + onReplyCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) + gSync.showReplyForm(this) ; + }, + onReadRepliesCommentClick : function (e) { + e.halt() ; // prevent click triggered on content box + if (readyForAction() && this.isVisible()) + gSync.openComment(this) ; + }, + onCommentClick : function (e) { + if (readyForAction() && this.isVisible()) { + // first condition here is checking it's not clicked via a 'show comments' link + if (e.target.get("target") == "_blank") { + var link = e.target ; + var showCommentUrl = sv_site_url + sv_text_view_show_comment_url ; + if (link.get('href').indexOf(showCommentUrl) == 0) { + var res = (new RegExp('comment_key=([^&]*)', "g")).exec(link.get('href')) ; + if (res != null) { + // open new comment .... we'll suppose it satisfies the filter for now + // TODO : should we reset the filter in this case ? instead of having the link open in a new window + var key = res[1] ; + var comment = gDb.getCommentByKey(key) ; + if (comment != null) { + e.halt() ; + if (!link.hasClass("c-permalink")) {// clicking on the permalink itself should do anything + checkForOpenedDialog(null, function() { + gSync.showSingleComment(comment) ; + }) ; + } + } + } + } + } + else { + if (gShowingAllComments) { + // 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 .... + if (!this._isHostingAForm()) { + var comment = gDb.getComment(this.commentId) ; + checkForOpenedDialog(null, function() { + if (comment != null) + gSync.showSingleComment(comment) ; + }) + } + } + else + gSync.activate(this) ; + } + } + }, + + onMouseEnterHeader : function () { + if (readyForAction() && this.isVisible()) { + this.overlay.get('contentBox').query(".c-permalink").removeClass('displaynone'); + } + }, + + onMouseLeaveHeader : function () { + if (readyForAction() && this.isVisible()) { + this.overlay.get('contentBox').query(".c-permalink").addClass('displaynone'); + } + }, + + setWidth : function(width) { + this.overlay.get('boundingBox').setStyle("width", width + 'px'); + }, + + activate:function() { + // debug !! +// CY.log('activate' + this.commentId) ; + this.overlay.get('boundingBox').addClass('c-focus-comment') ; + this.overlay.get('contentBox').query(".c-close").addClass('c-iclose-focus') ; + this.overlay.get('contentBox').query(".c-close").removeClass('c-iclose') ; + + }, + + deactivate:function() { + // debug !! +// CY.log('deactivate' + this.commentId) ; + this.overlay.get('boundingBox').removeClass('c-focus-comment') ; + this.overlay.get('contentBox').query(".c-close").addClass('c-iclose') ; + this.overlay.get('contentBox').query(".c-close").removeClass('c-iclose-focus') ; + + }, + hide:function() { + // is IComment the top active one ? + if (gIComments.isTopActive(this.commentId)) { // then try to activate next in displayed list + if (!gIComments.activateVisibleNext()) + gIComments.deactivate() ; + } + + if (this.isVisible()) { + this.overlay.hide(); + this.overlay.blur() ; + } + }, + hideContent:function() { + this.overlay.get('contentBox').query(".icomment-header").addClass('displaynone') ; + this.overlay.get('contentBox').query(".icomment-body").addClass('displaynone') ; + }, + showContent:function() { + this.overlay.get('contentBox').query(".icomment-header").removeClass('displaynone') ; + this.overlay.get('contentBox').query(".icomment-body").removeClass('displaynone') ; + }, + isVisible:function() { + return this.overlay.get('visible') ; + }, + show:function() { + this.hideReadRepliesLnk() ; // for now + return this.overlay.show() ; + }, + showReadRepliesLnk:function() { + this.overlay.get('contentBox').query(".c-readreplies").removeClass('displaynone') ; + }, + hideReadRepliesLnk:function() { + this.overlay.get('contentBox').query(".c-readreplies").addClass('displaynone') ; + }, + changeModeration:function(comment) { + var moderationLnk = this.overlay.get('contentBox').query(".c-moderate") ; + moderationLnk.set("innerHTML", gettext(comment.state)) ; + + moderationLnk.removeClass("c-state-approved") ; + moderationLnk.removeClass("c-state-pending") ; + moderationLnk.removeClass("c-state-unapproved") ; + moderationLnk.addClass("c-state-" + comment.state) ; + + this.overlay.get('contentBox').query(".c-iactions").removeClass("displaynone") ; + this.overlay.get('contentBox').query(".c-state-actions").addClass("displaynone") ; + }, + isfetched : function() { + return (this.commentId != null) ; + }, + unfetch : function() { + this.commentId = null ; + }, + fetch : function(comment) { + this.commentId = comment.id ; + var boundingBoxNode = this.overlay.get('boundingBox') ; + + // TITLE + var titleInfos = interpolate(gettext('last modified on %(date)s'),{'date':comment.modified_user_str}, true) ; + + var modifDateTooltip = (comment.modified == comment.created) ? '' : ' * ' ; + var permaTitle = gettext('Permalink to this comment') ; + var permalink = 'ΒΆ ' ; + + var infos = interpolate(gettext('by %(name)s, created on %(date)s'),{'name':comment.name, 'date':comment.created_user_str}, true) ; + + var newTitleContent = '
' + comment.title + permalink + '
' + infos + '
' ; + + var newTitleNode = CY.Node.create(newTitleContent) ; + var prevTitleNode = boundingBoxNode.query(".c-header") ; + if (prevTitleNode == null) // first time, no title yet + boundingBoxNode.query('.icomment-header').insertBefore(newTitleNode, boundingBoxNode.one('.c-iactions')) ; + else + prevTitleNode.get('parentNode').replaceChild(newTitleNode, prevTitleNode) ; + + // TAG + var newTagNode = CY.Node.create('
' + 'tags:' + '' + comment.tags + '
') ; + var prevTagNode = boundingBoxNode.query(".c-tags") ; + if (prevTagNode == null) + boundingBoxNode.query('.icomment-header').appendChild(newTagNode) ; + else + prevTagNode.get('parentNode').replaceChild(newTagNode, prevTagNode) ; + // NO TAG ? + if (comment.tags == "") + newTagNode.addClass('displaynone') ; + + // CONTENT + var newContentNode = CY.Node.create('' + comment.content_html + '') ; + var prevContentNode = boundingBoxNode.query(".c-content") ; + if (prevContentNode == null) + boundingBoxNode.query('.icomment-body').appendChild(newContentNode) ; + else + prevContentNode.get('parentNode').replaceChild(newContentNode, prevContentNode) ; + + // PERMALINK + boundingBoxNode.query(".c-permalink").set("href",sv_site_url + comment.permalink) ; + + // MODERATION + this.changeModeration(comment) ; + // also change link title to give users the possibility to know comment id (to be able to reference this exact comment in GET arguments) + var moderationLnk = this.overlay.get('contentBox').query(".c-moderate") ; + //var cid = (comment.reply_to_id == null) ? this.commentId : "" ; + moderationLnk.set("title", "click to change comment ID visibility".replace(/ID/, this.commentId).replace(/ /, " ")) ; + + // open links in new window : + var links = boundingBoxNode.queryAll(".c-content a") ; + if (links != null) + links.setAttribute( "target" , "_blank" ) ; + links = boundingBoxNode.queryAll(".c-header-title a") ; + if (links != null) + links.setAttribute( "target" , "_blank" ) ; + + this.permAdapt(comment) ; + }, + + permAdapt : function(comment) { + // this comment permissions + var delLnk = this.overlay.get('contentBox').query(".c-delete") ; + if (delLnk) { // there will be a server side check anyway + if (!comment.can_delete) + delLnk.addClass('displaynone') ; + else + delLnk.removeClass('displaynone') ; + } + + var editLnk = this.overlay.get('contentBox').query(".c-edit") ; + if (editLnk) { + if (!comment.can_edit) + editLnk.addClass('displaynone') ; + else + editLnk.removeClass('displaynone') ; + } + + var replyLnk = this.overlay.get('contentBox').query(".c-reply") ; + if (replyLnk) { + if (!hasPerm("can_create_comment")) + replyLnk.addClass('displaynone') ; + else + replyLnk.removeClass('displaynone') ; + } + + var moderateLnk = this.overlay.get('contentBox').query(".c-moderate") ; + if (moderateLnk) { + if (!comment.can_moderate) + moderateLnk.addClass('displaynone') ; + else + moderateLnk.removeClass('displaynone') ; + } + }, + setThreadPad : function(pad) { // TODO review ... + this.overlay.get('contentBox').query('.yui-widget-hd').setStyle('paddingLeft', pad + 'px') ; + this.overlay.get('contentBox').query('.yui-widget-bd').setStyle('paddingLeft', pad + 'px') ; + + }, + setPosition : function(xy) { + var boundingBoxNode = this.overlay.get('boundingBox') ; + + boundingBoxNode.setStyle("opacity", 1); // TODO check this is still usefull + boundingBoxNode.setXY(xy) ; + }, + getPosition : function(xy) { + var boundingBoxNode = this.overlay.get('boundingBox') ; + + return boundingBoxNode.getXY() ; + }, + onAnimationEnd : function() { + if (!CY.Lang.isUndefined(this['animation-handle']) && !CY.Lang.isNull(this['animation-handle'])) { + this['animation-handle'].detach() ; + this['animation-handle'] = null ; +// CY.log('detached...') ; + } + gIComments.signalAnimationEnd() ; + if (gIComments.animationsEnded()) + gIComments.whenAnimationsEnd() ; + }, + + //aa = new CY.Anim({node:gIComments._c[0].overlay.get('boundingBox'), to:{xy : [0,0]}, duration:2.0}) + setAnimationToPosition : function(toXY) { + var boundingBoxNode = this.overlay.get('boundingBox') ; + + // ANIMATION + // 2 lines of optim that could be removed (0.011) + if (gPrefs.get('general','animduration') < 0.011) + boundingBoxNode.setXY(toXY) ; + + this.animation.set('to', { xy: toXY}); + this.animation.set('duration', gPrefs.get('general','animduration')) ; // shouldn't be here really ... + this['animation-handle'] = this.animation.on('end', this.onAnimationEnd, this); + + return this.animation ; + }, + setWidth : function(width) { + var boundingBoxNode = this.overlay.get('boundingBox') ; + + // POSITION + boundingBoxNode.setStyle("width", width + 'px'); + }, + getHeight : function() { + return this.overlay.get('boundingBox').get('offsetHeight') ; + }, + scrollIntoView : function() { + //this.isVisible() && + if (!this.overlay.get('contentBox').inViewportRegion()) + this.overlay.get('contentBox').scrollIntoView(true) ; + }, + _isHostingAForm : function() { + return (this.isVisible() && ((gNewReplyHost != null && gNewReplyHost == this) || (gEditICommentHost != null && gEditICommentHost == this))); + } + +}