1 /** 2 * Discussion TopicReplies component. 3 * 4 * @namespace Alfresco 5 * @class Alfresco.TopicReplies 6 */ 7 (function() 8 { 9 /** 10 * YUI Library aliases 11 */ 12 var Dom = YAHOO.util.Dom, 13 Event = YAHOO.util.Event, 14 Element = YAHOO.util.Element; 15 16 /** 17 * Alfresco Slingshot aliases 18 */ 19 var $html = Alfresco.util.encodeHTML; 20 21 /** 22 * TopicReplies constructor. 23 * 24 * @param {String} htmlId The HTML id of the parent element 25 * @return {Alfresco.TopicReplies} The new Reply instance 26 * @constructor 27 */ 28 Alfresco.TopicReplies = function(htmlId) 29 { 30 /* Mandatory properties */ 31 this.name = "Alfresco.TopicReplies"; 32 this.id = htmlId; 33 34 /* Initialise prototype properties */ 35 this.widgets = {}; 36 this.editData = 37 { 38 formDiv : null, 39 viewDiv : null 40 }; 41 42 /* Register this component */ 43 Alfresco.util.ComponentManager.register(this); 44 45 /* Load YUI Components */ 46 Alfresco.util.YUILoaderHelper.require(["dom", "event", "element"], this.onComponentsLoaded, this); 47 48 /* Decoupled event listeners */ 49 YAHOO.Bubbling.on("addReplyToPost", this.onAddReplyToPost, this); 50 YAHOO.Bubbling.on("topicDataChanged", this.onTopicDataChanged, this); 51 52 return this; 53 }; 54 55 Alfresco.TopicReplies.prototype = 56 { 57 /** 58 * Object container for initialization options 59 * 60 * @property options 61 * @type object 62 */ 63 options: 64 { 65 /** 66 * Current siteId. 67 * 68 * @property siteId 69 * @type string 70 */ 71 siteId: "", 72 73 /** 74 * Current containerId. 75 * 76 * @property containerId 77 * @type string 78 */ 79 containerId: "discussions", 80 81 /** 82 * Reference to the topic for which to display replies. 83 */ 84 topicRef: "", 85 86 /** 87 * Id of the topic to display 88 * Note: this is solely used for generating the activities feed 89 */ 90 topicId: "", 91 92 /** 93 * Title of the topic for which replies are displayed 94 * Note: this is solely used for generating the activities feed 95 */ 96 topicTitle: "" 97 }, 98 99 /** 100 * Stores editing related data 101 */ 102 editData : null, 103 104 /** 105 * Stores the displayed data 106 */ 107 repliesData: null, 108 109 /** 110 * Object container for storing YUI widget instances. 111 * 112 * @property widgets 113 * @type object 114 */ 115 widgets: null, 116 117 /** 118 * Set multiple initialization options at once. 119 * 120 * @method setOptions 121 * @param obj {object} Object literal specifying a set of options 122 */ 123 setOptions: function TopicReplies_setOptions(obj) 124 { 125 this.options = YAHOO.lang.merge(this.options, obj); 126 return this; 127 }, 128 129 /** 130 * Set multiple initialization options at once. 131 * 132 * @method setOptions 133 * @param obj {object} Object literal specifying a set of options 134 */ 135 setMessages: function TopicReplies_setMessages(obj) 136 { 137 Alfresco.util.addMessages(obj, this.name); 138 return this; 139 }, 140 141 /** 142 * Fired by YUILoaderHelper when required component script files have 143 * been loaded into the browser. 144 * 145 * @method onComponentsLoaded 146 */ 147 onComponentsLoaded: function TopicReplies_onComponentsLoaded() 148 { 149 Event.onContentReady(this.id, this.onReady, this, true); 150 }, 151 152 /** 153 * Fired by YUI when parent element is available for scripting. 154 * Component initialisation, including instantiation of YUI widgets and event listener binding. 155 * 156 * @method onReady 157 */ 158 onReady: function TopicReplies_onReady() 159 { 160 // Hook action events. 161 var me = this; 162 var fnActionHandlerDiv = function TopicReplies_fnActionHandlerDiv(layer, args) 163 { 164 var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div"); 165 if (owner !== null) 166 { 167 var action = ""; 168 action = owner.className; 169 if (typeof me[action] == "function") 170 { 171 var id = ''; 172 id = owner.id; 173 var nodeRef = ''; 174 nodeRef = id.substring((me.id + '-' + action + '-').length); 175 nodeRef = me.toNodeRef(nodeRef); 176 me[action].call(me, nodeRef); 177 args[1].stop = true; 178 } 179 } 180 return true; 181 }; 182 YAHOO.Bubbling.addDefaultAction("reply-action-link", fnActionHandlerDiv); 183 184 // Hook the show/hide link 185 var fnShowHideChildrenHandler = function TopicReplies_fnShowHideChildrenHandler(layer, args) 186 { 187 var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "a"); 188 if (owner !== null) 189 { 190 var action = ""; 191 action = owner.className; 192 if (typeof me[action] == "function") 193 { 194 var id = ''; 195 id = owner.id; 196 var nodeRef = ''; 197 nodeRef = id.substring((me.id + '-' + action + '-').length); 198 nodeRef = me.toNodeRef(nodeRef); 199 me[action].call(me, nodeRef); 200 args[1].stop = true; 201 } 202 } 203 return true; 204 }; 205 YAHOO.Bubbling.addDefaultAction("showHideChildren", fnShowHideChildrenHandler); 206 207 // initialize the mouse over listener 208 Alfresco.util.rollover.registerHandlerFunctions(this.id, this.onReplyElementMouseEntered, this.onReplyElementMouseExited, this); 209 }, 210 211 212 // Bubble event management 213 214 /** 215 * Tag selected handler (document details) 216 * 217 * @method onTagSelected 218 * @param tagId {string} Tag name. 219 * @param target {HTMLElement} Target element clicked. 220 */ 221 onAddReplyToPost: function TopicReplies_addReplyToPost(layer, args) 222 { 223 var obj = args[1]; 224 if (obj && (obj.postRef !== null)) 225 { 226 this.onAddReply(obj.postRef); 227 } 228 }, 229 230 /** 231 * onLoadReplies handler 232 */ 233 onTopicDataChanged: function TopicReplies_onTopicDataChanged(layer, args) 234 { 235 var oldRef = this.options.topicRef; 236 var obj = args[1]; 237 if (obj && (obj.topicRef !== null) && (obj.topicId !== null) && (obj.topicTitle !== null)) 238 { 239 this.options.topicRef = obj.topicRef; 240 this.options.topicId = obj.topicId; 241 this.options.topicTitle = obj.topicTitle; 242 243 // load the data if not done so or if the topic has changed 244 if (this.repliesData === null || (oldRef != this.repliesData.topicRef)) 245 { 246 this._loadRepliesData(); 247 } 248 } 249 }, 250 251 /** 252 * Loads the replies data and updates the ui. 253 */ 254 _loadRepliesData: function TopicReplies__loadRepliesData() 255 { 256 // ajax request success handler 257 var loadRepliesDataSuccess = function TopicReplies_loadRepliesDataSuccess(response) 258 { 259 // set the loaded data 260 var data = response.json.items; 261 this.repliesData = data; 262 263 // render the ui 264 this.renderUI(); 265 }; 266 267 // construct the url to call 268 var url = YAHOO.lang.substitute(Alfresco.constants.URL_SERVICECONTEXT + "components/forum/post/site/{site}/{container}/{topicId}/replies?levels={levels}", 269 { 270 site : this.options.siteId, 271 container: this.options.containerId, 272 topicId: this.options.topicId, 273 levels: 999 274 }); 275 276 // execute ajax request 277 Alfresco.util.Ajax.request( 278 { 279 url: url, 280 successCallback: 281 { 282 fn: loadRepliesDataSuccess, 283 scope: this 284 }, 285 failureMessage: this._msg("message.loadreplies.failure") 286 }); 287 }, 288 289 /** 290 * Converts a html-id safe nodeRef to a real one. 291 * 292 * @param safeRef {string} a nodeReference where the separators have been replaced by _ 293 * @return {string} a valid node reference 294 */ 295 toNodeRef: function(safeRef) 296 { 297 return safeRef.replace(/_/, '://').replace(/_/, '/'); 298 }, 299 300 /** 301 * Converts a node reference to a html-id safe string. 302 * 303 * @param nodeRef {string} a nodeReference where the separators have been replaced by _ 304 * @return {string} a nodeRef string usable in html id values 305 */ 306 toSafeRef: function(nodeRef) 307 { 308 return nodeRef.replace(':/', '').replace('/', '_').replace('/', '_'); 309 }, 310 311 /** 312 * Renders the UI of the component 313 */ 314 renderUI: function TopicReplies_renderUI() 315 { 316 // get the root element 317 var rootDiv = Dom.get(this.id + '-replies-root'); 318 rootDiv.innerHTML = ''; 319 var elem = new Element(rootDiv); 320 321 // add the reply form element 322 var replyFormDiv = document.createElement("div"); 323 replyFormDiv.setAttribute("id", "reply-add-form-" + this.toSafeRef(this.options.topicRef)); 324 elem.appendChild(replyFormDiv); 325 326 for (var x=0; x < this.repliesData.length; x++) 327 { 328 this.renderReply(rootDiv, this.repliesData[x], false); 329 } 330 331 // attach the rollover listeners 332 Alfresco.util.rollover.registerListenersByClassName(this.id, 'reply', 'div'); 333 334 // finally show the root div 335 Dom.removeClass(rootDiv, "hidden"); 336 }, 337 338 /** 339 * Renders an individual reply element 340 */ 341 renderReply: function TopicReplies_renderReply(parentDiv, data, highlight) 342 { 343 var replyDiv = document.createElement("div"); 344 345 // we first generate the general html for an element, this is the 346 // edit and view divs, the child replies div including the add reply div. 347 var safeRef = this.toSafeRef(data.nodeRef); 348 var html = ''; 349 html += '<div class="reply" id="reply-' + safeRef + '">'; 350 html += '</div>'; 351 html += '<div id="reply-edit-form-' + safeRef + '" class="hidden"></div>'; 352 html += '<div id="reply-add-form-' + safeRef + '" class="indented hidden"></div>'; 353 html += '<div class="indented" id="replies-of-' + safeRef + '"></div>'; 354 replyDiv.innerHTML = html; 355 parentDiv.appendChild(replyDiv); 356 357 // render the reply content 358 var viewElem = Dom.get('reply-' + safeRef); 359 this.renderReplyView(viewElem, data); 360 361 // render the children if they got already loaded 362 if (data.children !== undefined) 363 { 364 var repliesElem = Dom.get('replies-of-' + safeRef); 365 for (var x=0; x < data.children.length; x++) 366 { 367 this.renderReply(repliesElem, data.children[x], false); 368 } 369 } 370 371 if (highlight) 372 { 373 Alfresco.util.Anim.pulse(viewElem); 374 this._scrollToElement(viewElem); 375 } 376 }, 377 378 /** 379 * Renders the view part of a reply element 380 */ 381 renderReplyView: function TopicReplies_renderReplyView(div, data) 382 { 383 var safeRef = this.toSafeRef(data.nodeRef); 384 var html = ''; 385 386 // render the actions 387 html += '<div class="nodeEdit">'; 388 if (data.permissions.reply) 389 { 390 html += '<div class="onAddReply" id="' + this.id + '-onAddReply-' + safeRef + '">'; 391 html += '<a href="#" class="reply-action-link">' + this._msg("action.reply") + '</a>'; 392 html += '</div>'; 393 } 394 395 if (data.permissions.edit) 396 { 397 html += '<div class="onEditReply" id="' + this.id + '-onEditReply-' + safeRef + '">'; 398 html += '<a href="#" class="reply-action-link">' + this._msg("action.edit") + '</a>'; 399 html += '</div>'; 400 } 401 html += '</div>'; 402 403 // avatar 404 html += '<div class="authorPicture">' + Alfresco.util.people.generateUserAvatarImg(data.author) + '</div>'; 405 406 // content 407 html += '<div class="nodeContent">'; 408 html += '<div class="userLink">' + Alfresco.util.people.generateUserLink(data.author) + ' ' + this._msg("post.said") + ': '; 409 if (data.isUpdated) 410 { 411 html += '<span class="theme-color-2 nodeStatus">(' + this._msg("post.updated") + ')</span>'; 412 } 413 html += '</div>'; 414 415 html += '<div class="content yuieditor">' + data.content + '</div>'; 416 html += '</div>'; 417 418 // footer part 419 html += '<div class="nodeFooter">'; 420 html += '<span class="nodeAttrLabel replyTo">' + this._msg("replies") + ': </span>'; 421 html += '<span class="nodeAttrValue">(' + (data.children !== undefined ? data.children.length : 0) + ') </span>'; 422 if (data.replyCount > 0) 423 { 424 html += '<span class="nodeAttrValue">'; 425 html += '<a href="#" class="showHideChildren" id="' + this.id + '-showHideChildren-' + safeRef + '">' + this._msg("replies.hide") + '</a>'; 426 html += '</span>'; 427 } 428 html += '<span class="separator"> </span>'; 429 html += '<span class="nodeAttrLabel">' + this._msg("post.postedOn") + ': ' + '</span>'; 430 html += '<span class="nodeAttrValue">' + Alfresco.util.formatDate(data.createdOn) + '</span>'; 431 html += '</div>'; 432 433 div.innerHTML = html; 434 }, 435 436 /** 437 * Re-renders the view UI for a reply element 438 */ 439 rerenderReplyUI: function TopicReplies_rerenderReplyUI(nodeRef, highlight) 440 { 441 // Get the view element and the data and update the html 442 var viewElem = Dom.get('reply-' + this.toSafeRef(nodeRef)); 443 var data = this.findReplyDataObject(nodeRef); 444 this.renderReplyView(viewElem, data); 445 446 if (highlight) 447 { 448 Alfresco.util.Anim.pulse(viewElem); 449 } 450 }, 451 452 453 // Actions handlers 454 455 /** 456 * Handler for the add reply action links. 457 */ 458 onAddReply: function TopicReplies_onAddReply(nodeRef) 459 { 460 this._loadEditForm(nodeRef, false); 461 }, 462 463 /** 464 * Handler for the edit reply action links. 465 */ 466 onEditReply: function TopicReplies_onEditReply(nodeRef) 467 { 468 this._loadEditForm(nodeRef, true); 469 }, 470 471 /** 472 * Handler for the show/hide replies toggle links 473 */ 474 showHideChildren: function TopicReplies_showideChildren(nodeRef) 475 { 476 // get the replies element 477 var repliesElem = Dom.get('replies-of-' + this.toSafeRef(nodeRef)); 478 if (Dom.hasClass(repliesElem, "hidden")) 479 { 480 this._showChildren(nodeRef); 481 } 482 else 483 { 484 this._hideChildren(nodeRef); 485 } 486 }, 487 488 /** 489 * Loads the reply add or edit form. 490 * 491 * @param nodeRef {string} the parent nodeRef to which a child should be added or the reply that should be edited 492 * @param isEdit {boolean} if true nodeRef is edited, otherwise nodeRef is the parent of the new reply to be created 493 */ 494 _loadEditForm: function TopicReplies__loadEditForm(nodeRef, isEdit) 495 { 496 // construct the id to use for the form elements 497 var formId = this.id + this.toSafeRef(nodeRef) + (isEdit ? "-edit" : "-add"); 498 499 // execute ajax request to load the form 500 Alfresco.util.Ajax.request( 501 { 502 url: Alfresco.constants.URL_SERVICECONTEXT + "modules/discussions/replies/reply-form", 503 dataObj: 504 { 505 htmlid : formId 506 }, 507 successCallback: 508 { 509 fn: this._onEditFormLoaded, 510 scope: this, 511 obj: 512 { 513 isEdit: isEdit, 514 nodeRef: nodeRef, 515 formId: formId 516 } 517 }, 518 failureMessage: this._msg("message.loadeditform.failure") 519 }); 520 }, 521 522 /** 523 * Request success handler for the loadReplyEditForm ajax request 524 */ 525 _onEditFormLoaded: function TopicReplies__onEditFormLoaded(response, obj) 526 { 527 // make sure no other forms are displayed 528 this._hideOpenForms(); 529 530 // insert the form at the right location 531 var safeRef = this.toSafeRef(obj.nodeRef); 532 var formDiv = null; 533 if (obj.isEdit) 534 { 535 formDiv = Dom.get('reply-edit-form-' + safeRef); 536 } 537 else 538 { 539 formDiv = Dom.get('reply-add-form-' + safeRef); 540 } 541 formDiv.innerHTML = response.serverResponse.responseText; 542 543 // find the data object for nodeRef. 544 // Note: this will be null in case of a reply to the topic itself. 545 var data = this.findReplyDataObject(obj.nodeRef); 546 547 // insert current values into the form 548 var actionUrl = ''; 549 var formTitle = ''; 550 var content = ''; 551 var submitButtonLabel = ''; 552 var viewDiv = null; 553 if (obj.isEdit) 554 { 555 viewDiv = Dom.get('reply-' + safeRef); 556 actionUrl = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/forum/post/node/{nodeRef}", 557 { 558 nodeRef: obj.nodeRef.replace(':/', '') 559 }); 560 formTitle = this._msg('form.updateTitle'); 561 submitButtonLabel = this._msg('action.update'); 562 content = data.content; 563 } 564 else 565 { 566 actionUrl = YAHOO.lang.substitute(Alfresco.constants.PROXY_URI + "api/forum/post/node/{nodeRef}/replies", 567 { 568 nodeRef: obj.nodeRef.replace(':/', '') 569 }); 570 571 // for root replies we don't have a parent data object and therefore can 572 // tell whom to reply to 573 if (data !== null) 574 { 575 formTitle = this._msg('form.replyToTitle', Alfresco.util.people.generateUserLink(data.author)); 576 } 577 else 578 { 579 formTitle = this._msg('form.replyTitle'); 580 } 581 submitButtonLabel = this._msg('action.create'); 582 } 583 584 // set the values in the dom 585 var formId = obj.formId; 586 Dom.get(formId + "-form-title").innerHTML = formTitle; 587 Dom.get(formId + "-form").setAttribute("action", actionUrl); 588 Dom.get(formId + "-site").setAttribute("value", this.options.siteId); 589 Dom.get(formId + "-container").setAttribute("value", this.options.containerId); 590 Dom.get(formId + "-submit").setAttribute("value", submitButtonLabel); 591 Dom.get(formId + "-content").value = content; 592 593 // store edit related data. 594 this.editData = 595 { 596 nodeRef: obj.nodeRef, 597 isEdit: obj.isEdit, 598 formId: formId, 599 viewDiv: viewDiv, 600 formDiv: formDiv 601 }; 602 603 // register the form logic 604 this._registerEditForm(obj.nodeRef, formId, obj.isEdit); 605 }, 606 607 /** 608 * Registers the form logic 609 */ 610 _registerEditForm: function TopicReplies__registerEditForm(nodeRef, formId, isEdit) 611 { 612 // register the okButton 613 this.widgets.okButton = new YAHOO.widget.Button(formId + "-submit", 614 { 615 type: "submit" 616 }); 617 618 // register the cancel button 619 this.widgets.cancelButton = new YAHOO.widget.Button(formId + "-cancel", 620 { 621 type: "button" 622 }); 623 this.widgets.cancelButton.subscribe("click", this.onFormCancelButtonClick, this, true); 624 625 // Instantiate and render the simple editor we use for the form 626 this.widgets.editor = new Alfresco.util.RichEditor(Alfresco.constants.HTML_EDITOR, formId + '-content',this.options.editorConfig); 627 this.widgets.editor.addPageUnloadBehaviour(this._msg("message.unsavedChanges.reply")); 628 this.widgets.editor.render(); 629 630 // Add validation to the rich text editor 631 var keyUpIdentifier = (Alfresco.constants.HTML_EDITOR === 'YAHOO.widget.SimpleEditor') ? 'editorKeyUp' : 'onKeyUp'; 632 this.widgets.editor.subscribe(keyUpIdentifier, function (e) 633 { 634 /** 635 * Doing a form validation on every key stroke is process consuming, below we try to make sure we only do 636 * a form validation if it's necessarry. 637 * NOTE: Don't check for zero-length in commentsLength, due to HTML <br>, <span> tags, etc. possibly 638 * being present. Only a "Select all" followed by delete will clean all tags, otherwise leftovers will 639 * be there even if the form looks empty. 640 */ 641 if (this.widgets.editor.getContent().length < 20 || this.widgets.okButton.get("disabled")) 642 { 643 // Submit was disabled and something has been typed, validate and submit will be enabled 644 this.widgets.editor.save(); 645 this.widgets.form.updateSubmitElements(); 646 } 647 }, this, true); 648 649 // create the form that does the validation/submit 650 this.widgets.form = new Alfresco.forms.Form(formId + "-form"); 651 var replyForm = this.widgets.form; 652 replyForm.setShowSubmitStateDynamically(true, false); 653 replyForm.addValidation(formId + "-content", Alfresco.forms.validation.mandatory, null); 654 replyForm.setSubmitElements(this.widgets.okButton); 655 if (isEdit) 656 { 657 replyForm.setAjaxSubmitMethod(Alfresco.util.Ajax.PUT); 658 } 659 replyForm.setAJAXSubmit(true, 660 { 661 successMessage: this._msg("message.savereply.success"), 662 successCallback: 663 { 664 fn: this.onFormSubmitSuccess, 665 scope: this, 666 obj: 667 { 668 nodeRef: nodeRef, 669 isEdit: isEdit 670 } 671 }, 672 failureMessage: this._msg("message.savereply.failure"), 673 failureCallback: 674 { 675 fn: this.onFormSubmitFailure, 676 scope: this 677 } 678 }); 679 replyForm.setSubmitAsJSON(true); 680 replyForm.doBeforeFormSubmit = 681 { 682 fn: function(form, obj) 683 { 684 // disable buttons 685 this.widgets.okButton.set("disabled", true); 686 this.widgets.cancelButton.set("disabled", true); 687 688 //Put the HTML back into the text area 689 this.widgets.editor.save(); 690 691 // show a wait message 692 this.widgets.feedbackMessage = Alfresco.util.PopupManager.displayMessage( 693 { 694 text: Alfresco.util.message(this._msg("message.submitting")), 695 spanClass: "wait", 696 displayTime: 0 697 }); 698 }, 699 scope: this 700 }; 701 replyForm.init(); 702 703 // now show the form 704 this._showForm(); 705 706 // finally scroll to the form 707 this._scrollToElement(this.editData.formDiv); 708 }, 709 710 /** 711 * Form submit success handler 712 */ 713 onFormSubmitSuccess: function TopicReplies_onFormSubmitSuccess(response, obj) 714 { 715 // remove wait message 716 this.widgets.feedbackMessage.destroy(); 717 718 var data, parentElem; 719 720 // in case of an edit reply, simply update the data/ui 721 if (obj.isEdit) 722 { 723 // update the data object for the reply 724 data = this.findReplyDataObject(obj.nodeRef); 725 YAHOO.lang.augmentObject(data, response.json.item, true); 726 727 // rerender the ui 728 this.rerenderReplyUI(data.nodeRef, true); 729 } 730 // in case of a create, add the new data and insert a new reply element 731 else 732 { 733 // the logic here is slightly different for top level replies 734 if (this.options.topicRef == obj.nodeRef) 735 { 736 // add the data object 737 this.repliesData.push(response.json.item); 738 739 // render the new reply 740 parentElem = Dom.get(this.id + '-replies-root'); 741 this.renderReply(parentElem, response.json.item, true); 742 } 743 else 744 { 745 // add the data object 746 data = this.findReplyDataObject(obj.nodeRef); 747 // make sure the children array exists 748 if (data.children === undefined) 749 { 750 data.children = []; 751 } 752 data.children.push(response.json.item); 753 754 // render the new reply 755 parentElem = Dom.get('replies-of-' + this.toSafeRef(obj.nodeRef)); 756 this.renderReply(parentElem, response.json.item, true); 757 758 // rerender the parent reply, which will update the reply count 759 this.rerenderReplyUI(obj.nodeRef, false); 760 } 761 762 // make sure the rolover listener gets attached to the new element 763 Alfresco.util.rollover.registerListenersByClassName(this.id, 'reply', 'div'); 764 } 765 766 // finally hide the form / show the updated view in case of an edit 767 this._hideOpenForms(); 768 }, 769 770 /** 771 * Form submit failure handler 772 */ 773 onFormSubmitFailure: function TopicReplies_onFormSubmitFailure(response, obj) 774 { 775 // enable buttons 776 this.widgets.okButton.set("disabled", false); 777 this.widgets.cancelButton.set("disabled", false); 778 779 // hide message 780 this.widgets.feedbackMessage.destroy(); 781 }, 782 783 /** 784 * Edit form cancel button click handler 785 */ 786 onFormCancelButtonClick: function TopicReplies_onFormCancelButtonClick(type, args) 787 { 788 this._hideOpenForms(); 789 }, 790 791 /** 792 * Find the data object for a reply given its node reference. 793 * 794 * @param nodeRef {string} the nodeRef of the reply to find the data for 795 * @return {string} a reply data object or null if not found 796 */ 797 findReplyDataObject: function TopicReplies_findReplyDataObject(nodeRef) 798 { 799 return this._findReplyDataObjectImpl(this.repliesData, nodeRef); 800 }, 801 802 /** 803 * Implementation of findReplyDataObject 804 */ 805 _findReplyDataObjectImpl: function TopicReplies__findReplyDataObjectImpl(arr, nodeRef) 806 { 807 for (var x=0; x < arr.length; x++) 808 { 809 // check the element 810 if (arr[x].nodeRef == nodeRef) 811 { 812 return arr[x]; 813 } 814 // check the children recursively 815 else if (arr[x].children !== undefined) 816 { 817 var result = this._findReplyDataObjectImpl(arr[x].children, nodeRef); 818 if (result !== null) 819 { 820 return result; 821 } 822 } 823 } 824 return null; 825 }, 826 827 /** 828 * Shows the children of a reply 829 * 830 * @param nodeRef the nodeRef of the reply for which children should be shown 831 */ 832 _showChildren: function TopicReplies__showChildren(nodeRef) 833 { 834 // show the replies element 835 var repliesElem = Dom.get('replies-of-' + this.toSafeRef(nodeRef)); 836 Dom.removeClass(repliesElem, "hidden"); 837 838 // the show/hide replies toggle link might not exist if there are no replies 839 var linkElem = Dom.get(this.id + '-showHideChildren-' + this.toSafeRef(nodeRef)); 840 if (linkElem !== null) 841 { 842 linkElem.innerHTML = this._msg("replies.hide"); 843 } 844 }, 845 846 /** 847 * Hides the children of a reply 848 * 849 * @param nodeRef the nodeRef of the reply for which children should be hidden 850 */ 851 _hideChildren: function TopicReplies__hideChildren(nodeRef) 852 { 853 var repliesElem = Dom.get('replies-of-' + this.toSafeRef(nodeRef)); 854 Dom.addClass(repliesElem, "hidden"); 855 var linkElem = Dom.get(this.id + '-showHideChildren-' + this.toSafeRef(nodeRef)); 856 linkElem.innerHTML = this._msg("replies.show"); 857 }, 858 859 /** 860 * Hides any open form, displays any hidden view element 861 */ 862 _hideOpenForms: function TopicReplies__hideOpenForms() 863 { 864 if (this.editData.formDiv !== null) 865 { 866 Dom.addClass(this.editData.formDiv, "hidden"); 867 this.editData.formDiv.innerHTML = ''; 868 this.editData.formDiv = null; 869 } 870 if (this.editData.viewDiv !== null) 871 { 872 Dom.removeClass(this.editData.viewDiv, "hidden"); 873 this.editData.viewDiv = null; 874 } 875 }, 876 877 /** 878 * Shows the already prepared form and hides the associated view element if any. 879 */ 880 _showForm: function TopicReplies__showForm() 881 { 882 // hide the view element if any 883 if (this.editData.viewDiv !== null) 884 { 885 Dom.addClass(this.editData.viewDiv, "hidden"); 886 } 887 888 // show the form element 889 Dom.removeClass(this.editData.formDiv, "hidden"); 890 }, 891 892 /** 893 * Vertically scrolls the browser window to the passed element 894 */ 895 _scrollToElement: function TopicReplies__scrollToElement(el) 896 { 897 var yPos = Dom.getY(el); 898 if (YAHOO.env.ua.ie > 0) 899 { 900 yPos = yPos - (document.body.clientHeight / 3); 901 } 902 else 903 { 904 yPos = yPos - (window.innerHeight / 3); 905 } 906 window.scrollTo(0, yPos); 907 }, 908 909 // mouse hover functionality 910 911 /** Called when the mouse enters into a list item. */ 912 onReplyElementMouseEntered: function TopicReplies_onReplyElementMouseEntered(layer, args) 913 { 914 // only highlight if there are actions on the specific element 915 var nodeRef = args[1].target.id.substring(('reply-').length); 916 nodeRef = this.toNodeRef(nodeRef); 917 918 var data = this.findReplyDataObject(nodeRef), permissions = data.permissions; 919 if (!(permissions.edit || permissions.reply || permissions['delete'])) 920 { 921 return; 922 } 923 924 Dom.addClass(args[1].target, 'over'); 925 }, 926 927 /** Called whenever the mouse exits a list item. */ 928 onReplyElementMouseExited: function TopicReplies_onReplyElementMouseExited(layer, args) 929 { 930 Dom.removeClass(args[1].target, 'over'); 931 }, 932 933 /** 934 * Gets a custom message 935 * 936 * @method _msg 937 * @param messageId {string} The messageId to retrieve 938 * @return {string} The custom message 939 * @private 940 */ 941 _msg: function TopicReplies__msg(messageId) 942 { 943 return Alfresco.util.message.call(this, messageId, "Alfresco.TopicReplies", Array.prototype.slice.call(arguments).slice(1)); 944 } 945 946 }; 947 })(); 948