1/** 2 * Wise Chat core controller. 3 * 4 * @author Kainex <[email protected]> 5 */ 6function WiseChatController(options) { 7 var progressBar = new WiseChatProgressBar(options); 8 var notifier = new WiseChatNotifier(options); 9 var messagesHistory = new WiseChatMessagesHistory(); 10 var imageViewer = new WiseChatImageViewer(); 11 var dateFormatter = new WiseChatDateFormatter(); 12 var messageAttachments = new WiseChatMessageAttachments(options, imageViewer, progressBar); 13 var dateAndTimeRenderer = new WiseChatDateAndTimeRenderer(options, dateFormatter); 14 var messages = new WiseChatMessages(options, messagesHistory, messageAttachments, dateAndTimeRenderer, notifier, progressBar); 15 var settings = new WiseChatSettings(options, messages); 16 var maintenanceExecutor = new WiseChatMaintenanceExecutor(options, messages, notifier); 17 var emoticonsPanel = new WiseChatEmoticonsPanel(options, messages); 18 19 messages.start(); 20 maintenanceExecutor.start(); 21}; 22 23/** 24 * WiseChatDateFormatter class. Formats dates given in UTC timezone. 25 * 26 * @author Kainex <[email protected]> 27 */ 28function WiseChatDateFormatter() { 29 30 function makeLeadZero(number) { 31 return (number < 10 ? '0' : '') + number; 32 } 33 34 /** 35 * Parses date given in ISO format. 36 * 37 * @param {String} isoDate Date in ISO format 38 * 39 * @return {Date} Parsed date 40 */ 41 function parseISODate(isoDate) { 42 var s = isoDate.split(/\D/); 43 44 return new Date(Date.UTC(s[0], --s[1]||'', s[2]||'', s[3]||'', s[4]||'', s[5]||'', s[6]||'')) 45 } 46 47 /** 48 * Determines whether two dates have equal day, month and year. 49 * 50 * @param {Date} firstDate 51 * @param {Date} secondDate 52 * 53 * @return {Boolean} 54 */ 55 function isSameDate(firstDate, secondDate) { 56 var dateFormatStr = 'Y-m-d'; 57 58 return formatDate(firstDate, dateFormatStr) == formatDate(secondDate, dateFormatStr); 59 } 60 61 /** 62 * Returns formatted date. 63 * 64 * @param {Date} date Date to format as a string 65 * @param {String} format Desired date format 66 * 67 * @return {String} Formatted date 68 */ 69 function formatDate(date, format) { 70 format = format.replace(/Y/, date.getFullYear()); 71 format = format.replace(/m/, makeLeadZero(date.getMonth() + 1)); 72 format = format.replace(/d/, makeLeadZero(date.getDate())); 73 format = format.replace(/H/, makeLeadZero(date.getHours())); 74 format = format.replace(/i/, makeLeadZero(date.getMinutes())); 75 76 return format; 77 } 78 79 /** 80 * Returns localized time without seconds. 81 * 82 * @param {Date} date Date to format as a string 83 * 84 * @return {String} Localized time 85 */ 86 function getLocalizedTime(date) { 87 if (typeof (date.toLocaleTimeString) != "undefined") { 88 var timeLocale = date.toLocaleTimeString(); 89 if ((timeLocale.match(/:/g) || []).length == 2) { 90 timeLocale = timeLocale.replace(/:\d\d$/, ''); 91 timeLocale = timeLocale.replace(/:\d\d /, ' '); 92 timeLocale = timeLocale.replace(/[A-Z]{2,4}\-\d{1,2}/, ''); 93 timeLocale = timeLocale.replace(/[A-Z]{2,4}/, ''); 94 } 95 96 return timeLocale; 97 } else { 98 return formatDate(date, 'H:i'); 99 } 100 } 101 102 /** 103 * Returns localized date. 104 * 105 * @param {Date} date Date to format as a string 106 * 107 * @return {String} Localized date 108 */ 109 function getLocalizedDate(date) { 110 if (typeof (date.toLocaleDateString) != "undefined") { 111 return date.toLocaleDateString(); 112 } else { 113 return formatDate(date, 'Y-m-d'); 114 } 115 } 116 117 // public API: 118 this.formatDate = formatDate; 119 this.parseISODate = parseISODate; 120 this.isSameDate = isSameDate; 121 this.getLocalizedTime = getLocalizedTime; 122 this.getLocalizedDate = getLocalizedDate; 123}; 124 125/** 126 * WiseChatMessageAttachments class. Attachments management. 127 * 128 * @author Kainex <[email protected]> 129 */ 130function WiseChatMessageAttachments(options, imageViewer, progressBar) { 131 var IMAGE_TYPES = ['jpg', 'jpeg', 'png', 'gif']; 132 var prepareImageEndpoint = options.apiEndpointBase + '?action=wise_chat_prepare_image_endpoint'; 133 var container = jQuery('#' + options.chatId); 134 var messageAttachmentsPanel = container.find('.wcMessageAttachments'); 135 var imageUploadPreviewImage = container.find('.wcImageUploadPreview'); 136 var imageUploadFile = container.find('.wcImageUploadFile'); 137 var attachmentClearButton = container.find('.wcAttachmentClear'); 138 var fileUploadFile = container.find('.wcFileUploadFile'); 139 var fileUploadNamePreview = container.find('.wcFileUploadNamePreview'); 140 var attachments = []; 141 142 function showErrorMessage(message) { 143 alert(message); 144 } 145 146 function addAttachment(type, data, name) { 147 attachments.push({ type: type, data: data, name: name }); 148 } 149 150 function showImageAttachment() { 151 if (attachments.length > 0 && attachments[0].type === 'image') { 152 imageViewer.show(attachments[0].data); 153 } 154 } 155 156 function onImageUploadFileChange() { 157 var fileInput = imageUploadFile[0]; 158 if (typeof FileReader === 'undefined' || fileInput.files.length === 0) { 159 showErrorMessage('Unsupported operation'); 160 return; 161 } 162 163 var fileDetails = fileInput.files[0]; 164 if (fileDetails.size && fileDetails.size > options.imagesSizeLimit) { 165 showErrorMessage(options.messages.messageSizeLimitError); 166 return; 167 } 168 169 if (IMAGE_TYPES.indexOf(getExtension(fileDetails)) > -1) { 170 var fileReader = new FileReader(); 171 fileReader.onload = function(event) { 172 clearAttachments(); 173 prepareImage(event.target.result, function(preparedImageData) { 174 if (typeof preparedImageData !== 'undefined' && preparedImageData.length > 0) { 175 addAttachment('image', preparedImageData); 176 177 imageUploadPreviewImage.show(); 178 imageUploadPreviewImage.attr('src', preparedImageData); 179 messageAttachmentsPanel.show(); 180 imageUploadFile.val(''); 181 } else {
182 showErrorMessage('Cannot prepare image due to server error'); 183 } 184 }); 185 }; 186 fileReader.readAsDataURL(fileDetails); 187 } else { 188 showErrorMessage(options.messages.messageUnsupportedTypeOfFile); 189 } 190 } 191 192 function prepareImage(imageSource, successCallback) { 193 var that = this; 194 195 progressBar.setValue(0); 196 progressBar.show(); 197 198 jQuery.ajax({ 199 type: "POST", 200 url: prepareImageEndpoint, 201 data: { 202 data: imageSource, 203 checksum: options.checksum 204 }, 205 progressUpload: function(event) { 206 if (event.lengthComputable) { 207 var percent = parseInt(event.loaded / event.total * 100); 208 if (percent > 100) { 209 percent = 100; 210 } 211 progressBar.setValue(percent); 212 } 213 } 214 }) 215 .done(function(result) { 216 progressBar.hide(); 217 successCallback.apply(that, [result]); 218 }) 219 .fail(function(jqXHR, textStatus, errorThrown) { 220 progressBar.hide(); 221 try { 222 var response = jQuery.parseJSON(jqXHR.responseText); 223 if (typeof response.error != 'undefined') { 224 showErrorMessage(response.error); 225 } else { 226 showErrorMessage('Image preparation error occurred'); 227 } 228 } 229 catch (e) { 230 showErrorMessage('Unknown image preparation error occurred'); 231 } 232 }); 233 } 234 235 function onFileUploadFileChange() { 236 var fileInput = fileUploadFile[0]; 237 if (typeof FileReader === 'undefined' || fileInput.files.length === 0) { 238 showErrorMessage('Unsupported operation'); 239 return; 240 } 241 242 var fileDetails = fileInput.files[0]; 243 if (options.attachmentsValidFileFormats.indexOf(getExtension(fileDetails)) > -1) { 244 var fileReader = new FileReader(); 245 var fileName = fileDetails.name; 246 247 if (fileDetails.size > options.attachmentsSizeLimit) { 248 showErrorMessage(options.messages.messageSizeLimitError); 249 } else { 250 fileReader.onload = function(event) { 251 clearAttachments(); 252 addAttachment('file', event.target.result, fileName); 253 fileUploadNamePreview.html(fileName); 254 fileUploadNamePreview.show(); 255 messageAttachmentsPanel.show(); 256 }; 257 fileReader.readAsDataURL(fileDetails); 258 } 259 } else { 260 showErrorMessage(options.messages.messageUnsupportedTypeOfFile); 261 } 262 } 263 264 function getExtension(fileDetails) { 265 if (typeof fileDetails.name !== 'undefined') { 266 var splitted = fileDetails.name.split('.'); 267 if (splitted.length > 1) { 268 return splitted.pop().toLowerCase(); 269 } 270 } 271 272 return null; 273 } 274 275 function resetInput(inputField) { 276 inputField.wrap('<form>').parent('form').trigger('reset'); 277 inputField.unwrap(); 278 } 279 280 /** 281 * Returns an array of prepared attachments. 282 * 283 * @return {Array} 284 */ 285 function getAttachments() { 286 return attachments; 287 } 288 289 /** 290 * Clears all added attachments, resets and hides UI related to added attachments. 291 */ 292 function clearAttachments(e) { 293 if (typeof e !== 'undefined') { 294 e.preventDefault(); 295 } 296 297 attachments = []; 298 messageAttachmentsPanel.hide(); 299 fileUploadNamePreview.hide(); 300 fileUploadNamePreview.html(''); 301 imageUploadPreviewImage.hide(); 302 resetInput(fileUploadFile); 303 resetInput(imageUploadFile); 304 } 305 306 // DOM events: 307 imageUploadFile.change(onImageUploadFileChange); 308 fileUploadFile.change(onFileUploadFileChange); 309 attachmentClearButton.click(clearAttachments); 310 imageUploadPreviewImage.click(showImageAttachment); 311 312 // public API: 313 this.getAttachments = getAttachments; 314 this.clearAttachments = clearAttachments; 315}; 316 317/** 318 * WiseChatImageViewer 319 * 320 * @author Kainex <[email protected]> 321 */ 322function WiseChatImageViewer() { 323 var HOURGLASS_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wQEDB4ktAYXpwAAAb5JREFUSMe1lr9qFFEUh78rg8gWW1ikSLEWgkVq2SoYsbBIk1dYEAsxaJt3sLAIFkEEX0FSRlgMhKAPkEIQwZDChATSBLMQP5uz4bKZmZ3ZxR+cYs75nT9z7rlnJpFBfQC8B24xG/4Cz1NK38eKYoKwADxiPiwA1wnSpFUdAO+A+y0D/wBeppQ+5sqihHgAdIBRSumsSWT1bvgcNCF31Et1tWnp6mr4dCZtNw4zpXQB7AJrLdqzBuyGb6OKBuq52m3A7QZ3UGZPVW0CfgJvgc/As4r4H4CnwGvgXkrpDy36uh6VPVRPvYnTsJ2r662HWS3U/ZDH6kkW/CR0Y3sx041Re+qh+kXtq59C+qE7VHt1MWpXQkrpF7ACdIFhZhqGbiU4syX474gWHUU7FjP9YuiOprVo2iF/jUO8U3Hj94NTzJLgVYxgL0v4JqTI3rD9mEZ1v9WN7Hk7G9Pt8d5RN4LbaZPgelWE7JVctL3MXrkqqhLsqFvqbXVoNYbB2VJ32rTnMlbwptOxWbeuyxL0w/GJetUgwVVwVfuT8crGawm4AEbAi4ZdHYXPEvCtrvpl58dy3Rscx9dsnt+W41zxD60+eUN8VNiNAAAAAElFTkSuQmCC"; 324 325 var container = jQuery('body'); 326 var imagePreviewFade = container.find('.wcImagePreviewFade'); 327 var imagePreview = container.find('.wcImagePreview'); 328 if (imagePreviewFade.length === 0) { 329 container.append('<div class="wcImagePreview"> </div><div class="wcImagePreviewFade"> </div>'); 330 imagePreviewFade = container.find('.wcImagePreviewFade'); 331 imagePreview = container.find('.wcImagePreview'); 332 } 333 334 function show(imageSource) { 335 clearRemnants(); 336 337 imagePreviewFade.show(); 338 addAndShowHourGlass(); 339 340 var imageElement = jQuery('<img style="display:none;" />'); 341 imageElement.on('load', function() { 342 removeHourGlass(); 343 344 var image = jQuery(this); 345 var additionalMargin = 20; 346 var windowWidth = jQuery(window).width(); 347 var windowHeight = jQuery(window).height(); 348 image.show(); 349 350 if (image.width() > windowWidth && image.height() > windowHeight) { 351 if (image.width() > image.height()) { 352 image.width(windowWidth - additionalMargin); 353 } else { 354 image.height(windowHeight - additionalMargin); 355 } 356 } else if (image.width() > windowWidth) { 357 image.width(windowWidth - additionalMargin); 358 } else if (image.height() > windowHeight) { 359 image.height(windowHeight - additionalMargin); 360 } 361 362 var topPosition = Math.max(0, ((windowHeight - image.outerHeight()) / 2) + jQuery(window).scrollTop()); 363 var leftMargin = Math.max(0, (windowWidth - image.width()) / 2); 364 imagePreview.css({ 365 top: topPosition + "px", 366 marginLeft: leftMargin + "px" 367 }); 368 }); 369 imageElement.attr('src', imageSource); 370 imageElement.appendTo(imagePreview); 371 imageElement.click(hide); 372 } 373 374 function hide() { 375 clearRemnants(); 376 imagePreview.hide(); 377 imagePreviewFade.hide(); 378 } 379 380 function clearRemnants() { 381 imagePreview.find('img').remove(); 382 } 383 384 function addAndShowHourGlass() { 385 var windowHeight = jQuery(window).height(); 386 var imageElement = jQuery('<img class="wcHourGlass" />'); 387 var topPosition = Math.max(0, ((windowHeight - 24) / 2) + jQuery(window).scrollTop()); 388 389 imageElement.attr('src', HOURGLASS_ICON); 390 imageElement.appendTo(imagePreview); 391 imagePreview.css({ 392 top: topPosition + "px", 393 marginLeft: "-12px" 394 }); 395 imagePreview.show(); 396 } 397 398 function removeHourGlass() { 399 container.find('.wcHourGlass').remove(); 400 } 401 402 // DOM events: 403 imagePreviewFade.click(hide); 404 405 // public API: 406 this.show = show; 407 this.hide = hide; 408}; 409 410/** 411 * WiseChatNotifier - window title and sound notifiers. 412 * 413 * @author Kainex <[email protected]> 414 */ 415function WiseChatNotifier(options) { 416 var isWindowFocused = true; 417 var isTitleNotificationVisible = false;
418 var rawTitle = document.title; 419 var notificationNumber = 0; 420 var newMessageSoundNotification = null; 421 var userLeftSoundNotification = null; 422 var userJoinedSoundNotification = null; 423 var userMentioningSoundNotification = null; 424 425 function initializeSoundFeatures(soundFile, eventID) { 426 if (soundFile != null && soundFile.length > 0) { 427 var elementId = 'wcMessagesNotificationAudio' + eventID; 428 var soundNotificationElement = jQuery('#' + elementId); 429 if (soundNotificationElement.length > 0) { 430 return soundNotificationElement; 431 } 432 433 var soundFileURLWav = options.baseDir + 'sounds/' + soundFile + '.wav'; 434 var soundFileURLMp3 = options.baseDir + 'sounds/' + soundFile + '.mp3'; 435 var soundFileURLOgg = options.baseDir + 'sounds/' + soundFile + '.ogg'; 436 var container = jQuery('body'); 437 438 container.append( 439 '<audio id="' + elementId + '" preload="auto" >' + 440 '<source src="' + soundFileURLWav + '" type="audio/x-wav" />' + 441 '<source src="' + soundFileURLOgg + '" type="audio/ogg" />' + 442 '<source src="' + soundFileURLMp3 + '" type="audio/mpeg" />' + 443 '</audio>' 444 ); 445 446 return jQuery('#' + elementId); 447 } 448 449 return null; 450 } 451 452 function showTitleNotification() { 453 if (!isTitleNotificationVisible) { 454 isTitleNotificationVisible = true; 455 rawTitle = document.title; 456 } 457 notificationNumber++; 458 document.title = '(' + notificationNumber + ') (!) ' + rawTitle; 459 setTimeout(function() { showTitleNotificationAnimStep1(); }, 1500); 460 } 461 462 function showTitleNotificationAnimStep1() { 463 if (isTitleNotificationVisible) { 464 document.title = '(' + notificationNumber + ') ' + rawTitle; 465 } 466 } 467 468 function hideTitleNotification() { 469 if (isTitleNotificationVisible) { 470 document.title = rawTitle; 471 isTitleNotificationVisible = false; 472 notificationNumber = 0; 473 } 474 } 475 476 function onWindowBlur() { 477 isWindowFocused = false; 478 } 479 480 function onWindowFocus() { 481 isWindowFocused = true; 482 hideTitleNotification(); 483 } 484 485 function sendNotifications() { 486 if (options.enableTitleNotifications && !isWindowFocused) { 487 showTitleNotification(); 488 } 489 if (!options.userSettings.muteSounds) { 490 if (newMessageSoundNotification !== null && newMessageSoundNotification[0].play) { 491 newMessageSoundNotification[0].play(); 492 } 493 } 494 } 495 496 function sendNotificationForEvent(eventName) { 497 if (!options.userSettings.muteSounds) { 498 if (eventName == 'userLeft') { 499 if (userLeftSoundNotification !== null && userLeftSoundNotification[0].play) { 500 userLeftSoundNotification[0].play(); 501 } 502 } else if (eventName == 'userJoined') { 503 if (userJoinedSoundNotification !== null && userJoinedSoundNotification[0].play) { 504 userJoinedSoundNotification[0].play(); 505 } 506 } else if (eventName == 'userMentioning') { 507 if (userMentioningSoundNotification !== null && userMentioningSoundNotification[0].play) { 508 userMentioningSoundNotification[0].play(); 509 } 510 } 511 } 512 } 513 514 // start-up actions: 515 newMessageSoundNotification = initializeSoundFeatures(options.soundNotification, 'NewMessage'); 516 userLeftSoundNotification = initializeSoundFeatures(options.leaveSoundNotification, 'UserLeft'); 517 userJoinedSoundNotification = initializeSoundFeatures(options.joinSoundNotification, 'UserJoined'); 518 userMentioningSoundNotification = initializeSoundFeatures(options.mentioningSoundNotification, 'UserMentioned'); 519 520 // DOM events: 521 jQuery(window).blur(onWindowBlur); 522 jQuery(window).focus(onWindowFocus); 523 524 // public API: 525 this.sendNotifications = sendNotifications; 526 this.sendNotificationForEvent = sendNotificationForEvent; 527} 528 529/** 530 * WiseChatDateAndTimeRenderer - renders date and time next to each message according to the settings. 531 * 532 * @author Kainex <[email protected]> 533 */ 534function WiseChatDateAndTimeRenderer(options, dateFormatter) { 535 var spanDate = '<span class="wcMessageTimeDate">'; 536 var spanTime = '<span class="wcMessageTimeHour">'; 537 var spanClose = '</span>'; 538 var dateAndTimeMode = options.messagesTimeMode; 539 540 function formatFullDateAndTime(date, nowDate, element) { 541 var timeFormatted = typeof options.messagesTimeFormat !== 'undefined' && options.messagesTimeFormat.length > 0
542 ? wiseChatUtilsMomentJS.moment(date).format(options.messagesTimeFormat) 543 : dateFormatter.getLocalizedTime(date); 544 545 if (dateFormatter.isSameDate(nowDate, date)) { 546 element.html(spanTime + timeFormatted + spanClose); 547 } else { 548 var dateFormatted = typeof options.messagesDateFormat !== 'undefined' && options.messagesDateFormat.length > 0 549 ? wiseChatUtilsMomentJS.moment(date).format(options.messagesDateFormat) 550 : dateFormatter.getLocalizedDate(date); 551 element.html( 552 spanDate + dateFormatted + spanClose + ' ' + 553 spanTime + timeFormatted + spanClose 554 ); 555 } 556 element.attr('data-fixed', '1'); 557 } 558 559 function formatElapsedDateAndTime(date, nowDate, element) { 560 var yesterdayDate = new Date(); 561 var diffSeconds = parseInt((nowDate.getTime() - date.getTime()) / 1000); 562 yesterdayDate.setDate(nowDate.getDate() - 1); 563 564 var formattedDateAndTime = ''; 565 var isFixed = false; 566 var timeFormatted = typeof options.messagesTimeFormat !== 'undefined' && options.messagesTimeFormat.length > 0 567 ? wiseChatUtilsMomentJS.moment(date).format(options.messagesTimeFormat) 568 : dateFormatter.getLocalizedTime(date); 569 570 if (diffSeconds < 60) { 571 if (diffSeconds <= 0) { 572 diffSeconds = 1; 573 } 574 formattedDateAndTime = spanTime + diffSeconds + ' ' + options.messages.messageSecAgo + spanClose; 575 } else if (diffSeconds < 60 * 60) { 576 formattedDateAndTime = spanTime + parseInt(diffSeconds / 60) + ' ' + options.messages.messageMinAgo + spanClose; 577 } else if (dateFormatter.isSameDate(nowDate, date)) { 578 formattedDateAndTime = spanTime + timeFormatted + spanClose; 579 isFixed = true; 580 } else if (dateFormatter.isSameDate(yesterdayDate, date)) { 581 formattedDateAndTime = spanDate + options.messages.messageYesterday + spanClose + ' ' + spanTime + timeFormatted + spanClose; 582 isFixed = true; 583 } else { 584 var dateFormatted = typeof options.messagesDateFormat !== 'undefined' && options.messagesDateFormat.length > 0 585 ? wiseChatUtilsMomentJS.moment(date).format(options.messagesDateFormat) 586 : dateFormatter.getLocalizedDate(date); 587 588 formattedDateAndTime = spanDate + dateFormatted + spanClose + ' ' + spanTime + timeFormatted + spanClose; 589 isFixed = true; 590 } 591 592 element.html(formattedDateAndTime); 593 if (isFixed) { 594 element.attr('data-fixed', '1'); 595 } 596 } 597 598 /** 599 * Format all elements containing dates withing parent container. 600 * 601 * @param {jQuery} date Date to format as a string 602 * @param {String} nowISODate Now date 603 * 604 */ 605 function convertUTCMessagesTime(parentContainer, nowISODate) { 606 if (dateAndTimeMode === 'hidden') { 607 return; 608 } 609 parentContainer.find('.wcMessageTime:not([data-fixed])').each(function(index, element) { 610 element = jQuery(element); 611 612 var date = dateFormatter.parseISODate(element.data('utc')); 613 var nowDate = dateFormatter.parseISODate(nowISODate); 614 if (dateAndTimeMode === 'elapsed') { 615 formatElapsedDateAndTime(date, nowDate, element); 616 } else { 617 formatFullDateAndTime(date, nowDate, element); 618 } 619 }); 620 } 621 622 // public API: 623 this.convertUTCMessagesTime = convertUTCMessagesTime; 624} 625
626/** 627 * WiseChatProgressBar - controls the main progress bar. 628 */ 629function WiseChatProgressBar(options) { 630 var container = jQuery('#' + options.chatId); 631 var progressBar = container.find('.wcMainProgressBar'); 632 633 // provide access to progress events to AJAX requests: 634 (function addXhrProgressEvent(jQuery) { 635 var originalXhr = jQuery.ajaxSettings.xhr; 636 jQuery.ajaxSetup({ 637 xhr: function() { 638 var req = originalXhr.call(jQuery.ajaxSettings), that = this; 639 if (req) { 640 if (typeof req.addEventListener == "function" && that.progress !== undefined) { 641 req.addEventListener("progress", function(evt) { 642 that.progress(evt); 643 }, false); 644 } 645 if (typeof req.upload == "object" && that.progressUpload !== undefined) { 646 req.upload.addEventListener("progress", function(evt) { 647 that.progressUpload(evt); 648 }, false); 649 } 650 } 651 return req; 652 } 653 }); 654 })(jQuery); 655 656 function show() { 657 progressBar.show(); 658 } 659 660 function hide() { 661 progressBar.hide(); 662 } 663 664 function setValue(value) { 665 progressBar.attr("value", value); 666 } 667 668 // public API: 669 this.show = show; 670 this.hide = hide; 671 this.setValue = setValue; 672} 673 674/** 675 * WiseChatEmoticonsPanel class is responsible for displaying emoticons layer and inserting selected emoticon 676 * into message input field. 677 * 678 * @param {Object} options 679 * @param {WiseChatMessages} messages 680 * @constructor 681 */ 682function WiseChatEmoticonsPanel(options, messages) { 683 var EMOTICONS_1 = [ 684 'zip-it', 'blush', 'angry', 'not-one-care', 'laugh-big', 'please', 'cool', 'minishock', 685 'devil', 'silly', 'smile', 'devil-laugh', 'heart', 'not-guilty', 'hay', 686 'in-love', 'meow', 'tease', 'gift', 'kissy', 'sad', 'speechless', 'goatse', 687 'fools', 'why-thank-you', 'wink', 'angel', 'annoyed', 'flower', 'surprised', 688 'female', 'laugh', 'ill', 'total-shock', 'zzz', 'clock', 'oh', 'mail', 'crazy', 689 'cry', 'boring', 'geek' 690 ]; 691 var EMOTICONS_SHORTCUTS_1 = { 692 'smile': ':)', 'wink': ';)', 'laugh': ':D', 'laugh-big': 'xD', 693 'sad': ':(', 'cry': ';(', 'kissy': ':*', 'silly': ':P', 694 'crazy': ';P', 'angry': ':[', 'devil-laugh': ':>', 'devil': ':]', 'goatse': ':|' 695 }; 696 var FILES_EXTENSION_1 = 'png'; 697 var SUBDIRECTORY_1 = ''; 698 var LAYER_WIDTH_1 = null; 699 700 var EMOTICONS_2 = [ 701 'angry', 'bulb', 'cafe', 'clap', 'clouds', 'cry', 'devil', 'gift', 'handshake', 702 'heart', 'kissy', 'laugh-big', 'no', 'ok', 'feel_peace', 'oh_please', 'rain', 'scared', 703 'silly', 'snail', 'sun', 'baloons', 'bye', 'cake', 'cleaver', 'cool', 'cry_big', 704 'drink', 'hat', 'heart_big', 'laugh', 'moon', 'offended', 'omg', 'a_phone', 705 'question', 'sad', 'shy', 'smile', 'stars', 'wine' 706 ]; 707 var EMOTICONS_SHORTCUTS_2 = { 708 'smile': ':)', 'wink': ';)', 'laugh': ':D', 'laugh-big': 'xD', 709 'sad': ':(', 'cry': ';(', 'kissy': ':*', 'silly': ':P', 710 'crazy': ';P', 'angry': ':[', 'devil-laugh': ':>', 'devil': ':]' 711 }; 712 var FILES_EXTENSION_2 = 'gif'; 713 var SUBDIRECTORY_2 = 'set_2/'; 714 var LAYER_WIDTH_2 = 235; 715 716 var EMOTICONS_3 = [ 717 'angel', 'confused', 'cthulhu', 'drugged', 'grinning', 'horrified', 'kawaii', 'madness', 718 'shy', 'spiteful', 'terrified', 'tongue_out', 'tongue_out_up_left', 'winking_grinning', 719 'angry', 'cool', 'cute', 'frowning', 'happy', 'hug', 'kissing', 'malicious', 'sick', 720 'stupid', 'thumbs_down', 'tongue_out_laughing', 'unsure', 'winking_tongue_out', 'aww',
721 'creepy', 'cute_winking', 'gasping', 'happy_smiling', 'irritated', 'laughing', 'naww', 722 'smiling', 'surprised', 'thumbs_up', 'tongue_out_left', 'unsure_2', 'blushing', 'crying', 723 'devil', 'greedy', 'heart', 'irritated_2', 'lips_sealed', 'i_am_pouting', 'speechless', 724 'surprised_2', 'tired', 'tongue_out_up', 'winking' 725 ]; 726 var EMOTICONS_SHORTCUTS_3 = { 727 'smiling': ':)', 'winking': ';)', 'laughing': ':D', 'madness': 'xD', 728 'frowning': ':(', 'crying': ';(', 'kissing': ':*', 'tongue_out': ':P', 729 'winking_tongue_out': ';P', 'angry': ':[', 'devil': ':>', 'devil': ':]', 'irritated': ':|' 730 }; 731 var FILES_EXTENSION_3 = 'png'; 732 var SUBDIRECTORY_3 = 'set_3/'; 733 var LAYER_WIDTH_3 = 195; 734 735 var EMOTICONS_4 = [ 736 'angel', 'beer', 'clock', 'crying', 'drink', 'eyeroll', 'glasses-cool', 'jump', 737 'mad-tongue', 'sad', 'sick', 'smile-big', 'thinking', 'wilt', 738 'angry', 'bomb', 'cloudy', 'cute', 'drool', 'fingers-crossed', 'go-away', 739 'kiss', 'mail', 'shock', 'silly', 'smirk', 'tongue', 'wink', 740 'arrogant', 'bye', 'coffee', 'devil', 'embarrassed', 'freaked-out', 'good', 741 'laugh', 'mean', 'shout', 'sleepy', 'star', 'vampire', 'worship', 742 'bad', 'cake', 'confused', 'disapointed', 'excruciating', 'giggle', 'in-love', 743 'love', 'neutral', 'rotfl', 'shut-mouth', 'smile', 'struggle', 'weep', 'yawn', 744 'beauty', 'hypnotized', 'island', 'quiet', 'rose', 'soccerball' 745 ]; 746 var EMOTICONS_SHORTCUTS_4 = { 747 'smile': ':)', 'wink': ';)', 'laugh': ':D', 'smile-big': 'xD', 748 'sad': ':(', 'crying': ';(', 'kiss': ':*', 'tongue': ':P', 749 'silly': ';P', 'angry': ':[', 'devil': ':>', 'devil': ':]', 'neutral': ':|' 750 }; 751 var FILES_EXTENSION_4 = 'png'; 752 var SUBDIRECTORY_4 = 'set_4/'; 753 var LAYER_WIDTH_4 = 280; 754 755 var LAYER_ID = 'wcEmoticonsLayer' + options.chatId; 756 var container = jQuery('#' + options.chatId); 757 var insertEmoticonButton = container.find('.wcInsertEmoticonButton'); 758 var layer = jQuery('#' + LAYER_ID); 759 760 var EMOTICONS = EMOTICONS_1; 761 var EMOTICONS_SHORTCUTS = EMOTICONS_SHORTCUTS_1; 762 var FILES_EXTENSION = FILES_EXTENSION_1; 763 var SUBDIRECTORY = SUBDIRECTORY_1; 764 var LAYER_WIDTH = null; 765 switch (options.emoticonsSet) { 766 case 2: 767 EMOTICONS = EMOTICONS_2; 768 EMOTICONS_SHORTCUTS = EMOTICONS_SHORTCUTS_2; 769 FILES_EXTENSION = FILES_EXTENSION_2; 770 SUBDIRECTORY = SUBDIRECTORY_2; 771 LAYER_WIDTH = LAYER_WIDTH_2; 772 break; 773 case 3: 774 EMOTICONS = EMOTICONS_3; 775 EMOTICONS_SHORTCUTS = EMOTICONS_SHORTCUTS_3; 776 FILES_EXTENSION = FILES_EXTENSION_3; 777 SUBDIRECTORY = SUBDIRECTORY_3; 778 LAYER_WIDTH = LAYER_WIDTH_3; 779 break; 780 case 4: 781 EMOTICONS = EMOTICONS_4; 782 EMOTICONS_SHORTCUTS = EMOTICONS_SHORTCUTS_4; 783 FILES_EXTENSION = FILES_EXTENSION_4; 784 SUBDIRECTORY = SUBDIRECTORY_4; 785 LAYER_WIDTH = LAYER_WIDTH_4; 786 break; 787 } 788 789 if (insertEmoticonButton.length > 0 && layer.length === 0) { 790 layer = jQuery('<div />') 791 .attr('id', LAYER_ID) 792 .attr('class', 'wcEmoticonsLayer') 793 .hide(); 794 jQuery('body').append(layer); 795 796 // build buttons: 797 for (var i = 0; i < EMOTICONS.length; i++) { 798 var emoticon = EMOTICONS[i]; 799 var imageSrc = options.emoticonsBaseURL + SUBDIRECTORY + emoticon + '.' + FILES_EXTENSION; 800 var button = jQuery('<a />') 801 .attr('href', 'javascript://') 802 .attr('title', emoticon) 803 .append(jQuery('<img />').attr('src', imageSrc)) 804 .click(function (emoticon) { 805 return function () { 806 onEmoticonClick(emoticon); 807 } 808 }(emoticon)); 809 layer.append(button); 810 } 811 } 812 813 if (LAYER_WIDTH !== null) { 814 layer.css('width', LAYER_WIDTH + 'px'); 815 } 816 817 function hideLayer() { 818 layer.hide(); 819 jQuery(document).unbind("mousedown", onDocumentMouseDown); 820 } 821 822 function showLayer() { 823 layer.show(); 824 jQuery(document).bind("mousedown", onDocumentMouseDown); 825 } 826 827 function onDocumentMouseDown(event) { 828 if (insertEmoticonButton[0] != event.target && layer[0] != event.target && !jQuery.contains(layer[0], event.target)) { 829 hideLayer(); 830 } 831 } 832 833 function onInsertEmoticonButtonClick(e) { 834 e.preventDefault(); 835 836 if (!layer.is(':visible')) { 837 var leftValue = insertEmoticonButton.offset().left - layer.outerWidth() - 5; 838 if (leftValue < 0) { 839 leftValue = container.offset().left; 840 841 if (container.outerWidth() < LAYER_WIDTH) { 842 layer.css({ 843 width: container.outerWidth() + 'px' 844 }); 845 846 } else { 847 layer.css('width', LAYER_WIDTH + 'px'); 848 } 849 } 850 var topValue = insertEmoticonButton.offset().top - layer.outerHeight(); 851 852 layer.css({ 853 top: topValue, 854 left: leftValue 855 }); 856 showLayer(); 857 } else { 858 hideLayer(); 859 } 860 } 861
862 var onEmoticonClick = function(emoticon) { 863 var emoticonCode = EMOTICONS_SHORTCUTS[emoticon] ? EMOTICONS_SHORTCUTS[emoticon] : '<' + emoticon + '>'; 864 messages.appendTextToInputField(emoticonCode); 865 hideLayer(); 866 }; 867 868 insertEmoticonButton.click(onInsertEmoticonButtonClick); 869} 870 871// chat init script: 872jQuery(window).on('load', function() { 873 jQuery(".wcContainer[data-wc-config]").each(function(index) { 874 var config = jQuery(this).data('wc-config'); 875 if (typeof config !== 'object') { 876 jQuery(this).prepend('<strong style="color:#f00;">Error: invalid Wise Chat configuration</strong>'); 877 return; 878 } 879 880 var messages = jQuery(this).find('.wcMessages'); 881 if (messages.length > 0 && config.messagesOrder == 'ascending') { 882 messages.scrollTop(messages[0].scrollHeight); 883 } 884 885 if (typeof(window['wiseChatInstances']) == 'undefined') { 886 window['wiseChatInstances'] = {}; 887 } 888 889 if (typeof(window.wiseChatInstances[config.chatId]) == 'undefined') { 890 window.wiseChatInstances[config.chatId] = new WiseChatController(config); 891 } 892 }); 893});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.