1//************************************************/ 2/* IPB3 Javascript */ 3/* -------------------------------------------- */ 4/* ipb.js - Global code */ 5/* (c) IPS, Inc 2008 */ 6/* -------------------------------------------- */ 7/* Author: Rikki Tissier */ 8/************************************************/ 9 10/* ===================================================================================================== */ 11/* IPB3 JS Debugging */ 12var USE_RTE = 0; 13var Debug = { 14 write: function( text ){ 15 if( jsDebug && !Object.isUndefined(window.console) ){ 16 console.log( text ); 17 } 18 /*else if( jsDebug ) 19 { 20 if( !$('_inline_debugging') ){ 21 var _inline_debug = new Element('div', { id: '_inline_debugging' }).setStyle('background: rgba(0,0,0,0.7); color: #fff; padding: 10px; width: 97%; height: 150px; position: absolute; bottom: 0; overflow: auto; z-index: 50000').show(); 22 23 if( !Object.isUndefined( $$('body')[0] ) ){ 24 $$('body')[0].insert( _inline_debug ); 25 } 26 } 27 28 try { 29 $('_inline_debugging').innerHTML += "<br />" + text; 30 } catch(err){} 31 }*/ 32 }, 33 dir: function( values ){ 34 if( jsDebug && !Object.isUndefined(window.console) && ! Prototype.Browser.IE && ! Prototype.Browser.Opera ){ 35 console.dir( values ); 36 } 37 }, 38 error: function( text ){ 39 if( jsDebug && !Object.isUndefined(window.console) ){ 40 console.error( text ); 41 } 42 }, 43 warn: function( text ){ 44 if( jsDebug && !Object.isUndefined(window.console) ){ 45 console.warn( text ); 46 } 47 }, 48 info: function( text ){ 49 if( jsDebug && !Object.isUndefined(window.console) ){ 50 console.info( text ); 51 } 52 } 53}; 54 55/* Set up version specifics */ 56Prototype.Browser.IE6 = Prototype.Browser.IE && parseInt(navigator.userAgent.
56substring(navigator.userAgent.indexOf("MSIE")+5)) == 6; 57Prototype.Browser.IE7 = Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 7; 58Prototype.Browser.IE8 = Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 8; 59Prototype.Browser.IE9 = Prototype.Browser.IE && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5)) == 9; 60 61/* Add in stuff prototype does not include */ 62Prototype.Browser.Chrome = Prototype.Browser.WebKit && ( navigator.userAgent.indexOf('Chrome/') > -1 ); 63 64/* Older browsers do not have String.trim() */ 65if ( !String.prototype.trim ) 66{ 67 String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, ''); }; 68} 69 70/* ===================================================================================================== */ 71/* OVERWRITE getOffsetParent TO FIX TD ISSUE */ 72 73function isBody(element) { 74 return element.nodeName.toUpperCase() === 'BODY'; 75} 76 77function isHtml(element) { 78 return element.nodeName.toUpperCase() === 'HTML'; 79} 80 81function isDocument(element) { 82 return element.nodeType === Node.DOCUMENT_NODE; 83} 84 85function isDetached(element) { 86 return element !== document.body && 87 !Element.descendantOf(element, document.body); 88} 89 90Element.Methods.getOffsetParent = function(element) { 91 element = $(element); 92 93 if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) 94 return $(document.body); 95 96 if( Prototype.Browser.IE ){ 97 if (element.offsetParent && element.offsetParent != document.body && Element.getStyle( element.offsetParent, 'position' ) != 'static') return $(element.offsetParent); 98 if (element == document.body) return $(element); 99 } else { 100 var isInline = (Element.getStyle(element, 'display') === 'inline'); 101 if (!isInline && element.offsetParent && Element.getStyle(element.offsetParent,'position') != 'static') return $(element.offsetParent); 102 } 103 104 while ((element = element.parentNode) && element !== document.body) { 105 if (Element.getStyle(element, 'position') !== 'static') { 106 return isHtml(element) ? $(document.body) : $(element); 107 } 108 } 109 110 return $(document.body); 111} 112 113/*Event.observe( window, 'load', function(e){ 114 Element.Methods.getOffsetParent = function( element ){ 115 //alert( "Using overloaded getOffsetParent" ); 116 if (element.offsetParent && element.offsetParent != document.body) return $(element.offsetParent); 117 if (element == document.body) return $(element); 118 119 while ((element = element.parentNode) && element != document.body) 120 if (Element.getStyle(element, 'position') != 'static') 121 return $(element); 122 123 return $(document.body); 124 }; 125}); 126 127function _getOffsetParent( element ) 128{ 129 //alert( "Using overloaded getOffsetParent" ); 130 if (element.offsetParent && element.offsetParent != document.body) return $(element.offsetParent); 131 if (element == document.body) return $(element); 132 133 while ((element = element.parentNode) && element != document.body ) 134 if (Element.getStyle(element, 'position') != 'static') 135 return $(element); 136 137 return $(document.body); 138}*/ 139 140/* ===================================================================================================== */ 141/* MAIN ROUTINE */ 142 143window.IPBoard = Class.create({ 144 namePops: [], 145 topicPops: [], 146 vars: [], 147 lang: [], 148 templates: [], 149 editors: $A(), 150 initDone: false, 151 152 initialize: function() 153 { 154 Debug.write("IPB js is loading..."); 155 156 document.observe("dom:loaded", function(){ 157 158 this.Cookie.init(); 159 // Show a little loading graphic 160 Ajax.Responders.register({ 161 onLoading: function( handler ) { 162 if( !Object.isUndefined( handler['options']['hideLoader'] ) && handler['options']['hideLoader'] != false ){ 163 return; 164 } 165 166 if( !$('ajax_loading') ){ 167 if( !ipb.templates['ajax_loading'] ){ return; } 168 $('ipboard_body').insert( ipb.templates['ajax_loading'] ); 169 } 170 171 var effect = new Effect.Appear( $('ajax_loading'), { duration: 0.2 } ); 172 }, 173 onComplete: function() { 174 175 if( !$('ajax_loading') || !$('ajax_loading').visible() ){ return; } 176 var effect = new Effect.Fade( $('ajax_loading'), { duration: 0.2 } ); 177 178 if ( ! Object.isUndefined( ipb.hoverCard ) ){ 179 ipb.hoverCardRegister.postAjaxInit(); 180 } 181 182 /* General links to functions */ 183 $$("[data-clicklaunch]").invoke('clickLaunch'); 184 185 /* Quote functions */ 186 ipb.global.parseQuoteBoxes(); 187 ipb.global.removeLinkedLightbox(); 188 }, 189 onSuccess: function() { 190 if ( ! Object.isUndefined( ipb.hoverCard ) ){ 191 ipb.hoverCardRegister.postAjaxInit(); 192 } 193 }, 194 onFailure: function( t ) 195 { 196 if( !$('ajax_loading') || !$('ajax_loading').visible() ){ return; } 197 var effect = new Effect.Fade( $('ajax_loading'), { duration: 0.2 } ); 198 199 if ( ! Object.isUndefined( ipb.global ) ) 200 { 201 ipb.global.showInlineNotification( ipb.lang['ajax_failure'] ); 202 } 203 204 }, 205 onException: function( t, exception ) 206 { 207 if( !$('ajax_loading') || !$('ajax_loading').visible() ){ return; } 208 var effect = new Effect.Fade( $('ajax_loading'), { duration: 0.2 } ); 209 210 Debug.error( exception ); 211 212 if ( ! Object.isUndefined( ipb.global ) ) 213 { 214 //ipb.global.showInlineNotification( ipb.lang['ajax_failure'] ); 215 } 216 } 217 }); 218 219 // Initialize our delegation manager 220 ipb.delegate.initialize(); 221 ipb.initDone = true; 222 223 }.bind(this)); 224 }, 225 226 positionCenter: function( elem, dir ) 227 { 228 if( !$(elem) ){ return; } 229 elem_s = $(elem).getDimensions(); 230 window_s = document.viewport.getDimensions(); 231 window_offsets = document.viewport.getScrollOffsets(); 232 233 center = { left: ((window_s['width'] - elem_s['width']) / 2), 234 top: ((window_s['height'] - elem_s['height']) / 2) 235 }; 236 237 if( typeof(dir) == 'undefined' || ( dir != 'h' && dir != 'v' ) ) 238 {
239 $(elem).setStyle('top: ' + center['top'] + 'px; left: ' + center['left'] + 'px'); 240 } 241 else if( dir == 'h' ) 242 { 243 $(elem).setStyle('left: ' + center['left'] + 'px'); 244 } 245 else if( dir == 'v' ) 246 { 247 $(elem).setStyle('top: ' + center['top'] + 'px'); 248 } 249 250 $(elem).setStyle('position: fixed'); 251 }, 252 showModal: function() 253 { 254 if( !$('ipb_modal') ) 255 { 256 this.createModal(); 257 } 258 this.modal.show(); 259 }, 260 hideModal: function() 261 { 262 if( !$('ipb_modal') ){ return; } 263 this.modal.hide(); 264 }, 265 createModal: function() 266 { 267 this.modal = new Element('div', { id: 'ipb_modal' } ).hide().addClassName('modal'); 268 this.modal.setStyle("width: 100%; height: 100%; position: fixed; top: 0px; left: 0px; overflow: hidden; z-index: 1000; opacity: 0.2"); 269 $('ipboard_body').insert({bottom: this.modal}); 270 }, 271 editorInsert: function( content, editorid ) 272 { 273 // If no editor id supplied, lets use the first one 274 if( !editorid ) { 275 var editor = ipb.textEditor.getEditor(); 276 } else { 277 var editor = ipb.textEditor.getEditor(editorid); 278 } 279 280 if( Object.isUndefined( editor ) ) 281 { 282 /* Get current */ 283 var editor = ipb.textEditor.getEditor(); 284 } 285 286 editor.insert( content ); 287 } 288}); 289 290/* ===================================================================================================== */ 291/* IPB3 Delegation manager */ 292/* Simple class that allows us to specify css selectors and an associated function to run */ 293/* when an appropriate element is clicked */ 294 295IPBoard.prototype.delegate = { 296 store: $A(), 297 298 initialize: function() 299 { 300 document.observe('click', function(e){ 301 302 if( Event.isLeftClick(e) || Prototype.Browser.IE || ipb.vars['is_touch'] ) // IE doesnt provide isLeftClick info for click event, touch devices either 303 { 304 var elem = null; 305 var handler = null; 306 307 var target = ipb.delegate.store.find( function(item){ 308 elem = e.findElement( item['selector'] ); 309 if( elem ){ 310 handler = item; 311 return true; 312 } else { 313 return false; 314 } 315 }); 316 317 if( !Object.isUndefined( target ) ) 318 { 319 if( handler ) 320 { 321 Debug.write("Firing callback for selector " + handler['selector'] ); 322 handler['callback']( e, elem, handler['params'] ); 323 } 324 } 325 } 326 }); 327 }, 328 329 register: function( selector, callback, params ) 330 { 331 ipb.delegate.store.push( { selector: selector, callback: callback, params: params } ); 332 } 333}; 334 335/* ===================================================================================================== */ 336/* IPB3 Cookies */ 337 338/* Meow */ 339IPBoard.prototype.Cookie = { 340 store: [], 341 initDone: false, 342 343 set: function( name, value, sticky ) 344 { 345 var expires = ''; 346 var path = '/'; 347 var domain = ''; 348 349 if( !name ) 350 { 351 return; 352 } 353 354 if( sticky ) 355 { 356 if( sticky == 1 ) 357 { 358 expires = "; expires=Wed, 1 Jan 2020 00:00:00 GMT"; 359 } 360 else if( sticky == -1 ) // Delete 361 { 362 expires = "; expires=Thu, 01-Jan-1970 00:00:01 GMT"; 363 } 364 else if( sticky.length > 10 ) 365 { 366 expires = "; expires=" + sticky; 367 } 368 } 369 if( ipb.vars['cookie_domain'] ) 370 { 371 domain = "; domain=" + ipb.vars['cookie_domain']; 372 } 373 if( ipb.vars['cookie_path'] ) 374 { 375 path = ipb.vars['cookie_path']; 376 } 377 378 document.cookie = ipb.vars['cookie_id'] + name + "=" + escape( value ) + "; path=" + path + expires + domain + ';'; 379 380 ipb.Cookie.store[ name ] = value; 381 382 Debug.write( "Set cookie: " + ipb.vars['cookie_id'] + name + "=" + value + "; path=" + path + expires + domain + ';' ); 383 }, 384 get: function( name ) 385 { 386 /* Init done yet? */ 387 if ( ipb.Cookie.initDone !== true ) 388 { 389 ipb.Cookie.init(); 390 } 391 392 if( ipb.Cookie.store[ name ] ) 393 { 394 return ipb.Cookie.store[ name ]; 395 } 396 397 return ''; 398 }, 399 doDelete: function( name ) 400 { 401 Debug.write("Deleting cookie " + name); 402 ipb.Cookie.set( name, '', -1 ); 403 }, 404 init: function() 405 { 406 // Already init? 407 if ( ipb.Cookie.initDone ) 408 { 409 return true; 410 } 411 412 // Init cookies by pulling in document.cookie 413 skip = ['session_id', 'ipb_admin_session_id', 'member_id', 'pass_hash']; 414 cookies = $H( document.cookie.replace(" ", '').toQueryParams(";") ); 415 416 if( cookies ) 417 {
418 cookies.each( function(cookie){ 419 cookie[0] = cookie[0].strip(); 420 421 if( ipb.vars['cookie_id'] != '' ) 422 { 423 if( !cookie[0].startsWith( ipb.vars['cookie_id'] ) ) 424 { 425 return; 426 } 427 else 428 { 429 cookie[0] = cookie[0].replace( ipb.vars['cookie_id'], '' ); 430 } 431 } 432 433 if( skip[ cookie[0] ] ) 434 { 435 return; 436 } 437 else 438 { 439 ipb.Cookie.store[ cookie[0] ] = unescape( cookie[1] || '' ); 440 Debug.write( "Loaded cookie: " + cookie[0] + " = " + cookie[1] ); 441 } 442 }); 443 } 444 445 ipb.Cookie.initDone = true; 446 } 447}; 448 449/* ===================================================================================================== */ 450/* Form validation */ 451 452IPBoard.prototype.validate = { 453 // Checks theres actually a value 454 isFilled: function( elem ) 455 { 456 if( !$( elem ) ){ return null; } 457 return !$F(elem).blank(); 458 }, 459 isNumeric: function( elem ) 460 { 461 if( !$( elem ) ){ return null; } 462 return $F(elem).match( /^[\d]+?$/ ); 463 }, 464 isMatching: function( elem1, elem2 ) 465 { 466 if( !$( elem1 ) || !$( elem2 ) ){ return null; } 467 return $F(elem1) == $F(elem2); 468 }, 469 email: function( elem ) 470 { 471 if( !$( elem ) ){ return null; } 472 if( $F( elem ).match( /^.+@.+\..{2,4}$/ ) ){ 473 return true; 474 } else { 475 return false; 476 } 477 } 478}; 479 480/* ===================================================================================================== */ 481/* AUTOCOMPLETE */ 482 483IPBoard.prototype.Autocomplete = Class.create( { 484 485 initialize: function(id, options) 486 { 487 this.id = $( id ).id; 488 this.timer = null; 489 this.last_string = ''; 490 this.internal_cache = $H(); 491 this.pointer = 0; 492 this.items = $A(); 493 this.observing = true; 494 this.objHasFocus = null; 495 this.options = Object.extend({ 496 min_chars: 3, 497 multibox: false, 498 global_cache: false, 499 goToUrl: false, 500 classname: 'ipb_autocomplete', 501 templates: { 502 wrap: new Template("<ul id='#{id}'></ul>"), 503 item: new Template("<li id='#{id}' data-url='#{url}'>#{itemvalue}</li>") 504 } 505 }, arguments[1] || {}); 506 507 //----------------------------------------- 508 509 if( !$( this.id ) ){ 510 Debug.error("Invalid textbox ID"); 511 return false; 512 } 513 514 this.obj = $( this.id ); 515 516 if( !this.options.url ) 517 { 518 Debug.error("No URL specified for autocomplete"); 519 return false; 520 } 521 522 $( this.obj ).writeAttribute('autocomplete', 'off'); 523 524 this.buildList(); 525 526 // Observe keypress 527 $( this.obj ).observe('focus', this.timerEventFocus.bindAsEventListener( this ) ); 528 $( this.obj ).observe('blur', this.timerEventBlur.bindAsEventListener( this ) ); 529 $( this.obj ).observe('keydown', this.eventKeypress.bindAsEventListener( this ) ); 530 }, 531 532 eventKeypress: function(e) 533 { 534 if( ![ Event.KEY_TAB, Event.KEY_UP, Event.KEY_DOWN, Event.KEY_LEFT, Event.KEY_RIGHT, Event.KEY_RETURN ].include( e.keyCode ) ){ 535 return; // Not interested in anything else 536 } 537 538 // & and up key are both keycode 38. So if we're holding shift, ignore this 539 console.log( e.shiftKey ); 540 if ( e.shiftKey === true ) { 541 return; 542 } 543 544 if( $( this.list ).visible() ) 545 { 546 switch( e.keyCode ) 547 { 548 case Event.KEY_TAB: 549 case Event.KEY_RETURN: 550 this.selectCurrentItem(e); 551 break; 552 case Event.KEY_UP: 553 case Event.KEY_LEFT: 554 this.selectPreviousItem(e); 555 break; 556 case Event.KEY_DOWN: 557 case Event.KEY_RIGHT: 558 this.selectNextItem(e); 559 break; 560 } 561 562 Event.stop(e); 563 } 564 }, 565 566 // MOUSE & KEYBOARD EVENT 567 selectCurrentItem: function(e) 568 { 569 var current = $( this.list ).down('.active'); 570 this.unselectAll(); 571 572 if( !Object.isUndefined( current ) ) 573 { 574 var itemid = $( current ).id.replace( this.id + '_ac_item_', ''); 575 if( !itemid ){ return; } 576 577 // Go to URL? 578 if( this.options.goToUrl && $( current ).readAttribute('data-url') ) 579 { 580 window.location = $( current ).readAttribute('data-url'); 581 return false; 582 } 583 584 // Get value 585 var value = this.items[ itemid ] 586 .replace('&', '&') 587 .replace( /'/g, "'" ) 588 .replace( />/g, '>' ) 589 .replace( /</g, '<' ) 590 .replace( /!/g, '!' ); 591 592 if( this.options.multibox ) 593 { 594 // some logic to get current name 595 if( $F( this.obj ).indexOf(',') !== -1 ) 596 { 597 var pieces = $F( this.obj ).split(','); 598 pieces[ pieces.length - 1 ] = ''; 599 600 $( this.obj ).value = pieces.join(',') + ' '; 601 } 602 else 603 { 604 $( this.obj ).value = ''; 605 $( this.obj ).focus(); 606 } 607 608 $( this.obj ).value = $F( this.obj ) + value + ', '; 609 } 610 else 611 {
612 $( this.obj ).value = value; 613 614 var effect = new Effect.Fade( $(this.list), { duration: 0.3 } ); 615 //this.observing = false; 616 } 617 } 618 619 $( this.obj ).focus(); 620 621 /* Stop cursor jumping back when adding input */ 622 if ( Prototype.Browser.IE ) 623 { 624 if ( $( this.obj ).createTextRange ) 625 { 626 var r = $( this.obj ).createTextRange(); 627 r.moveStart("character", $( this.obj ).value.length); 628 r.select(); 629 } 630 } 631 }, 632 633 // MOUSE EVENT 634 selectThisItem: function(e) 635 { 636 this.unselectAll(); 637 638 var items = $( this.list ).immediateDescendants(); 639 var elem = Event.element(e); 640 641 // Find the element 642 while( !items.include( elem ) ) 643 { 644 elem = elem.up(); 645 } 646 647 $( elem ).addClassName('active'); 648 }, 649 650 // KEYBOARD EVENT 651 selectPreviousItem: function(e) 652 { 653 var current = $( this.list ).down('.active'); 654 this.unselectAll(); 655 656 if( Object.isUndefined( current ) ) 657 { 658 this.selectFirstItem(); 659 } 660 else 661 { 662 var prev = $( current ).previous(); 663 664 if( prev ){ 665 $( prev ).addClassName('active'); 666 } 667 else 668 { 669 this.selectLastItem(); 670 } 671 } 672 }, 673 674 // KEYBOARD EVENT 675 selectNextItem: function(e) 676 { 677 // Get the current item 678 var current = $( this.list ).down('.active'); 679 this.unselectAll(); 680 681 if( Object.isUndefined( current ) ){ 682 this.selectFirstItem(); 683 } 684 else 685 { 686 var next = $( current ).next(); 687 688 if( next ){ 689 $( next ).addClassName('active'); 690 } 691 else 692 { 693 this.selectFirstItem(); 694 } 695 } 696 }, 697 698 // INTERNAL CALL 699 selectFirstItem: function() 700 { 701 if( !$( this.list ).visible() ){ return; } 702 this.unselectAll(); 703 704 $( this.list ).firstDescendant().addClassName('active'); 705 }, 706 707 // INTERNAL CALL 708 selectLastItem: function() 709 { 710 if( !$( this.list ).visible() ){ return; } 711 this.unselectAll(); 712 713 var d = $( this.list ).immediateDescendants(); 714 var l = d[ d.length -1 ]; 715 716 if( l ) 717 { 718 $( l ).addClassName('active'); 719 } 720 }, 721 722 unselectAll: function() 723 { 724 $( this.list ).childElements().invoke('removeClassName', 'active'); 725 }, 726 727 // Ze goggles are blurry! 728 timerEventBlur: function(e) 729 { 730 window.clearTimeout( this.timer ); 731 this.eventBlur.bind(this).delay( 0.6, e ); 732 }, 733 734 // Phew, ze goggles are focussed again 735 timerEventFocus: function(e) 736 { 737 this.timer = this.eventFocus.bind(this).delay(0.4, e); 738 }, 739 740 eventBlur: function(e) 741 { 742 this.objHasFocus = false; 743 744 if( $( this.list ).visible() ) 745 { 746 var effect = new Effect.Fade( $(this.list), { duration: 0.3 } ); 747 } 748 }, 749 750 eventFocus: function(e) 751 { 752 if( !this.observing ){ Debug.write("Not observing keypress"); return; } 753 this.objHasFocus = true; 754 755 // Keep loop going 756 this.timer = this.eventFocus.bind(this).delay(0.6, e); 757 758 var curValue = this.getCurrentName(); 759 if( curValue == this.last_string ){ return; } 760 761 if( curValue.length < this.options.min_chars ){ 762 // Hide list if necessary 763 if( $( this.list ).visible() ) 764 { 765 var effect = new Effect.Fade( $( this.list ), { duration: 0.3, afterFinish: function(){ $( this.list ).update(); }.bind(this) } ); 766 } 767 768 return; 769 } 770 771 this.last_string = curValue; 772 773 // Cached? 774 json = this.cacheRead( curValue ); 775 776 if( json == false ){ 777 // No results yet, get them 778 var request = new Ajax.Request( this.options.url + encodeURIComponent( curValue ), 779 { 780 method: 'get', 781 evalJSON: 'force', 782 onSuccess: function(t) 783 { 784 if( Object.isUndefined( t.responseJSON ) ) 785 { 786 // Well, this is bad. 787 Debug.error("Invalid response returned from the server"); 788 return; 789 } 790 791 if( t.responseJSON['error'] ) 792 { 793 switch( t.responseJSON['error'] ) 794 { 795 case 'requestTooShort': 796 Debug.warn("Server said request was too short, skipping..."); 797 break; 798 default: 799 Debug.error("Server returned an error: " + t.responseJSON['error']); 800 break; 801 } 802 803 return false; 804 } 805 806 if( t.responseText != "[]" ) 807 { 808 809 // Seems to be OK! 810 this.cacheWrite( curValue, t.responseJSON ); 811 this.updateAndShow( t.responseJSON ); 812 } 813 }.bind( this ) 814 } 815 ); 816 } 817 else 818 { 819 this.updateAndShow( json ); 820 } 821 822 //Debug.write( curValue ); 823 }, 824 825 updateAndShow: function( json ) 826 { 827 if( !json ){ return; } 828 829 this.updateList( json ); 830 831 if( !$( this.list ).visible() && this.objHasFocus ) 832 { 833 Debug.write("Showing"); 834 var effect = new Effect.Appear( $( this.list ), { duration: 0.3, afterFinish: function(){ this.selectFirstItem(); }.bind(this) } ); 835 } 836 }, 837 838 cacheRead: function( value ) 839 { 840 if( this.options.global_cache != false ) 841 { 842 if( !Object.isUndefined( this.options.global_cache.get( value ) ) ){ 843 Debug.write("Read from global cache"); 844 return this.options.global_cache.get( value ); 845 } 846 } 847 else 848 { 849 if( !Object.isUndefined( this.internal_cache.get( value ) ) ){
850 Debug.write("Read from internal cache"); 851 return this.internal_cache.get( value ); 852 } 853 } 854 855 return false; 856 }, 857 858 cacheWrite: function( key, value ) 859 { 860 if( this.options.global_cache !== false ){ 861 this.options.global_cache.set( key, value ); 862 } else { 863 this.internal_cache.set( key, value ); 864 } 865 866 return true; 867 }, 868 869 getCurrentName: function() 870 { 871 if( this.options.multibox ) 872 { 873 // some logic to get current name 874 if( $F( this.obj ).indexOf(',') === -1 ){ 875 return $F( this.obj ).strip(); 876 } 877 else 878 { 879 var pieces = $F( this.obj ).split(','); 880 var lastPiece = pieces[ pieces.length - 1 ]; 881 882 return lastPiece.strip(); 883 } 884 } 885 else 886 { 887 return $F( this.obj ).strip(); 888 } 889 }, 890 891 buildList: function() 892 { 893 if( $( this.id + '_ac' ) ) 894 { 895 return; 896 } 897 898 var finalPos = {}; 899 900 // Position menu to keep it on screen 901 var sourcePos = $( this.id ).viewportOffset(); 902 var sourceDim = $( this.id ).getDimensions(); 903 var delta = [0,0]; 904 var parent = null; 905 var screenScroll = document.viewport.getScrollOffsets(); 906 907 var ul = this.options.templates.wrap.evaluate({ id: this.id + '_ac' }); 908 909 /* In a modal pop up? */ 910 var test = $( this.id ).up('.popupWrapper'); 911 912 if ( ! Object.isUndefined( test ) && test.getStyle('position') == 'fixed' ) 913 { 914 $(this.id).up().insert( {bottom: ul} ); 915 916 parent = $( this.id ).getOffsetParent(); 917 delta = [ parseInt( parent.getStyle('left') / 2 ), parseInt( parent.getStyle('top') / 2 ) ]; 918 919 finalPos['left'] = delta[0]; 920 finalPos['top'] = delta[1] + screenScroll.top; 921 922 /* Make it appear over */ 923 $( this.id + '_ac' ).setStyle( { 'zIndex': 10002 } ); 924 } 925 else 926 { 927 $$('body')[0].insert( {bottom: ul} ); 928 929 if ( Element.getStyle( $( this.id ), 'position') == 'absolute') 930 { 931 parent = $( this.id ).getOffsetParent(); 932 delta = [ parseInt( parent.getStyle('left') ), parseInt( parent.getStyle('top') ) ]; 933 } 934 935 finalPos['left'] = sourcePos[0] - delta[0]; 936 finalPos['top'] = sourcePos[1] - delta[1] + screenScroll.top; 937 } 938 939 // Now try and keep it on screen 940 finalPos['top'] = finalPos['top'] + sourceDim.height; 941 942 $( this.id + '_ac' ).setStyle('position: absolute; top: ' + finalPos['top'] + 'px; left: ' + finalPos['left'] + 'px;').hide(); 943 944 945 this.list = $( this.id + '_ac' ); 946 }, 947 948 updateList: function( json ) 949 { 950 if( !json || !$( this.list ) ){ return; } 951 952 var newitems =''; 953 this.items = $A(); 954 955 json = $H( json ); 956 957 json.each( function( item ) 958 { 959 var li = this.options.templates.item.evaluate({ id: this.id + '_ac_item_' + item.key, 960 itemid: item.key, 961 itemvalue: item.value['showas'] || item.value['name'], 962 img: item.value['img'] || '', 963 img_w: item.value['img_w'] || '', 964 img_h: item.value['img_h'] || '', 965 url: item.value['url'] || '' 966 }); 967 this.items[ item.key ] = item.value['name']; 968 969 newitems = newitems + li; 970 }.bind(this) 971 ); 972 973 $( this.list ).update( newitems ); 974 $( this.list ).immediateDescendants().each( function(elem){ 975 $( elem ).observe('mouseover', this.selectThisItem.bindAsEventListener(this)); 976 $( elem ).observe('click', this.selectCurrentItem.bindAsEventListener(this)); 977 $( elem ).setStyle('cursor: pointer'); 978 }.bind(this)); 979 980 if( $( this.list ).visible() ) 981 { 982 this.selectFirstItem(); 983 } 984 } 985}); 986 987/** 988 * Get query string param 989 */ 990getQueryStringParamByName = function(name) 991{ 992 name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); 993 var regexS = "[\\?&]" + name + "=([^&#]*)"; 994 var regex = new RegExp(regexS); 995 var results = regex.exec(window.location.search); 996 997 if ( results == null ) 998 { 999 return ""; 1000 } 1001 else 1002 { 1003 return decodeURIComponent( results[1].replace(/\+/g, " ") ); 1004 } 1005}; 1006 1007/* ===================================================================================================== */ 1008/* Extended objects */ 1009 1010// Extend RegExp with escape 1011Object.extend( RegExp, { 1012 escape: function(text) 1013 { 1014 if (!arguments.callee.sRE) 1015 { 1016 var specials = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$' ]; 1017 //arguments.callee.sRE = new RegExp( '(\\' + specials.join('|\\') + ')' ); // IMPORTANT: dont use g flag 1018 arguments.callee.sRE = new RegExp( '(\\' + specials.join('|\\') + ')', 'g' ); 1019 } 1020 return text.replace(arguments.callee.sRE, '\\$1'); 1021 } 1022}); 1023 1024 1025// Escape regex 1026String.prototype.regExpEscape = function() 1027{ 1028 var text = this; 1029 return text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); 1030}; 1031 1032// Extend String with HTMLspecial chars 1033String.prototype.escapeHtml = function() 1034{ 1035 return this 1036 .replace(/&/g, "&") 1037 .replace(/</g, "<") 1038 .replace(/>/g, ">") 1039 .replace(/"/g, """) 1040 .replace(/'/g, "'"); 1041}; 1042//Extend String with HTMLspecial chars 1043String.prototype.unEscapeHtml = function() 1044{ 1045 var _t = this.replace( /&/g, "&" ); 1046 return _t 1047 .replace(/</g, "<") 1048 .replace(/>/g, ">") 1049 .replace(/"/g, '"') 1050 .replace(/'/g, "'") 1051 .replace(/'/g, "'"); 1052}; 1053 1054// Extend String with URL UTF-8 escape 1055String.prototype.encodeUrl = function() 1056{ 1057 var text = this;
1058 var regcheck = text.match(/[\x90-\xFF]/g); 1059 1060 if ( regcheck ) 1061 { 1062 for (var i = 0; i < regcheck.length; i++) 1063 { 1064 text = text.replace(regcheck[i], '%u00' + (regcheck[i].charCodeAt(0) & 0xFF).toString(16).toUpperCase()); 1065 } 1066 } 1067 1068 return escape(text).replace(/\+/g, "%2B").replace(/%20/g, '+').replace(/\*/g, '%2A').replace(/\//g, '%2F').replace(/@/g, '%40'); 1069}; 1070 1071// Extend String with URL UTF-8 escape - duplicated so it can be changed from above 1072String.prototype.encodeParam = function() 1073{ 1074 var text = this; 1075 var regcheck = text.match(/[\x90-\xFF]/g); 1076 1077 if ( regcheck ) 1078 { 1079 for (var i = 0; i < regcheck.length; i++) 1080 { 1081 text = text.replace(regcheck[i], '%u00' + (regcheck[i].charCodeAt(0) & 0xFF).toString(16).toUpperCase()); 1082 } 1083 } 1084 1085 /* Return just text as it is then encoded by prototype lib */ 1086 return escape(text).replace(/\+/g, "%2B"); 1087}; 1088 1089// Extend date object to return date and time as object 1090Date.prototype.getDateAndTime = function( unix ) 1091{ 1092 var a = new Date( parseInt( unix ) * 1000 ); 1093 var months = ipb.lang['gbl_months'].split(','); 1094 var year = a.getFullYear(); 1095 var month = months[a.getMonth()]; 1096 var date = a.getDate(); 1097 var hour = a.getHours(); 1098 var min = a.getMinutes(); 1099 var sec = a.getSeconds(); 1100 1101 return { year : year, 1102 monthName: month, 1103 month : ( '0' + a.getMonth() + 1 ).slice( -2 ), 1104 date : ( '0' + date ).slice( -2 ), 1105 hour : ( '0' + hour ).slice( -2 ), 1106 min : ( '0' + min ).slice( -2 ), 1107 sec : ( '0' + sec ).slice( -2 ), 1108 dst : a.getTimezoneOffset() }; 1109}; 1110 1111// Extend Date object with a function to check for DST 1112Date.prototype.getDST = function() 1113{ 1114 var beginning = new Date( "January 1, 2008" ); 1115 var middle = new Date( "July 1, 2008" ); 1116 var difference = middle.getTimezoneOffset() - beginning.getTimezoneOffset(); 1117 var offset = this.getTimezoneOffset() - beginning.getTimezoneOffset(); 1118 1119 if( difference != 0 ) 1120 { 1121 /* Hemisphere check */ 1122 if ( difference < 0 ) 1123 { 1124 /* Northern */ 1125 return (difference == offset) ? 1 : 0; 1126 } 1127 else 1128 { 1129 /* Southern */ 1130 return (difference != offset) ? 1 : 0; 1131 } 1132 } 1133 else 1134 { 1135 return 0; 1136 } 1137}; 1138 1139/* ==================================================================================================== */ 1140/* IPB3 JS Loader */ 1141 1142var Loader = { 1143 require: function( name ) 1144 { 1145 document.write("<script type='text/javascript' src='" + name + ".js'></script>"); 1146 }, 1147 boot: function() 1148 { 1149 $A( document.getElementsByTagName("script") ).findAll( 1150 function(s) 1151 { 1152 return (s.src && s.src.match(/ipb\.js(\?.*)?$/)); 1153 } 1154 ).each( 1155 function(s) { 1156 var path = s.src.replace(/ipb\.js(\?.*)?$/,''); 1157 var includes = s.src.match(/\?.*load=([a-zA-Z0-9_,\.]*)/); 1158 if( ! Object.isUndefined(includes) && includes != null && includes[1] ) 1159 { 1160 includes[1].split(',').each( 1161 function(include) 1162 { 1163 if( include ) 1164 { 1165 Loader.require( path + "ips." + include ); 1166 } 1167 } 1168 ); 1169 } 1170 } 1171 ); 1172 } 1173}; 1174var callback = { 1175 afterOpen: function( popup ){ 1176 try { 1177 $( 'pj_' + $(elem).identify() + '_input').activate(); 1178 } 1179 catch(err){ } 1180 } 1181}; 1182 1183/* ==================================================================================================== */ 1184/* ELEMENT EXTENSIONS (tooltips, authorpane etc.) */ 1185 1186Element.addMethods( { 1187 1188 getInnerText: function(element) 1189 { 1190 element = $(element); 1191 return element.innerText && ! window.opera ? element.innerText : element.innerHTML.stripScripts().unescapeHTML().replace(/[\n\r\s]+/g, ' '); 1192 }, 1193 1194 defaultize: function( element, lang ) 1195 { 1196 if( ipb.global._supportsPlaceholder == null ){ 1197 ipb.global._supportsPlaceholder = (function(){ 1198 var i = document.createElement('input'); 1199 return 'placeholder' in i; 1200 })(); 1201 } 1202 1203 if( ipb.global._supportsPlaceholder ){ 1204 if( $F( element ) == lang || $F( element ).empty() ){ 1205 $(element).removeClassName('inactive').writeAttribute('placeholder', lang).value = ''; 1206 } 1207 } else { 1208 if( $F( element ) == lang || $F( element ).empty() ){ 1209 $(element).addClassName('inactive').value = lang; 1210 } 1211 1212 $(element).observe('focus', function(e){ 1213 if( $(element).hasClassName('inactive') && ( $F(element) == '' || $F(element) == lang ) ){ 1214 $(element).removeClassName('inactive').value = ''; 1215 } else { 1216 $(element).removeClassName('inactive'); 1217 } 1218 }). 1219 observe('blur', function(e){ 1220 if( $F(element).empty() ){ 1221 $(element).addClassName('inactive').value = lang; 1222 } 1223 }); 1224 1225 // Try and find a form around the element 1226 var form = $( element ).up('form'); 1227 if( !Object.isUndefined( form ) ){ 1228 $( form ).observe('submit', function(e){ 1229 if( $(element).hasClassName('inactive') ){ 1230 $(element).value = ''; 1231 } 1232 }); 1233 } 1234 } 1235 }, 1236 1237 clickLaunch: function( element ) 1238 { 1239 var _callback = $( element ).readAttribute("data-clicklaunch"); 1240 var _scope = 'global'; 1241 1242 try { 1243 var _try = $( element ).readAttribute("data-scope"); 1244 _scope = ( _try ) ? _try.replace("ipb.", '') : _scope; 1245 } catch(e) { }; 1246
1247 if( $(element).retrieve('clickevent') ){ 1248 try { 1249 $(element).retrieve('clickevent').stop(); 1250 } catch(err){ }; 1251 } 1252 1253 var click = $(element).on( 'click', function(e) { 1254 Event.stop(e); 1255 ipb[_scope][_callback]( element, e); 1256 } ); 1257 $(element).store('clickevent', click); 1258 }, 1259 1260 confirmAction: function( element, callback ) 1261 { 1262 var _text = $( element ).readAttribute("data-confirmaction"); 1263 var _ok = ''; 1264 1265 if( callback ) 1266 { 1267 _ok = callback; 1268 } 1269 else if ( element.tagName == 'FORM' ) 1270 { 1271 _ok = "$('" + element.id +"').submit()"; 1272 } 1273 else 1274 { 1275 _ok = 'window.location=\'' + element.readAttribute( 'href' ) + '\''; 1276 } 1277 1278 if ( ! _text || _text == 'true' ) 1279 { 1280 _text = ipb.lang['gbl_confirm_desc']; 1281 } 1282 1283 var _options = { type: 'pane', 1284 modal: true, 1285 /* Inline JS makes it easier even if it is uglier */ 1286 initial: '<div><h3>' + ipb.lang['gbl_confirm_text'] + '</h3><div class="ipsPad ipsForm_center"><p>' + _text + '</p><br /><span onclick="ipb.global.popups[\'conact\'].hide();" class="clickable ipsButton_secondary important">' + ipb.lang['gbl_confirm_cancel'] + '</span> <span onclick="' + _ok + '" class="clickable ipsButton_secondary">' + ipb.lang['gbl_confirm_ok'] + '</span></div>', 1287 hideAtStart: false, 1288 w: '300px', 1289 h: 150 }; 1290 1291 if ( element.tagName == 'FORM' || callback ) 1292 { 1293 /* Fire immediately */ 1294 if ( ! Object.isUndefined( ipb.global.popups['conact'] ) ) 1295 { 1296 ipb.global.popups['conact'].kill(); 1297 } 1298 1299 ipb.global.popups['conact'] = new ipb.Popup( 'confirm', _options ); 1300 } 1301 else 1302 { 1303 $(element).on( 'click', function(e) 1304 { 1305 Event.stop(e); 1306 1307 if ( ! Object.isUndefined( ipb.global.popups['conact'] ) ) 1308 { 1309 ipb.global.popups['conact'].kill(); 1310 } 1311 1312 ipb.global.popups['conact'] = new ipb.Popup( 'confirm', _options ); 1313 1314 } ); 1315 } 1316 }, 1317 1318 tooltip: function( element, options ){ 1319 1320 options = Object.extend( { 1321 template: new Template("<div class='ipsTooltip' id='#{id}' style='display: none'><div class='ipsTooltip_inner'>#{content}</div></div>"), 1322 position: 'auto', 1323 content: $( element ).readAttribute("data-tooltip").stripTags().escapeHTML(), 1324 animate: true, 1325 overrideBrowser: true, 1326 delay: 0.4 1327 }, options); 1328 1329 var show = function(e){ 1330 if( options.delay && !options._still_going ){ 1331 return; // Action has been cancelled 1332 } 1333 1334 // Don't show if empty. Bug #36247 Tooltips 1335 if( ! options.content ){ 1336 return; 1337 } 1338 1339 var id = $(element).identify(); 1340 1341 if( !$( id + '_tooltip' ) ){ 1342 $( document.body ).insert({ 'bottom': options.template.evaluate({ 'id': id + '_tooltip', 'content': options.content }) } ); 1343 } 1344 1345 if( options.overrideBrowser && $(element).hasAttribute('title') ){ 1346 $(element).writeAttribute("data-title", $(element).readAttribute('title').stripTags().escapeHTML()).writeAttribute("title", false); 1347 } 1348 1349 var tooltip = $(id + '_tooltip').setStyle({position: 'absolute'}); 1350 1351 // Add word wrap for calculations 1352 //$(tooltip).setStyle('white-space: nowrap'); 1353 1354 var layout = $(element).getLayout(); 1355 var position = $(element).cumulativeOffset(); 1356 var dims = $( id + '_tooltip' ).getDimensions(); 1357 var docDim = $( document.body ).getLayout(); 1358 1359 // Detect best position for tooltip 1360 if( options.position == 'auto' ){ 1361 if( position.left + (layout.get('padding-box-width')/2) - (dims.width/2) < 0 ){ 1362 options.position = 'right'; 1363 } else if( position.left + (dims.width/2) > docDim.get('width') ){ 1364 options.position = 'left'; 1365 } else { 1366 options.position = 'top'; 1367 } 1368 } 1369 1370 Debug.write( dims ); 1371 1372 // And now position 1373 switch( options.position ){ 1374 case 'top': 1375 $(tooltip).setStyle( { top: (position.top - dims.height - 1) + 'px', left: (position.left + (layout.get('padding-box-width')/2) - (dims.width/2)) + 'px' } ).addClassName('top'); 1376 break; 1377 case 'bottom': 1378 $(tooltip).setStyle( { top: (position.top + layout.get('padding-box-height') + 1) + 'px', left: (position.left + (layout.get('padding-box-width')/2) - (dims.width/2)) + 'px' } ).addClassName('bottom'); 1379 break; 1380 case 'left': 1381 $(tooltip).setStyle( { top: (position.top - (layout.get('padding-box-height') / 2)) + 'px', left: (position.left - dims.width - 3) + 'px' }).addClassName('left'); 1382 break; 1383 case 'right': 1384 $(tooltip).setStyle( { top: (position.top - (layout.get('padding-box-height') / 2)) + 'px', left: (position.left + layout.get('padding-box-width') - 3) + 'px' }).addClassName('right'); 1385 break; 1386 } 1387 1388 // Remove word wrap 1389 //$(tooltip).setStyle('white-space: normal'); 1390 1391 if( options.animate ){ 1392 new Effect.Appear( $(tooltip), { duration: 0.3, queue: 'end' } ); 1393 } else { 1394 $(tooltip).show(); 1395 } 1396 }, 1397 hide = function(e){ 1398 var id = $(element).identify(); 1399 if( !$(id + '_tooltip') ){ return; } 1400 1401 if( options.animate ){ 1402 new Effect.Fade( $(id + '_tooltip'), { duration: 0.2, queue: 'end' }); 1403 } else { 1404 $(id + '_tooltip').hide(); 1405 } 1406 }; 1407 1408 $( element ).observe("mouseenter", function(e){ 1409 if( options.delay ){ 1410 options._still_going = true; 1411 show.delay( options.delay, e ); 1412 } else { 1413 show(e); 1414 } 1415 }). 1416 observe("click", function(e){ 1417 options._still_going = false;
1418 hide(); 1419 }). 1420 observe("mouseleave", function(e) 1421 { 1422 options._still_going = false; 1423 hide(); 1424 }); 1425 } 1426}); 1427 1428/************************************************/ 1429/* IPB3 Javascript */ 1430/* -------------------------------------------- */ 1431/* ips.global.js - Global functionality */ 1432/* (c) IPS, Inc 2008 */ 1433/* -------------------------------------------- */ 1434/* Author: Rikki Tissier */ 1435/************************************************/ 1436 1437var _global = window.IPBoard; 1438 1439_global.prototype.global = { 1440 searchTimer: [], 1441 searchLastQuery: '', 1442 rssItems: [], 1443 reputation: {}, 1444 popups: {}, 1445 ac_cache: $H(), 1446 pageJumps: $H(), 1447 pageJumpMenus: $H(), 1448 boardMarkers: $H(), 1449 searchResults: $H(), 1450 tidPopOpen: 0, 1451 activeTab: 'forums', 1452 userCards: null, 1453 inlineNotification: { timers: [] }, 1454 _supportsPlaceholder: null, 1455 1456 /*------------------------------*/ 1457 /* Constructor */ 1458 init: function() 1459 { 1460 Debug.write("Initializing ips.global.js"); 1461 1462 document.observe("dom:loaded", function(){ 1463 ipb.global.initEvents(); 1464 }); 1465 }, 1466 initEvents: function() 1467 { 1468 // Delegate our user popup links/warn logs 1469 ipb.delegate.register(".warn_link", ipb.global.displayWarnLogs); 1470 ipb.delegate.register(".mini_friend_toggle", ipb.global.toggleFriend); 1471 ipb.delegate.register(".__topic_preview", ipb.global.topicPreview); 1472 ipb.delegate.register('.bbc_spoiler_show', ipb.global.toggleSpoiler); 1473 ipb.delegate.register('a[rel~="external"]', ipb.global.openNewWindow ); 1474 ipb.delegate.register('._repLikeMore', ipb.global.repLikeMore); 1475 ipb.delegate.register('a[rel~="quickNavigation"]', ipb.global.openQuickNavigation ); 1476 1477 if( $('sign_in') && !$('sign_in').hasClassName('no_ajax') ){ 1478 $('sign_in').on('click', ipb.global.inlineSignin); 1479 } 1480 1481 if( $('rss_feed') ){ 1482 ipb.global.buildRSSmenu(); 1483 } 1484 1485 if ( ! Object.isUndefined( ipb.vars['notificationData'] ) ) 1486 { 1487 new ipb.Popup( 'navigation_popup', { type: 'modal', 1488 initial: ipb.templates['notificationTemplate'].evaluate( ipb.vars['notificationData'] ), 1489 hideAtStart: false, 1490 w: '600px', 1491 h: 250} ); 1492 } 1493 1494 /* This is needed for RTL - the generic anchoring isn't working on RTL for some reason */ 1495 if( $('backtotop') ){ 1496 $('backtotop').observe( "click", function(e){ Event.stop(e); window.scroll( 0, 0 ); } ); 1497 } 1498 1499 ipb.global.buildPageJumps(); 1500 ipb.global.initUserCards(); 1501 1502 /* Got an inline notification? */ 1503 if ( ! Object.isUndefined( ipb.templates['inlineMsg'] ) && ipb.templates['inlineMsg'] != '' ){ 1504 ipb.global.showInlineNotification( ipb.templates['inlineMsg'] ); 1505 } 1506 1507 // Contextual search 1508 if( $('search-box') ){ 1509 ipb.global.contextualSearch(); 1510 } 1511 1512 // Global menus 1513 if( $('user_link') ){ 1514 new ipb.Menu( $('user_link'), $('user_link_menucontent') ); 1515 } 1516 1517 if( $('new_skin') ){ 1518 new ipb.Menu( $('new_skin'), $('new_skin_menucontent') ); 1519 } 1520 1521 if( $('new_language') ){ 1522 new ipb.Menu( $('new_language'), $('new_language_menucontent') ); 1523 } 1524 1525 if( $('mark_all_read') ){ 1526 new ipb.Menu( $('mark_all_read'), $('mark_all_read_menucontent') ); 1527 } 1528 1529 // Tooltips 1530 $$("[data-tooltip]").invoke('tooltip'); 1531 1532 // General click handlers 1533 $$("[data-clicklaunch]").invoke('clickLaunch'); 1534 1535 // Confirm action used in delete, etc 1536 $$("[data-confirmaction]").invoke('confirmAction'); 1537 1538 // Status updates 1539 if( $('statusUpdateGlobal') ){ 1540 $('statusUpdateGlobal').defaultize( ipb.lang['global_status_update'] ); 1541 1542 $('statusSubmitGlobal').observe( 'click', ipb.global.statusUpdated ); 1543 } 1544 1545 ipb.global.parseQuoteBoxes(); 1546 ipb.global.removeLinkedLightbox(); 1547 1548 /* Attachments wrapped in URL @link http://community.invisionpower.com/tracker/issue-37113-linking-image-attachments-does-not-work/ */ 1549 $$('a.resized_img').each( function( elem ) 1550 { 1551 if ( $(elem).previous('a.bbc_url') ) 1552 { 1553 var test = $(elem).previous('a.bbc_url'); 1554 1555 if ( ! test.innerHTML.length ) 1556 { 1557 $(elem).writeAttribute( 'href', test.href ); 1558 $(elem).writeAttribute( 'rel', test.rel ); 1559 1560 /* Remove empty href */ 1561 test.remove(); 1562 } 1563 } 1564 } ); 1565
1566 /* Tag show more */ 1567 if ( ! Object.isUndefined( ipb.hoverCard ) && ipb.vars['is_touch'] === false ) 1568 { 1569 var ajaxUrl = ipb.vars['base_url'] + "app=core&module=ajax§ion=tags&do=getTagsAsPopUp&md5check="+ipb.vars['secure_hash']; 1570 1571 ipb.hoverCardRegister.initialize( 'tagsPopUp', { 'w': '500px', 'delay': 750, 'position': 'auto', 'ajaxUrl': ajaxUrl, 'getId': true, 'setIdParam': 'key' } ); 1572 } 1573 }, 1574 1575 /* Make quote boxes all nice */ 1576 parseQuoteBoxes: function() 1577 { 1578 $$('blockquote.ipsBlockquote').each( function( el ) 1579 { 1580 if ( ! $(el).hasClassName('built') ) 1581 { 1582 var author = ''; 1583 var cid = ''; 1584 var time = 0; 1585 var date = ''; 1586 var collapsed = 0; 1587 var _extra = ''; 1588 var _a = new Element('span'); 1589 1590 try { 1591 author = $(el).getAttribute( 'data-author' ) ? $(el).getAttribute( 'data-author' ).escapeHtml() : ''; 1592 cid = $(el).getAttribute( 'data-cid' ) ? $(el).getAttribute( 'data-cid' ).escapeHtml() : ''; 1593 time = $(el).getAttribute( 'data-time' ) ? $(el).getAttribute( 'data-time' ).escapeHtml() : 0; 1594 date = $(el).getAttribute( 'data-date' ) ? $(el).getAttribute( 'data-date' ).escapeHtml() : ''; 1595 collapsed = $(el).getAttribute( 'data-collapsed' ) ? $(el).getAttribute( 'data-collapsed' ).escapeHtml() : 0; 1596 } catch( aCold ) { } 1597 1598 if ( time ) 1599 { 1600 if ( time == parseInt( time ) && time.length == 10 ) 1601 { 1602 /* First, determine if we are in DST now */ 1603 var _tz = new Date().getTimezoneOffset() * 60; 1604 1605 /* Find out if timestamp was in DST */ 1606 var _date = new Date().getDateAndTime( parseInt( time ) ); 1607 1608 /* If there isn't a match, figure out the DST offset and then add it back in */ 1609 if( _date['dst'] * 60 != _tz ) 1610 { 1611 _tz = _tz - _date['dst'] * 60; 1612 _date = new Date().getDateAndTime( parseInt( time ) - parseInt( _tz ) ); 1613 } 1614 1615 var _ampm = ''; 1616 1617 if( ipb.vars['hour_format'] == "12" ) 1618 { 1619 if( _date['hour'] > 12 ) 1620 { 1621 _date['hour'] -= 12; 1622 _ampm = ' ' + ipb.lang['date_pm']; 1623 } 1624 else if( _date['hour'] == 12 ) 1625 { 1626 _ampm = ' ' + ipb.lang['date_pm']; 1627 } 1628 else if( _date['hour'] == 0 ) 1629 { 1630 _date['hour'] = 12; 1631 _ampm = ' ' + ipb.lang['date_am']; 1632 } 1633 else 1634 { 1635 _ampm = ' ' + ipb.lang['date_am']; 1636 } 1637 } 1638 1639 date = _date['date'] + ' ' + _date['monthName'] + ' ' + _date['year'] + ' - ' + _date['hour'] + ':' + _date['min'] + _ampm; 1640 } 1641 } 1642 1643 if ( author && date ) 1644 { 1645 _extra = ipb.lang['quote__date_author'].replace( /#name#/, author ).replace( /#date#/, date ); 1646 } 1647 else if ( author ) 1648 { 1649 _extra = ipb.lang['quote__author'].replace( /#name#/, author ); 1650 } 1651 1652 /* finally.. */ 1653 if ( _extra.length == 0 ) 1654 { 1655 _extra = ipb.lang['quote_title']; 1656 } 1657 1658 if ( cid && parseInt( cid ) == cid ) 1659 { 1660 _a = new Element( 'a', { 'class': 'snapback right', rel: 'citation', href: ipb.vars['board_url'] + '/index.php?app=forums&module=forums§ion=findpost&pid=' + cid } ); 1661 _a.update( new Element( 'img', { src: ipb.vars['img_url'] + '/snapback.png' } ) ); 1662 } 1663 1664 el.insert( { before: new Element( 'p', { 'class': 'citation' } ).update( _extra ).insert( _a ) } ); 1665 1666 try 1667 { 1668 el.down('cite').hide(); 1669 } 1670 catch(err){} 1671 1672 el.addClassName( 'built' ); 1673 1674 if ( collapsed ) 1675 { 1676 // Hide this element 1677 el.hide(); 1678 1679 // Create a new blockquote that will be our 'clickable' area 1680 var bq = new Element( 'blockquote', { 'class': 'ipsBlockquote built clickable' } ).update( ipb.lang['quote_expand'] ); 1681 1682 bq.on( 'click', function( e, elem ) 1683 { 1684 el.show(); 1685 bq.remove(); 1686 } ); 1687 1688 el.insert( { before: bq } ); 1689 } 1690 } 1691 } ); 1692 }, 1693 1694 /* Remove lightbox if wrapped in a link */ 1695 removeLinkedLightbox: function() 1696 { 1697 $$('a.bbc_url').each( function( el ) 1698 { 1699 if ( _thislightbox = $(el).down('span[rel~=lightbox]') ) 1700 { 1701 var contents = $(el).innerHTML.replace('<span rel="lightbox">', ''); 1702 contents = contents.replace('</span>', ''); 1703 1704 $(el).innerHTML = contents; 1705 } 1706 } ); 1707 }, 1708 1709 /* Lightbox has been disabled */ 1710 lightBoxIsOff: function() 1711 { 1712 $$('span[rel*="lightbox"]').each( function( elem ) 1713 { 1714 if ( ! $(elem).down('a') ) 1715 { 1716 $(elem).down('img').on( 'click', function(e, el) { window.open(el.src); } ); 1717 } 1718 } ); 1719 }, 1720 1721 /* Stores default values of status share checkboxes */ 1722 saveSocialShareDefaults: function( elem, e ) 1723 { 1724 var services = {}; 1725 1726 /* Gather elements */ 1727 $$('._share_x_').each( function(elem){ 1728 services[ elem.id.replace(/share_x_/, '' ) ] = ( elem.checked ) ? 1 : 0; 1729 } ); 1730
1731 new Ajax.Request( ipb.vars['base_url'] + "app=core§ion=sharelinks&module=ajax&do=savePostPrefs&md5check=" + ipb.vars['secure_hash'], 1732 { 1733 method: 'post', 1734 evalJSON: 'force', 1735 parameters: services, 1736 onSuccess: function(t) 1737 { 1738 if ( Object.isUndefined( t.responseJSON ) ) 1739 { 1740 alert( ipb.lang['action_failed'] ); 1741 return; 1742 } 1743 1744 if ( ! Object.isUndefined( t.responseJSON['error'] ) ) 1745 { 1746 alert( t.responseJSON['error'] ); 1747 } 1748 else 1749 { 1750 /* Nothing to do */ 1751 } 1752 } 1753 }); 1754 }, 1755 1756 /*!! statusUpdated */ 1757 /* Updates a status where ever you are on the page. Differs from the ipb.status version slightly */ 1758 statusUpdated: function(e) 1759 { 1760 Event.stop(e); 1761 1762 if ( $('statusUpdateGlobal' ).value.length < 2 || $('statusUpdateGlobal').value == ipb.lang['prof_update_default'] ) 1763 { 1764 return false; 1765 } 1766 1767 // Bug #34650: Commented this out so that board index updates 1768 // use the ajax function defined below instead of the main status function 1769 /* Main status library loaded? */ 1770 /*if ( ! Object.isUndefined( ipb.status ) ) 1771 { 1772 return ipb.status.updateSubmit( e, 'statusUpdateGlobal' ); 1773 }*/ 1774 1775 var su_Twitter = $('su_TwitterGlobal') && $('su_TwitterGlobal').checked ? 1 : 0; 1776 var su_Facebook = $('su_FacebookGlobal') && $('su_FacebookGlobal').checked ? 1 : 0; 1777 1778 var skin_group = ( $('statusHook') ) ? 'boards' : 'profile'; 1779 1780 new Ajax.Request( ipb.vars['base_url'] + "app=members§ion=status&module=ajax&do=new&md5check=" + ipb.vars['secure_hash'] + "&skin_group=" + skin_group + "&return=json&smallSpace=1", 1781 { 1782 method: 'post', 1783 evalJSON: 'force', 1784 parameters: { 1785 content: $('statusUpdateGlobal' ).value.encodeParam(), 1786 su_Twitter: su_Twitter, 1787 su_Facebook: su_Facebook 1788 }, 1789 onSuccess: function(t) 1790 { 1791 if( Object.isUndefined( t.responseJSON ) ) 1792 { 1793 alert( ipb.lang['action_failed'] ); 1794 return; 1795 } 1796 1797 if ( t.responseJSON['error'] ) 1798 { 1799 alert( t.responseJSON['error'] ); 1800 } 1801 else 1802 { 1803 try { 1804 if ( $('status_wrapper') ) 1805 { 1806 var memberId = 0; 1807 1808 try 1809 { 1810 memberId = $('status_wrapper').readAttribute('data-member'); 1811 } 1812 catch(err){ } 1813 1814 if ( ! memberId || ( memberId == ipb.vars['member_id'] ) ) 1815 { 1816 $('status_wrapper').innerHTML = t.responseJSON['html'] + $('status_wrapper').innerHTML; 1817 1818 /* Showing latest only? */ 1819 if ( ipb.status.myLatest ) 1820 { 1821 if ( $('statusWrap-' + ipb.status.myLatest ) ) 1822 { 1823 $('statusWrap-' + ipb.status.myLatest ).hide(); 1824 } 1825 } 1826 } 1827 } 1828 1829 ipb.menus.closeAll(e,true); 1830 ipb.global.showInlineNotification( ipb.lang['status_updated'] ); 1831 1832 $('statusUpdateGlobal').value = ''; 1833 $('statusUpdateGlobal').defaultize( ipb.lang['global_status_update'] ); 1834 } 1835 catch(err) 1836 { 1837 Debug.error( 'Logging error: ' + err ); 1838 } 1839 } 1840 } 1841 }); 1842 }, 1843 1844 /** 1845 * Changes the user's skin 1846 */ 1847 changeSkin: function(element, e) 1848 { 1849 Debug.dir( element ); 1850 var skinId = $(element).readAttribute('data-skinid'); 1851 1852 var url = ipb.vars['base_url'] + 'app=core&module=ajax§ion=skin&do=change&skinId=' + skinId + '&secure_key=' + ipb.vars['secure_hash']; 1853 Debug.write( url ); 1854 new Ajax.Request( url, 1855 { 1856 method: 'get', 1857 onSuccess: function(t) 1858 { 1859 /* 1860 * Get an error? 1861 */ 1862 if( t.responseJSON['status'] == 'ok' ) 1863 { 1864 window.location = window.location; 1865 window.location.reload(true); 1866 } 1867 else 1868 { 1869 ipb.global.errorDialogue( ipb.lang['ajax_failure'] ); 1870 } 1871 } 1872 } 1873 ); 1874 1875 1876 Event.stop(e); 1877 return false; 1878 }, 1879 1880 /** 1881 * Displays the inbox drop down in header 1882 */ 1883 getInboxList: function(element, e) 1884 { 1885 /* 1886 * Only run AJAX call once. Cache and use the cache for subsequent requests. 1887 */ 1888 if ( Object.isUndefined( ipb.global.popups['inbox'] ) ) 1889 { 1890 ipb.global.popups['inbox'] = true; 1891 ipb.menus.closeAll(e); 1892 1893 $(element).identify(); 1894 1895 /* Create pop-up wrapper */ 1896 $(element).addClassName('ipbmenu'); 1897 1898 $('ipboard_body').insert( ipb.templates['header_menu'].evaluate( { id: 'user_inbox_link_menucontent' } ) ); 1899 1900 $('user_inbox_link_menucontent').setStyle('width: 300px').update( "<div class='ipsPad ipsForm_center'><img src='" + ipb.vars['loading_img'] + "' /></div>" ); 1901 1902 var _newMenu = new ipb.Menu( $(element), $( "user_inbox_link_menucontent" ) ); 1903 _newMenu.doOpen(); 1904 1905 var url = ipb.vars['base_url'] + 'app=members&module=ajax§ion=messenger&do=getInboxDropDown'; 1906 Debug.write( url ); 1907 new Ajax.Request( url, 1908 { 1909 method: 'post', 1910 evalJSON: 'force', 1911 hideLoader: true, 1912 parameters: { 1913 secure_key: ipb.vars['secure_hash'] 1914 }, 1915 onSuccess: function(t) 1916 { 1917 /* 1918 * Get an error? 1919 */ 1920 if( t.responseJSON['error'] ) 1921 { 1922 if ( t.responseJSON['__board_offline__'] ) 1923 { 1924 ipb.global.errorDialogue( ipb.lang['board_offline'] ); 1925 ipb.menus.closeAll(e); 1926 } 1927 } 1928 else 1929 { 1930 $('user_inbox_link_menucontent').update( t.responseJSON['html'] ); 1931 1932 /* Clear counter */ 1933 try 1934 { 1935 $(element).down('.ipsHasNotifications').fade( { afterFinish: function() { $(element).down('.ipsHasNotifications').show().addClassName('ipsHasNotifications_blank'); } } ); 1936 } catch( acold ) { } 1937 } 1938 } 1939 } 1940 ); 1941 } 1942 1943 Event.stop(e); 1944 return false;
1945 }, 1946 1947 /** 1948 * Displays the notifications drop down in header 1949 */ 1950 getNotificationsList: function(element, e) 1951 { 1952 Event.stop(e); 1953 1954 /* 1955 * Only run AJAX call once. Cache and use the cache for subsequent requests. 1956 */ 1957 if ( Object.isUndefined( ipb.global.popups['notification'] ) ) 1958 { 1959 ipb.global.popups['notification'] = true; 1960 1961 ipb.menus.closeAll(e); 1962 1963 $(element).identify(); 1964 1965 /* Create pop-up wrapper */ 1966 $(element).addClassName('ipbmenu'); 1967 1968 $('ipboard_body').insert( ipb.templates['header_menu'].evaluate( { id: 'user_notifications_link_menucontent' } ) ); 1969 1970 $('user_notifications_link_menucontent').setStyle('width: 300px').update( "<div class='ipsPad ipsForm_center'><img src='" + ipb.vars['loading_img'] + "' /></div>" ); 1971 1972 var _newMenu = new ipb.Menu( $(element), $( "user_notifications_link_menucontent" ) ); 1973 _newMenu.doOpen(); 1974 1975 var url = ipb.vars['base_url'] + 'app=core&module=ajax§ion=notifications&do=getlatest'; 1976 Debug.write( url ); 1977 1978 new Ajax.Request( url, 1979 { 1980 method: 'post', 1981 evalJSON: 'force', 1982 hideLoader: true, 1983 parameters: { 1984 secure_key: ipb.vars['secure_hash'] 1985 }, 1986 onSuccess: function(t) 1987 { 1988 /* 1989 * Get an error? 1990 */ 1991 if( t.responseJSON['error'] ) 1992 { 1993 if ( t.responseJSON['__board_offline__'] ) 1994 { 1995 ipb.global.errorDialogue( ipb.lang['board_offline'] ); 1996 ipb.menus.closeAll(e); 1997 } 1998 } 1999 else 2000 { 2001 $('user_notifications_link_menucontent').update( t.responseJSON['html'] ); 2002 2003 /* Clear counter */ 2004 try 2005 { 2006 $(element).down('.ipsHasNotifications').fade( { afterFinish: function() { $(element).down('.ipsHasNotifications').show().addClassName('ipsHasNotifications_blank'); } } ); 2007 } catch( acold ) { } 2008 } 2009 } 2010 } 2011 ); 2012 } 2013 2014 2015 return false; 2016 }, 2017 2018 2019 openQuickNavigation: function( e ) 2020 { 2021 Event.stop(e); 2022 2023 if( ipb.global.popups['quickNav'] ){ 2024 ipb.global.popups['quickNav'].show(); 2025 } else { 2026 var url = ipb.vars['base_url'] + "app=core&module=ajax§ion=navigation&secure_key=" + ipb.vars['secure_hash'] + "&inapp=" + ipb.vars['active_app']; 2027 ipb.global.popups['quickNav'] = new ipb.Popup( 'navigation_popup', { type: 'modal', 2028 ajaxURL: url, 2029 hideAtStart: false, 2030 w: '600px', 2031 h: 460 } ); 2032 2033 2034 /* delegate */ 2035 ipb.delegate.register('a[rel~="ipsQuickNav"]', ipb.global.quickNavTabClick ); 2036 } 2037 2038 return false; 2039 }, 2040 2041 launchPhotoEditor: function( elem, e ) 2042 { 2043 Event.stop(e); 2044 2045 if ( ! Object.isUndefined( ipb.global.popups['photoEditor'] ) ) 2046 { 2047 ipb.global.popups['photoEditor'].kill(); 2048 } 2049 2050 var url = ipb.vars['base_url'] + "&app=members&module=ajax§ion=photo&do=show&secure_key=" + ipb.vars['secure_hash']; 2051 ipb.global.popups['photoEditor'] = new ipb.Popup( 'photo_popup', { type: 'pane', 2052 modal: true, 2053 ajaxURL: url, 2054 hideAtStart: false, 2055 evalJs: 'force', 2056 w: '750px', 2057 h: 500 2058 } ); 2059 return false; 2060 }, 2061 2062 quickNavTabClick: function( e, elem ) 2063 { 2064 Event.stop(e); 2065 app = elem.readAttribute( 'data-app' ); 2066 2067 var url = ipb.vars['base_url'] + "app=core&module=ajax§ion=navigation&secure_key=" + ipb.vars['secure_hash'] + "&do=panel&inapp=" + app; 2068 2069 new Ajax.Request( url.replace(/&/g, '&'), 2070 { 2071 method: 'get', 2072 evalJSON: 'force', 2073 hideLoader: true, 2074 onSuccess: function(t) 2075 { 2076 $('ipsNav_content').update( t.responseText ); 2077 2078 $$('a[rel~="ipsQuickNav"]').each( function(link) 2079 { 2080 link.up('li').removeClassName('active'); 2081 var _app = link.readAttribute( 'data-app' ); 2082 2083 if ( _app == app ) 2084 { 2085 link.up('li').addClassName('active'); 2086 } 2087 } ); 2088 } 2089 }); 2090 2091 return false; 2092 }, 2093 2094 2095 ajaxPagination: function( element, url ) 2096 { 2097 new Ajax.Request( url.replace(/&/g, '&'), 2098 { 2099 method: 'get', 2100 evalJSON: 'force', 2101 hideLoader: true, 2102 onSuccess: function(t) 2103 { 2104 $(element).update( t.responseText ); 2105 } 2106 }); 2107 2108 return false;
2109 }, 2110 2111 inlineSignin: function( e ) 2112 { 2113 if( ipb.vars['is_touch'] ){ // Just go to normal form for touch devices 2114 return; 2115 } 2116 2117 /* If we don't have the template bit.. */ 2118 if ( ! $('inline_login_form') ) 2119 { 2120 return; 2121 } 2122 2123 Event.stop(e); 2124 2125 if( ipb.global.loginRedirect ) 2126 { 2127 window.location = ipb.global.loginRedirect; 2128 return; 2129 } 2130 2131 new ipb.Popup( 'sign_in_popup', { type: 'pane', 2132 initial: $('inline_login_form').show(), 2133 hideAtStart: false, 2134 hideClose: false, 2135 defer: false, 2136 modal: true, 2137 w: '600px' }, 2138 { 2139 afterShow: function(pop){ 2140 try { 2141 $('ips_username').focus(); 2142 } catch(err){} 2143 } 2144 }); 2145 }, 2146 2147 forumMarkRead: function(elem, e) 2148 { 2149 Event.stop(e); 2150 2151 var id = $(elem).readAttribute("data-fid"); 2152 2153 if( !id ){ return; } 2154 2155 var url = ipb.vars['base_url'] + '&app=forums&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=markasread&forumid=' + id; 2156 2157 // Send AJAX request 2158 new Ajax.Request( url, 2159 { 2160 method: 'get', 2161 evalJSON: 'force', 2162 onSuccess: function(t) 2163 { 2164 if( t.responseText == 'no_forum' || t.responseText == 'no_permission' ){ 2165 alert( ipb.lang['mark_read_forum'] ); 2166 return; 2167 } 2168 2169 /* Remove elements */ 2170 $$('.__topic').each( function( topic ) 2171 { 2172 if ( $(topic).hasClassName('unread') ) 2173 { 2174 var tid = $(topic).readAttribute("data-tid"); 2175 2176 if ( tid ) 2177 { 2178 ipb.global.topicRemoveUnreadElements( tid ); 2179 } 2180 } 2181 } ); 2182 } 2183 }); 2184 2185 }, 2186 2187 topicMarkRead: function(elem, e) 2188 { 2189 Event.stop(e); 2190 2191 var id = $(elem).readAttribute("data-tid"); 2192 2193 if( !id ){ return; } 2194 2195 var row = $('trow_'+id); 2196 var url = ipb.vars['base_url'] + '&app=forums&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=topics&do=markRead&tid=' + id; 2197 2198 // Send AJAX request 2199 new Ajax.Request( url, 2200 { 2201 method: 'get', 2202 evalJSON: 'force', 2203 onSuccess: function(t) 2204 { 2205 if( t.responseText == 'no_topic' || t.responseText == 'no_permission' ){ 2206 alert( ipb.lang['mark_read_topic'] ); 2207 return; 2208 } 2209 2210 /* Remove mark as read link */ 2211 $(elem).remove(); 2212 2213 /* Close preview */ 2214 ipb.global.topicPreview(e, row.down('.__topic_preview') ); 2215 2216 /* Remove elements */ 2217 ipb.global.topicRemoveUnreadElements( id ); 2218 } 2219 }); 2220 2221 }, 2222 2223 topicRemoveUnreadElements: function( tid ) 2224 { 2225 $('trow_' + tid).removeClassName('unread').down('.col_f_icon').select('a img').invoke('remove'); 2226 }, 2227 2228 topicPreview: function(e, elem) 2229 { 2230 Event.stop(e); 2231 2232 var toggle = $(elem).down(".expander"); 2233 var row = $(elem).up(".__topic"); 2234 var id = $(row).readAttribute("data-tid"); 2235 2236 if( !id ){ return; } 2237 2238 // Stop multiple loads 2239 if( $(row).readAttribute('loadingPreview') == 'yes' ){ 2240 return; // Just be patient! 2241 } 2242 $( row ).writeAttribute('loadingPreview', 'yes'); 2243 2244 if( $("topic_preview_" + id) ) 2245 { 2246 if( $("topic_preview_wrap_" + id).visible() ) 2247 { 2248 new Effect.BlindUp( $("topic_preview_wrap_" + id), { duration: 0.3, afterFinish: function(){ $('topic_preview_' + id).hide(); } } ); 2249 row.removeClassName('highlighted'); 2250 $( toggle ).addClassName('closed').removeClassName('loading').removeClassName('open').writeAttribute('title', ipb.lang['open_tpreview']); 2251 } 2252 else 2253 { 2254 $('topic_preview_' + id).show(); 2255 new Effect.BlindDown( $("topic_preview_wrap_" + id), { duration: 0.3 } ); 2256 row.addClassName('highlighted'); 2257 $( toggle ).addClassName('open').removeClassName('loading').removeClassName('closed').writeAttribute('title', ipb.lang['close_tpreview']); 2258 } 2259 2260 $(row).writeAttribute('loadingPreview', 'no'); 2261 } 2262 else 2263 { 2264 var url = ipb.vars['base_url'] + '&app=forums&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=topics&do=preview&tid=' + id; 2265 if ( ipb.global.searchResults[ id ] ){ 2266 url += '&pid=' + ipb.global.searchResults[ id ]['pid'] + '&searchTerm=' + ipb.global.searchResults[ id ]['searchterm']; 2267 } 2268 2269 $( toggle ).addClassName('loading').removeClassName('closed').removeClassName('open'); 2270 2271 // Send AJAX request 2272 new Ajax.Request( url, 2273 { 2274 method: 'get', 2275 evalJSON: 'force', 2276 onSuccess: function(t) 2277 { 2278 if( t.responseText == 'no_topic' || t.responseText == 'no_permission' ){ 2279 alert( ipb.lang['no_permission_preview'] ); 2280 $( toggle ).addClassName('open').removeClassName('loading').removeClassName('closed').writeAttribute('title', ipb.lang['close_tpreview']); 2281 2282 $(row).writeAttribute('loadingPreview', 'no'); 2283 return; 2284 } 2285 2286 if( row.tagName == "TR" ) 2287 { 2288 var count = row.childElements().size(); 2289 var newrow = new Element('tr', { 'class': 'preview', 'id': 'topic_preview_' + id }); 2290 var newcell = new Element('td', { 'colspan': count } ); 2291 var wrap = new Element('div', { 'id': 'topic_preview_wrap_' + id }).hide().update( new Element('div' ) ); 2292 2293 // Put all the bits inside each other 2294 row.insert( { after: newrow.insert( newcell.insert( wrap ) ) } ); 2295 } 2296 else 2297 { 2298 var wrap = new Element('div', { 'id': 'topic_preview_wrap_' + id }).hide().update( new Element('div') ); 2299 row.insert( { after: wrap } ); 2300 } 2301 2302 // Insert content 2303 wrap.update( t.responseText ).relativize(); 2304 // display it 2305 new Effect.BlindDown( wrap, { duration: 0.3 } ); 2306 // Update row & elem 2307 row.addClassName('highlighted'); 2308 $( toggle ).addClassName('open').removeClassName('loading').removeClassName('closed').writeAttribute('title', ipb.lang['close_tpreview']); 2309 2310 $(row).writeAttribute('loadingPreview', 'no'); 2311 } 2312 }); 2313 } 2314 }, 2315 2316 // Set up the main menu 2317 activateMainMenu: function() 2318 { 2319 if( $("nav_other_apps") && $("community_app_menu") ){ 2320 /* Image is 0 width inside hidden div, so we have to compensate manually...hardcoded for our useropts image 17px + 3px margin */ 2321 /* Note that we are grabbing width of more menu tab, instead of 'margin-box-width' which accounts for margin. Cannot grab this 2322 value while the tab is hidden and we don't want to loop a second time unnecessarily to readjust after showing the menu */ 2323 var start = totalW = $("nav_other_apps").getWidth() + 20; 2324 var menuWidth = $("community_app_menu").getWidth(); 2325 2326 /* Add up the widths of any menu items we won't be moving to the more menu */ 2327 $("community_app_menu").select("li.skip_moremenu").each( function(elem){ 2328 totalW += $(elem).measure('margin-box-width'); 2329 }); 2330 2331 $("community_app_menu").select("li:not(#nav_other_apps,.submenu_li)").each( function(elem){ 2332 /* These tabs should not be moved to the more menu */ 2333 if( $(elem).hasClassName('skip_moremenu') ) 2334 { 2335 return; 2336 } 2337 2338 totalW += $(elem).measure('margin-box-width'); 2339 2340 if( totalW >= menuWidth ) 2341 { 2342 if( !$("more_apps_menucontent") ){ 2343 $$("body")[0].insert("<div id='more_apps_menucontent' class='submenu_container clearfix boxShadow'><div class='left'><ul class='submenu_links' id='more_apps_menucontentul'></ul></div></div>"); 2344 } 2345 2346 $(elem).addClassName('submenu_li').removeClassName('left'); 2347 2348 $("more_apps_menucontentul").insert( elem ); // Move item to menu 2349 } 2350 }); 2351 2352 // Do we have an app menu? 2353 if( $("more_apps_menucontent" ) ) 2354 { 2355 $("nav_other_apps").show(); 2356 new ipb.Menu( $('more_apps'), $('more_apps_menucontent') ); 2357 } 2358 2359 Debug.write( menuWidth ); 2360 } 2361 }, 2362 2363 /** 2364 * Init user cards 2365 */ 2366 initUserCards: function() 2367 { 2368 /* User cards */ 2369 if ( ! Object.isUndefined( ipb.hoverCard ) && ipb.vars['is_touch'] === false && ipb.vars['member_group']['g_mem_info']
2369== 1 ) 2370 { 2371 var ajaxUrl = ipb.vars['base_url'] + '&app=members&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=card'; 2372 if ( ipb.topic !== undefined && ipb.topic.forum_id !== undefined ) 2373 { 2374 ajaxUrl += "&f=" + ipb.topic.forum_id; 2375 } 2376 2377 ipb.hoverCardRegister.initialize( 'member', { 'w': '500px', 'delay': 750, 'position': 'auto', 'ajaxUrl': ajaxUrl, 'getId': true, 'setIdParam': 'mid' } ); 2378 } 2379 }, 2380 2381 /** 2382 * Display an inline notification 2383 * 2384 */ 2385 showInlineNotification: function( content, options ) 2386 { 2387 /* Fix options */ 2388 options = ( Object.isUndefined( options ) ) ? {} : options; 2389 options.showClose = ( Object.isUndefined( options.manualClose ) ) ? false : options.showClose; 2390 options.neverClose = ( Object.isUndefined( options.neverClose ) ) ? false : options.neverClose; 2391 options.displayForSeconds = ( Object.isUndefined( options.displayForSeconds ) ) ? 5 : options.displayForSeconds; 2392 2393 // If there's already one open, close it, 2394 // and add a callback to try again in when its done. 2395 if ( $('ipsGlobalNotification' ) ) 2396 { 2397 ipb.global.closeInlineNotification( function(){ 2398 ipb.global.showInlineNotification( content, options ); 2399 }); 2400 2401 return; 2402 } 2403 else 2404 { 2405 if( $('ipbwrapper') ) 2406 { 2407 /* Front end */ 2408 $('ipbwrapper').insert( new Element('div', { id: 'ipsGlobalNotification' } ).update( ipb.templates['global_notify'].evaluate( { 'message': content } ) ) ); 2409 } 2410 else 2411 { 2412 /* ACP */ 2413 $('ipboard_body').insert( new Element('div', { id: 'ipsGlobalNotification' } ).update( ipb.templates['global_notify'].evaluate( { 'message': content, 'close': ipb.templates['global_notify_close'] } ) ) ); 2414 } 2415 2416 /* Add in content */ 2417 new Effect.Appear( 'ipsGlobalNotification', { duration: 1.5 } ); 2418 2419 if ( options.showClose ) 2420 { 2421 $('ipsGlobalNotification').insert( new Element( 'div', { id: 'ipsGlobalNotification_close' } ) ); 2422 $('ipsGlobalNotification_close').observe('click', ipb.global.closeInlineNotification ); 2423 } 2424 else if( $('ipsGlobalNotification_close') ) 2425 { 2426 $('ipsGlobalNotification_close').observe('click', ipb.global.closeInlineNotification ); 2427 } 2428 } 2429 2430 /* Listen on any a hrefs */ 2431 $('ipsGlobalNotification').on('click', 'span a', ipb.global.closeInlineNotification); 2432 2433 /* Close */ 2434 if ( options.neverClose !== true ) 2435 { 2436 try { 2437 clearTimeout( ipb.global.inlineNotification['timers']['close'] ); 2438 } 2439 catch(e) {} 2440 2441 ipb.global.inlineNotification['timers']['close'] = setTimeout( ipb.global.closeInlineNotification, options.displayForSeconds * 1000 ); 2442 } 2443 }, 2444 2445 /** 2446 * Closes a notification 2447 */ 2448 closeInlineNotification: function( callback ) 2449 { 2450 callback = callback || Prototype.emptyFunction; 2451 2452 if ( $('ipsGlobalNotification_close') ){ 2453 $('ipsGlobalNotification_close').stopObserving('click'); 2454 } 2455 2456 try { 2457 clearTimeout( ipb.global.inlineNotification['timers']['close'] ); 2458 } 2459 catch(e) {} 2460 2461 new Effect.Fade( 'ipsGlobalNotification', { duration: 1.0, afterFinish: function(){ 2462 $('ipsGlobalNotification').remove(); 2463 callback(); 2464 } } ); 2465 }, 2466 2467 /** 2468 * Show an error dialogue 2469 * @param string 2470 * @returns Nothing 2471 */ 2472 errorDialogue: function( text ) 2473 { 2474 errContent = "<h3>" + ipb.lang['error_occured'] + "</h3><div class='row2 ipsPad ipsForm_center'><p>" + text + "</p></div>"; 2475 2476 new ipb.Popup( 'generic__errorDialogue', { type: 'pane', 2477 initial: errContent, 2478 stem: true, 2479 hideAtStart: false, 2480 hideClose: false, 2481 defer: false, 2482 warning: false, 2483 w: 400 } ); 2484 }, 2485 2486 /** 2487 * Show an OK dialogue 2488 * @param string 2489 * @returns Nothing 2490 */ 2491 okDialogue: function( text ) 2492 { 2493 okContent = "<h3>" + ipb.lang['success'] + "</h3><div class='row2 ipsPad ipsForm_center'><p>" + text + "</p></div>"; 2494 2495 new ipb.Popup( 'generic__okDialogue', { type: 'pane', 2496 initial: okContent, 2497 stem: true, 2498 hideAtStart: false, 2499 hideClose: false, 2500 defer: false, 2501 w: 400 } ); 2502 }, 2503 2504 contextualSearch: function() 2505 { 2506 if( !$('search_options') && !$('search_options_menucontent') ){ return; } 2507 2508 if ( ! $('main_search') ) 2509 { 2510 return; 2511 } 2512 2513 $('main_search').defaultize( ipb.lang['search_default_value'] ); 2514 2515 // This removes the text for IE7 2516 $('search').select('.submit_input').find( function(elem){ $(elem).value = ''; } ); 2517
2518 var update = function( noSelect ) 2519 { 2520 var checked = $('search_options_menucontent').select('input').find( function(elem){ return $(elem).checked; } ); 2521 if( Object.isUndefined( checked ) ){ 2522 checked = $('search_options_menucontent').select('input:first')[0]; 2523 if( !checked ){ return; } 2524 checked.checked = true; 2525 } 2526 $('search_options').show().update( $( checked ).up('label').readAttribute('title') || '' ); 2527 2528 // Put cursor in search box 2529 if( noSelect != true ){ 2530 $('main_search').focus(); 2531 } 2532 2533 return true; 2534 }; 2535 update(true); 2536 2537 $('search_options_menucontent').select('input').invoke('observe', 'click', update); 2538 }, 2539 2540 fetchTid: function( e ) 2541 { 2542 var elem = Event.element(e); 2543 elem.identify(); 2544 2545 if( !elem.hasClassName('__topic') ) 2546 { 2547 elem = elem.up('.__topic'); 2548 } 2549 2550 var id = elem.id; 2551 2552 if ( !id || ! $(id) ) 2553 { 2554 return 0; 2555 } 2556 2557 var m = $(id).className.match('__tid([0-9]+)'); 2558 var tid = m[1]; 2559 2560 return tid; 2561 }, 2562 2563 displayWarnLogs: function( e, elem ) 2564 { 2565 mid = elem.id.match('warn_link_([0-9a-z]+)_([0-9]+)')[2]; 2566 if( Object.isUndefined(mid) ){ return; } 2567 2568 if( parseInt(mid) == 0 ){ 2569 return false; 2570 } 2571 2572 Event.stop(e); 2573 2574 var _url = ipb.vars['base_url'] + '&app=core&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=warn&do=view&mid=' + mid; 2575 warnLogs = new ipb.Popup( 'warnLogs', {type: 'pane', modal: false, w: '500px', h: 500, ajaxURL: _url, hideAtStart: false, close: '.cancel' } ); 2576 2577 }, 2578 2579 /* ------------------------------ */ 2580 /** 2581 * Toggle mini friend button 2582 * 2583 * @param {event} e The event 2584 * @param {int} id Member id 2585 */ 2586 toggleFriend: function(e, elem) 2587 { 2588 Event.stop(e); 2589 2590 // Get ID of friend 2591 var id = $( elem ).id.match('friend_(.*)_([0-9]+)'); 2592 if( Object.isUndefined( id[2] ) ){ return; } 2593 2594 var isFriend = ( $(elem).hasClassName('is_friend') ) ? 1 : 0; 2595 var urlBit = ( isFriend ) ? 'remove' : 'add'; 2596 2597 var url = ipb.vars['base_url'] + "app=members§ion=friends&module=ajax&do=" + urlBit + "&member_id=" + id[2] + "&md5check=" + ipb.vars['secure_hash']; 2598 2599 // Send 2600 new Ajax.Request( url, 2601 { 2602 method: 'get', 2603 onSuccess: function(t) 2604 { 2605 switch( t.responseText ) 2606 { 2607 case 'pp_friend_timeflood': 2608 alert( ipb.lang['cannot_readd_friend'] ); 2609 Event.stop(e); 2610 break; 2611 case "pp_friend_already": 2612 alert( ipb.lang['friend_already'] ); 2613 Event.stop(e); 2614 break; 2615 case "error": 2616 return true; 2617 break; 2618 default: 2619 2620 var newIcon = ( isFriend ) ? ipb.templates['m_add_friend'].evaluate({ id: id[2]}) : ipb.templates['m_rem_friend'].evaluate({ id: id[2] }); 2621 2622 // Find all friend links for this user 2623 var friends = $$('.mini_friend_toggle').each( function( fr ){ 2624 if( $(fr).id.endsWith('_' + id[2] ) ) 2625 { 2626 if ( isFriend ) { 2627 $(fr).removeClassName('is_friend').addClassName('is_not_friend').update( newIcon ); 2628 } else { 2629 $(fr).removeClassName('is_not_friend').addClassName('is_friend').update( newIcon ); 2630 } 2631 } 2632 }); 2633 2634 new Effect.Highlight( $( elem ), { startcolor: ipb.vars['highlight_color'] } ); 2635 2636 // Fire an event so we can update if necessary 2637 document.fire('ipb:friendRemoved', { friendID: id[2] } ); 2638 Event.stop(e); 2639 break; 2640 } 2641 } 2642 } 2643 ); 2644 }, 2645 2646 /** 2647 * MATT 2648 * Toggle spammer 2649 */ 2650 toggleFlagSpammer: function( memberId, flagStatus ) 2651 { 2652 if ( flagStatus == true ) 2653 { 2654 if( confirm( ipb.lang['set_as_spammer'] ) ) 2655 { 2656 var tid = 0; 2657 var fid = 0; 2658 var sid = 0; 2659 2660 if( typeof(ipb.topic) != 'undefined' ) 2661 { 2662 tid = ipb.topic.topic_id; 2663 fid = ipb.topic.forum_id; 2664 sid = ipb.topic.start_id; 2665 } 2666 2667 window.location = ipb.vars['base_url'] + 'app=core&module=modcp&do=setAsSpammer&member_id=' + memberId + '&t=' + tid + '&f=' + fid + '&st=' + sid + '&auth_key=' + ipb.vars['secure_hash']; 2668 return false;
2669 } 2670 else 2671 { 2672 return false; 2673 } 2674 } 2675 else 2676 { 2677 alert( ipb.lang['is_spammer'] ); 2678 return false; 2679 } 2680 }, 2681 2682 /* ------------------------------ */ 2683 /** 2684 * Toggle spoiler 2685 * 2686 * @param {event} e The event 2687 */ 2688 toggleSpoiler: function(e, button) 2689 { 2690 Event.stop(e); 2691 2692 var returnvalue = $(button).up('.bbc_spoiler').down('.bbc_spoiler_wrapper').down('.bbc_spoiler_content').toggle(); 2693 2694 if( returnvalue.visible() ){ 2695 $(button).value = ipb.lang['spoiler_hide']; 2696 } else { 2697 $(button).value = ipb.lang['spoiler_show']; 2698 } 2699 }, 2700 2701 /* ------------------------------ */ 2702 /** 2703 * Builds the popup menu for RSS feeds 2704 */ 2705 buildRSSmenu: function() 2706 { 2707 // Get all link tags 2708 $$('link').each( function(link) 2709 { 2710 if( link.readAttribute('type') == "application/rss+xml" ) 2711 { 2712 ipb.global.rssItems.push( ipb.templates['rss_item'].evaluate( { url: link.readAttribute('href'), title: link.readAttribute('title').escapeHtml() } ) ); 2713 } 2714 }); 2715 2716 if( ipb.global.rssItems.length > 0 ) 2717 { 2718 rssmenu = ipb.templates['rss_shell'].evaluate( { items: ipb.global.rssItems.join("\n") } ); 2719 $( 'rss_feed' ).insert( { after: rssmenu } ); 2720 new ipb.Menu( $( 'rss_feed' ), $( 'rss_menu' ) ); 2721 } 2722 else 2723 { 2724 $('rss_feed').hide(); 2725 } 2726 }, 2727 2728 /* ------------------------------ */ 2729 /** 2730 * Reputation Popup Balloon 2731 */ 2732 repPopUp: function( e, repId, repApp, repType ) 2733 { 2734 if( ipb.global.popups['rep_' + repId] ){ 2735 ipb.global.popups['rep_' + repId].kill(); 2736 } 2737 2738 var _url = ipb.vars['base_url'] + '&app=core&module=ajax&secure_key=' + ipb.vars['secure_hash'] + '§ion=reputation&do=view&repApp=' + repApp + '&repType=' + repType + '&repId=' + repId; 2739 ipb.global.popups['rep_' + repId] = new ipb.Popup('rep_' + repId, { 2740 type: 'balloon', 2741 stem: true, 2742 attach: { target: e, position: 'auto' }, 2743 hideAtStart: false, 2744 ajaxURL: _url, 2745 w: '300px', 2746 h: 400 2747 }); 2748 }, 2749 2750 /* ------------------------------ */ 2751 /** 2752 * Hides the PM notification box 2753 * 2754 * @param {event} e The event 2755 */ 2756 closePMpopup: function(e) 2757 { 2758 if( $('pm_notification') ) 2759 { 2760 new Effect.Parallel([ 2761 new Effect.Fade( $('pm_notification') ), 2762 new Effect.BlindUp( $('pm_notification') ) 2763 ], { duration: 0.5 } ); 2764 } 2765 2766 Event.stop(e); 2767 }, 2768 2769 /* ------------------------------ */ 2770 /** 2771 * Mark a PM read from popup 2772 * 2773 * @param {event} e The event 2774 */ 2775 markReadPMpopup: function(e) 2776 { 2777 if( $('pm_notification') ) 2778 { 2779 var elem = Event.findElement(e, 'a'); 2780 var href = elem.href.replace( /&/g, '&' ) + '&ajax=1'; 2781 2782 //Debug.write( 'Mark as read: ' + href ); 2783 2784 new Ajax.Request( href + "&md5check=" + ipb.vars['secure_hash'], 2785 { 2786 method: 'get', 2787 evalJSON: 'force', 2788 onSuccess: function(t){} 2789 }); 2790 2791 new Effect.Parallel([ 2792 new Effect.Fade( $('pm_notification') ), 2793 new Effect.BlindUp( $('pm_notification') ) 2794 ], { duration: 0.5 } ); 2795 } 2796 2797 Event.stop(e); 2798 return false; 2799 }, 2800 2801 /* ------------------------------ */ 2802 /** 2803 * Initializes GD image 2804 * 2805 */ 2806 initGD: function() 2807 { 2808 $('gd-antispam').observe('click', ipb.global.generateNewImage); 2809 2810 if( $('gd-image-link') ) 2811 { 2812 $('gd-image-link').observe('click', ipb.global.generateNewImage ); 2813 } 2814 }, 2815 2816 /* ------------------------------ */ 2817 /** 2818 * Simulate clicking the image 2819 * 2820 * @param {element} elem The GD image element 2821 */ 2822 generateImageExternally: function( elem ) 2823 { 2824 if( !$(elem) ){ return; } 2825 2826 $(elem).observe('click', ipb.global.generateNewImage); 2827 }, 2828 2829 /* ------------------------------ */ 2830 /** 2831 * Click event for generating new GD image 2832 * 2833 * @param {event} e The event 2834 */ 2835 generateNewImage: function(e) 2836 { 2837 img = $('gd-antispam'); 2838 2839 Event.stop(e); 2840 2841 oldSrc = img.src.toQueryParams(); 2842 oldSrc = $H( oldSrc ).toObject(); 2843 2844 if( !oldSrc['captcha_unique_id'] ){ Debug.error("No captcha ID found"); } 2845 2846 // Get new image 2847 new Ajax.Request( 2848 ipb.vars['base_url'] + "app=core&module=global§ion=captcha&do=refresh&captcha_unique_id=" + oldSrc['captcha_unique_id'] + '&secure_key='
2848+ ipb.vars['secure_hash'], 2849 { 2850 method: 'get', 2851 onSuccess: function(t) 2852 { 2853 //Change src 2854 oldSrc['captcha_unique_id'] = t.responseText; 2855 img.writeAttribute( { src: ipb.vars['base_url'] + $H( oldSrc ).toQueryString() } ); 2856 $('regid').value = t.responseText; 2857 } 2858 } 2859 ); 2860 }, 2861 2862 /* ------------------------------ */ 2863 /** 2864 * Registers a reputation toggle on the page 2865 * 2866 * @param {int} id The element that wraps rep 2867 * @param {string} url The URL to ping 2868 * @param {int} rating The current rep rating 2869 */ 2870 registerReputation: function( id, url, rating ) 2871 { 2872 if( !$( id ) ){ return; } 2873 2874 // Find rep up 2875 var rep_up = $( id ).down('.rep_up'); 2876 var rep_down = $( id ).down('.rep_down'); 2877 2878 var domLikeStripId = ( $(url.domLikeStripId) ) ? $(url.domLikeStripId) : false; 2879 2880 var sendUrl = ipb.vars['base_url'] + '&app=core&module=ajax§ion=reputation&do=add_rating&app_rate=' + url.app + '&type=' + url.type + '&type_id=' + url.typeid + '&secure_key=' + ipb.vars['secure_hash']; 2881 2882 if( $( rep_up ) ){ 2883 $( rep_up ).observe( 'click', ipb.global.repRate.bindAsEventListener(this, 1, id) ); 2884 } 2885 2886 if( $( rep_down ) ){ 2887 $( rep_down ).observe( 'click', ipb.global.repRate.bindAsEventListener(this, -1, id) ); 2888 } 2889 2890 ipb.global.reputation[ id ] = { obj: $( id ), domLikeStripId: domLikeStripId, url: url, sendUrl: sendUrl, currentRating: rating || 0 }; 2891 Debug.write( "Registered reputation" ); 2892 }, 2893 2894 /* ------------------------------ */ 2895 /** 2896 * Does a reputation rating action 2897 * 2898 * @param {event} e The event 2899 */ 2900 repRate: function( e ) 2901 { 2902 Event.stop(e); 2903 var type = $A(arguments)[1]; 2904 var id = $A(arguments)[2]; 2905 var value = ( type == 1 ) ? 1 : -1; 2906 2907 if( !ipb.global.reputation[ id ] ){ 2908 return; 2909 } else { 2910 var rep = ipb.global.reputation[ id ]; 2911 } 2912 2913 Debug.write( rep.sendUrl + '&rating=' + value ); 2914 2915 // Send ping 2916 new Ajax.Request( rep.sendUrl + '&rating=' + value, 2917 { 2918 method: 'get', 2919 onSuccess: function( t ) 2920 { 2921 if( t.responseJSON['status'] == 'ok' ) 2922 { 2923 try { 2924 // It worked! Hide the rep buttons 2925 rep.obj.down('.rep_up').up('li').hide(); 2926 rep.obj.down('.rep_down').up('li').hide(); 2927 2928 /* Can we see some, though? */ 2929 if ( t.responseJSON['canRepUp'] === true ) 2930 { 2931 rep.obj.down('.rep_up').up('li').show(); 2932 } 2933 2934 if ( t.responseJSON['canRepDown'] === true ) 2935 { 2936 rep.obj.down('.rep_down').up('li').show(); 2937 } 2938 } catch(err) { Debug.error( err ); } 2939 2940 // Update the figure 2941 var rep_display = rep.obj.down('.rep_show'); 2942 if( rep_display ) 2943 { 2944 ['positive', 'negative', 'zero'].each(function(c){ rep_display.removeClassName(c); }); 2945 2946 var newValue = parseInt( t.responseJSON['rating'] ); 2947 2948 if( newValue > 0 ) 2949 { 2950 rep_display.addClassName('positive'); 2951 } 2952 else if( newValue < 0 ) 2953 { 2954 rep_display.addClassName('negative'); 2955 } 2956 else 2957 { 2958 rep_display.addClassName('zero'); 2959 } 2960 2961 rep_display.update( newValue ); 2962 } 2963 2964 /* Got a like strip */ 2965 if ( $(rep.domLikeStripId.id) ) 2966 { 2967 if ( t.responseJSON['likeData'].formatted !== false ) 2968 { 2969 $(rep.domLikeStripId.id).update( t.responseJSON['likeData'].formatted ).show(); 2970 } 2971 else 2972 { 2973 $(rep.domLikeStripId.id).update( '' ).hide(); 2974 } 2975 } 2976 } 2977 else 2978 { 2979 if( t.responseJSON['error'] == 'nopermission' || t.responseJSON['error'] == 'no_permission' ) 2980 { 2981 ipb.global.errorDialogue( ipb.lang['no_permission'] ); 2982 } 2983 else 2984 { 2985 ipb.global.errorDialogue( t.responseJSON['error'] ); 2986 } 2987 } 2988 } 2989 }); 2990 2991 }, 2992 2993 /** 2994 * Fetch 'more'pop-up 2995 */ 2996 repLikeMore: function(e, elem) 2997 { 2998 Event.stop(e); 2999
3000 try 3001 { 3002 var id = elem.readAttribute('data-id'); 3003 var app = elem.readAttribute('data-app'); 3004 var type = elem.readAttribute('data-type'); 3005 } 3006 catch( e ) 3007 { 3008 Debug.error(e); 3009 } 3010 3011 if ( ! Object.isUndefined( ipb.global.popups['likeMore'] ) ) 3012 { 3013 ipb.global.popups['likeMore'].kill(); 3014 } 3015 3016 var popid = 'setfave_' + id; 3017 var _url = ipb.vars['base_url'] + '&app=core&module=ajax§ion=reputation&do=more&secure_key=' + ipb.vars['secure_hash'] + '&f_app=' + app + '&f_type=' + type + '&f_id=' + id; 3018 Debug.write( _url ); 3019 3020 /* easy one this... */ 3021 ipb.global.popups['likeMore'] = new ipb.Popup( popid, { type: 'pane', 3022 ajaxURL: _url, 3023 stem: false, 3024 hideAtStart: false, 3025 h: 500, 3026 w: '450px' }); 3027 }, 3028 3029 /* ------------------------------ */ 3030 /** 3031 * Utility function for converting bytes 3032 * 3033 * @param {int} size The value in bytes to convert 3034 * @return {string} The converted string, with unit 3035 */ 3036 convertSize: function(size) 3037 { 3038 var kb = 1024; 3039 var mb = 1024 * 1024; 3040 var gb = 1024 * 1024 * 1024; 3041 3042 if( size < kb ){ return size + " B"; } 3043 if( size < mb ){ return ( size / kb ).toFixed( 2 ) + " KB"; } 3044 if( size < gb ){ return ( size / mb ).toFixed( 2 ) + " MB"; } 3045 3046 return ( size / gb ).toFixed( 2 ) + " GB"; 3047 }, 3048 3049 /* ------------------------------ */ 3050 /** 3051 * Registers a page jump toggle 3052 * 3053 * @param {int} source ID of this jump 3054 * @param {hash} options Options for this jump 3055 */ 3056 registerPageJump: function( source, options ) 3057 { 3058 if( !source || !options ){ 3059 return; 3060 } 3061 3062 ipb.global.pageJumps[ source ] = options; 3063 }, 3064 3065 /* ------------------------------ */ 3066 /** 3067 * Builds a page jump control 3068 */ 3069 buildPageJumps: function() 3070 { 3071 $$('.pagejump').each( function(elem){ 3072 // Find the pj ID 3073 var classes = $( elem ).className.match(/pj([0-9]+)/); 3074 3075 if( Object.isUndefined( classes ) || !classes || !classes[1] ){ 3076 return; 3077 } 3078 3079 $( elem ).identify(); 3080 3081 // Doth a popup exist? 3082 //Debug.write( "This wrapper has been created! " + classes[1] ); 3083 var temp = ipb.templates['page_jump'].evaluate( { id: 'pj_' + $(elem).identify() } ); 3084 $$('body')[0].insert( temp ); 3085 3086 $('pj_' + $(elem).identify() + '_submit').observe('click', ipb.global.pageJump.bindAsEventListener( this, $(elem).identify() ) ); 3087 3088 // So it submits on enter 3089 $('pj_' + $(elem).identify() + '_input').observe('keypress', function(e){ 3090 if( e.which == Event.KEY_RETURN ) 3091 { 3092 ipb.global.pageJump( e, $(elem).identify() ); 3093 } 3094 }); 3095 3096 var wrap = $( 'pj_' + $(elem).identify() + '_wrap' ).addClassName('pj' + classes[1]).writeAttribute('jumpid', classes[1] ); 3097 3098 var callback = { 3099 afterOpen: function( popup ){ 3100 try { 3101 $( 'pj_' + $(elem).identify() + '_input').activate(); 3102 } 3103 catch(err){ } 3104 } 3105 }; 3106 3107 ipb.global.pageJumpMenus[ classes[1] ] = new ipb.Menu( $( elem ), $( wrap ), { stopClose: true }, callback ); 3108 }); 3109 }, 3110 3111 /* ------------------------------ */ 3112 /** 3113 * Executes a page jump 3114 * 3115 * @param {event} e The event 3116 * @param {element} elem The page jump element 3117 */ 3118 pageJump: function( e, elem ) 3119 { 3120 if( !$( elem ) || !$( 'pj_' + $(elem).id + '_input' ) ){ return; } 3121 3122 var value = $F( 'pj_' + $(elem).id + '_input' ); 3123 var jumpid = $( 'pj_' + $(elem).id + '_wrap' ).readAttribute( 'jumpid' ); 3124 3125 if( value.blank() ){ 3126 try { 3127 ipb.global.pageJumpMenus[ source ].doClose(); 3128 } catch(err) { } 3129 } 3130 else 3131 { 3132 value = parseInt( value ); 3133 } 3134 3135 // Work out page number 3136 var options = ipb.global.pageJumps[ jumpid ]; 3137 if( !options ){ Debug.dir( ipb.global.pageJumps ); Debug.write( jumpid ); return; } 3138 3139 var pageNum = ( ( value - 1 ) * options.perPage ); 3140 Debug.write( pageNum ); 3141 3142 if( pageNum < 1 ){ 3143 pageNum = 0; 3144 } 3145 /*else if( pageNum > options.totalPages ){ 3146 pageNum = options.totalPages; 3147 }*/ 3148 3149 /* Probably much easier to let the PHP redirect figure it out 3150 * now we have the new page-2 options. Not an SEO issue as crawlers won't input 3151 * data and not a resouces issue as drop down is hardly a hot spot for traffic 3152 */ 3153 3154 var separator = options.url.indexOf( '&' ) !== -1 ? '&' : '?' ; 3155 3156 var url = options.url + separator + options.stKey + '=' + pageNum; 3157 3158 if( options.anchor ) 3159 { 3160 url = url + options.anchor; 3161 } 3162 3163 url = url.replace(/&/g, '&'); 3164 // Without a negative lookbehind, http:// gets replaced with http:/ when we replace // with / 3165 // @see http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript 3166 url = url.replace(/(http:|https:)?\/\//g, function($0, $1) { return $1 ? $0 : '/'; } ); 3167
3168 document.location = url; 3169 3170 return; 3171 }, 3172 3173 /* ------------------------------ */ 3174 /** 3175 * Open the link in a new window 3176 * 3177 * @param {event} e The event 3178 * @param {boolean} force Force new window regardless of host? 3179 */ 3180 openNewWindow: function(e, link, force) 3181 { 3182 var ourHost = document.location.host; 3183 var newHost = link.host; 3184 3185 if( Prototype.Browser.IE ) 3186 { 3187 newHost = newHost.replace( /^(.+?):(\d+)$/, '$1' ); 3188 } 3189 3190 /** 3191 * Open a new window, if link is to a different host 3192 */ 3193 if( ourHost != newHost || force ) 3194 { 3195 window.open(link.href); 3196 Event.stop(e); 3197 return false; 3198 } 3199 else 3200 { 3201 return true; 3202 } 3203 }, 3204 3205 /* ------------------------------ */ 3206 /** 3207 * Registers an ajax marker 3208 * 3209 * @param {string} id ID of the wrapper element 3210 * @param {string} key Key of the current marker status (e.g. f_unread) 3211 * @param {string} url URL to ping 3212 */ 3213 registerMarker: function( id, key, url ) 3214 { 3215 if( !$(id) || key.blank() || url.blank() ){ return; } 3216 Debug.write( "Marker INIT: " + id ); 3217 $( id ).observe('click', ipb.global.sendMarker.bindAsEventListener( this, id, key, url ) ); 3218 }, 3219 3220 /* ------------------------------ */ 3221 /** 3222 * Sends a marker read request 3223 * 3224 * @param {event} e The event 3225 * @param {string} id ID of containing element 3226 * @param {string} key Key of current marker 3227 * @param {string} url URL to ping 3228 */ 3229 sendMarker: function( e, id, key, url ) 3230 { 3231 Event.stop(e); 3232 3233 new Ajax.Request( url + "&secure_key=" + ipb.vars['secure_hash'], 3234 { 3235 method: 'get', 3236 evalJSON: 'force', 3237 onSuccess: function(t) 3238 { 3239 if( Object.isUndefined( t.responseJSON ) ) 3240 { 3241 Debug.error("Invalid server response"); 3242 return false; 3243 } 3244 3245 if( t.responseJSON['error'] ) 3246 { 3247 Debug.error( t.responseJSON['error'] ); 3248 return false; 3249 } 3250 3251 if ( $(id + '_tooltip') ){ 3252 $(id + '_tooltip').hide(); 3253 } 3254 3255 $( id ).up('tr').removeClassName('unread'); 3256 3257 // Update icon 3258 $( id ).replace( unreadIcon ); 3259 3260 // Update subforum icons 3261 var _intId = id.replace( /forum_img_/, '' ); 3262 3263 if( $("subforums_" + _intId ) ) 3264 { 3265 $$("#subforums_" + _intId + " li" ).each( function(elem) { 3266 $(elem).removeClassName('unread'); 3267 }); 3268 } 3269 } 3270 }); 3271 }, 3272 3273 registerCheckAll: function( id, classname ) 3274 { 3275 if( !$( id ) ){ return; } 3276 3277 $( id ).observe('click', ipb.global.checkAll.bindAsEventListener( this, classname ) ); 3278 3279 $$('.' + classname ).each( function(elem){ 3280 $( elem ).observe('click', ipb.global.checkOne.bindAsEventListener( this, id ) ); 3281 }); 3282 }, 3283 3284 checkAll: function( e, classname ) 3285 { 3286 Debug.write('checkAll'); 3287 var elem = Event.element(e); 3288 3289 // Get all checkboxes 3290 var checkboxes = $$('.' + classname); 3291 3292 if( elem.checked ){ 3293 checkboxes.each( function(check){ 3294 check.checked = true; 3295 }); 3296 } else { 3297 checkboxes.each( function(check){ 3298 check.checked = false; 3299 }); 3300 } 3301 }, 3302 3303 checkOne: function(e, id) 3304 { 3305 var elem = Event.element(e); 3306 3307 if( $( id ).checked && elem.checked == false ) 3308 { 3309 $( id ).checked = false; 3310 } 3311 }, 3312 3313 updateReportStatus: function(e, reportID, noauto, noimg ) 3314 { 3315 Event.stop(e); 3316 3317 var url = ipb.vars['base_url'] + "app=core&module=ajax&section=reports&do=change_status&secure_key=" + ipb.vars['secure_hash'] + "&status=3&id=" + parseInt( reportID ) + "&noimg=" + parseInt( noimg ) + "&noauto=" + parseInt( noauto ); 3318 3319 // Do request, see what we get 3320 new Ajax.Request( url.replace(/&/g, '&'), 3321 { 3322 method: 'post', 3323 evalJSON: 'force', 3324 onSuccess: function( t ) 3325 { 3326 if( Object.isUndefined( t.responseJSON ) ) 3327 { 3328 alert( ipb.lang['action_failed'] ); 3329 return; 3330 } 3331 3332 try { 3333 $('rstat-' + reportID).update( t.responseJSON['img'] ); 3334 ipb.menus.closeAll( e ); 3335 } catch(err) { 3336 Debug.error( err ); 3337 } 3338 } 3339 }); 3340 }, 3341 3342 getTotalOffset: function(elem, top, left) 3343 { 3344 if( $( elem ).getOffsetParent() != document.body ) 3345 { 3346 Debug.write( "Checking " + $(elem).id ); 3347 var extra = $(elem).positionedOffset(); 3348 top += extra['top']; 3349 left += extra['left']; 3350 3351 return ipb.global.getTotalOffset( $( elem ).getOffsetParent(), top, left ); 3352 } 3353 else 3354 { 3355 Debug.write("OK Finished!"); 3356 return { top: top, left: left }; 3357 } 3358 }, 3359 3360 // Checks a server response from an ajax request for 'nopermission' 3361 checkPermission: function( text ) 3362 { 3363 if( text == "nopermission" || text == 'no_permission' ) 3364 { 3365 alert( ipb.lang['no_permission'] ); 3366 return false;
3367 } 3368 3369 return true; 3370 }, 3371 3372 /** 3373 * Check for entry keypress 3374 */ 3375 checkForEnter: function(e, callback) 3376 { 3377 if( ![ Event.KEY_RETURN ].include( e.keyCode ) ){ 3378 return; // Not interested in anything else 3379 } 3380 3381 if ( callback ) 3382 { 3383 switch( e.keyCode ) 3384 { 3385 case Event.KEY_RETURN: 3386 callback(e); 3387 break; 3388 } 3389 3390 Event.stop(e); 3391 } 3392 }, 3393 3394 /** 3395 * Check for DST 3396 */ 3397 checkDST: function() 3398 { 3399 var memberHasDst = ipb.vars['dst_in_use']; 3400 var dstInEffect = new Date().getDST(); 3401 3402 if( memberHasDst - dstInEffect != 0 ) 3403 { 3404 var url = ipb.vars['base_url'] + 'app=members&module=ajax§ion=dst&md5check='+ipb.vars['secure_hash']; 3405 3406 new Ajax.Request( url, 3407 { 3408 method: 'get', 3409 onSuccess: function(t) 3410 { 3411 // We don't need to do anything about this.. 3412 return true; 3413 } 3414 } 3415 ); 3416 } 3417 } 3418}; 3419 3420/************************************************/ 3421/* IPB3 Javascript */ 3422/* -------------------------------------------- */ 3423/* ips.menu.js - Me n you class <3 */ 3424/* (c) IPS, Inc 2008 */ 3425/* -------------------------------------------- */ 3426/* Author: Rikki Tissier */ 3427/************************************************/ 3428 3429/* ipb.menus is a menu manager; ipb.Menu is a menu object */ 3430 3431var _menu = window.IPBoard; 3432_menu.prototype.menus = { 3433 registered: $H(), 3434 closeCallBack: false, 3435 3436 init: function() 3437 { 3438 Debug.write("Initializing ips.menu.js"); 3439 document.observe("dom:loaded", function(){ 3440 ipb.menus.initEvents(); 3441 }); 3442 }, 3443 3444 initEvents: function() 3445 { 3446 // Set document event 3447 Event.observe( document, 'click', ipb.menus.docCloseAll ); 3448 3449 // Auto-find menus 3450 $$('.ipbmenu').each( function(menu){ 3451 id = menu.identify(); 3452 if( $( id + "_menucontent" ) ) 3453 { 3454 new ipb.Menu( menu, $( id + "_menucontent" ) ); 3455 } 3456 }); 3457 }, 3458 3459 register: function( source, obj ) 3460 { 3461 ipb.menus.registered.set( source, obj ); 3462 }, 3463 3464 registerCloseCallBack: function( callBack ) 3465 { 3466 ipb.menus.closeCallBack = callBack; 3467 }, 3468 3469 docCloseAll: function( e ) 3470 { 3471 ipb.menus.closeAll( e ); 3472 }, 3473 3474 /*!! Close all menus (forceClose ignores clicked in menu area check) */ 3475 closeAll: function( except, forceClose ) 3476 { 3477 ipb.menus.registered.each( function(menu, force){ 3478 3479 if( typeof( except ) == 'undefined' || ( except && menu.key != except ) ) 3480 { 3481 try{ 3482 if( forceClose || ( !(except.target && $(except.target).descendantOf( menu.value.target )) && except != menu.key ) ){ 3483 menu.value.doClose(); 3484 } 3485 } catch(err) { 3486 // Assume this menu gone byebye 3487 } 3488 } 3489 }); 3490 3491 /* Could make this an array and chain events */ 3492 if ( Object.isFunction( ipb.menus.closeCallBack ) ) 3493 { 3494 ipb.menus.closeCallBack(); 3495 } 3496 } 3497}; 3498 3499_menu.prototype.Menu = Class.create({ 3500 initialize: function( source, target, options, callbacks ){ 3501 if( !$( source ) || !$( target ) ){ return; } 3502 if( !$( source ).id ){ 3503 $( source ).identify(); 3504 } 3505 this.id = $( source ).id + '_menu'; 3506 this.source = $( source ); 3507 this.target = $( target ); 3508 this.callbacks = callbacks || {}; 3509 3510 this.options = Object.extend( { 3511 eventType: 'click', 3512 closeOnMouseout: false, 3513 stopClose: true, 3514 offsetX: 0, 3515 offsetY: 0 3516 }, arguments[2] || {}); 3517 3518 // Set up events 3519 $( source ).observe( 'click', this.eventClick.bindAsEventListener( this ) ); 3520 $( source ).observe( 'mouseover', this.eventOver.bindAsEventListener( this ) ); 3521 $( target ).observe( 'click', this.targetClick.bindAsEventListener( this ) ); 3522 3523 /* Close on mouse out too? */ 3524 if( this.options['closeOnMouseout'] !== false ) 3525 { 3526 $( this.options['closeOnMouseout'] ).observe( 'mouseleave', this.mouseOutClose.bindAsEventListener( this ) ); 3527 } 3528 3529 /* Have an alt? */ 3530 if ( $( $( source ).id + '_alt' ) ) 3531 { 3532 $( $( source ).id + '_alt' ).observe( 'click', this.eventClick.bindAsEventListener( this, $( $( source ).id + '_alt' ) ) ); 3533 $( $( source ).id + '_alt' ).observe( 'mouseover', this.eventOver.bindAsEventListener( this ) ); 3534 } 3535 3536 // Set up target 3537 $( this.target ).setStyle( 'position: absolute;' ).hide().setStyle( { zIndex: 9999 } ); 3538 $( this.target ).descendants().each( function( elem ){ 3539 $( elem ).setStyle( { zIndex: 10000 } ); 3540 }); 3541
3542 ipb.menus.register( $( source ).id, this ); 3543 3544 if( Object.isFunction( this.callbacks['afterInit'] ) ) 3545 { 3546 this.callbacks['afterInit']( this ); 3547 } 3548 }, 3549 3550 doOpen: function(elem) 3551 { 3552 Debug.write("Menu open"); 3553 var pos = {}; 3554 3555 var _source = ( this.options.positionSource ) ? this.options.positionSource : this.source; 3556 3557 if ( ! Object.isUndefined( elem ) ) 3558 { 3559 var _source = elem; 3560 } 3561 3562 // This is the positioned offset of the source element 3563 var sourcePos = $( _source ).positionedOffset(); 3564 3565 // Cumulative offset (actual position on the page, e.g. if you scrolled down it could be higher than max resolution height) 3566 var _sourcePos = $( _source ).cumulativeOffset(); 3567 3568 // Cumulative offset of your scrolling (how much you have scrolled) 3569 var _offset = $( _source ).cumulativeScrollOffset(); 3570 3571 // Real source position: Actual position on page, minus scroll offset (provides position on page within viewport) 3572 var realSourcePos = { top: _sourcePos.top - _offset.top, left: _sourcePos.left - _offset.left }; 3573 3574 // Dimensions of source object 3575 var sourceDim = $( _source ).getDimensions(); 3576 3577 // Viewport dimensions (e.g. 1280x1024) 3578 var screenDim = document.viewport.getDimensions(); 3579 3580 // Target dimensions 3581 var menuDim = { width: $( this.target ).measure('border-box-width'), height: $(this.target).measure('border-box-height') }; 3582 3583 var isFixed = $( _source ).ancestors().find( function(el){ return el.getStyle('position') == 'fixed'; } ); 3584 3585 /* RTL bug fixes */ 3586 if( isRTL ) 3587 { 3588 if( sourcePos.top < 0 ) 3589 { 3590 sourcePos.top = realSourcePos.top; 3591 } 3592 3593 // Really really hacky RTL bug fix... :( 3594 if( $(_source).id == 'user_link' ) 3595 { 3596 //alert(sourcePos.left); 3597 sourcePos.left = sourcePos.left - ( parseInt($(_source).getStyle('padding-left').replace( /px/, '' )) + parseInt($(_source).getStyle('margin-left').replace( /px/, '' )) ); 3598 //alert(sourcePos.left); 3599 } 3600 } 3601 3602 // Some logging 3603 Debug.write( "realSourcePos: " + realSourcePos.top + " x " + realSourcePos.left ); 3604 Debug.write( "sourcePos: " + sourcePos.top + " x " + sourcePos.left ); 3605 Debug.write( "scrollOffset: " + _offset.top + " x " + _offset.left ); 3606 Debug.write( "_sourcePos: " + _sourcePos.top + " x " + _sourcePos.left ); 3607 Debug.write( "sourceDim: " + sourceDim.width + " x " + sourceDim.height); 3608 Debug.write( "menuDim: " + menuDim.height ); 3609 Debug.write( "screenDim: " + screenDim.height ); 3610 Debug.write( "manual ofset: " + this.options.offsetX + " x " + this.options.offsetY ); 3611 3612 // Ok, if it's a relative parent, do one thing, else be normal 3613 // Getting fed up of this feature and IE bugs 3614 _a = _source.getOffsetParent(); 3615 _b = this.target.getOffsetParent(); 3616 3617 Debug.write("_a is " + _a ); 3618 Debug.write("_b is " + _b ); 3619 3620 if( isFixed ) 3621 { 3622 $( this.target ).setStyle('position: fixed'); 3623 3624 if( ( _sourcePos.left + menuDim.width ) > screenDim.width ){ 3625 diff = menuDim.width - sourceDim.width; 3626 pos.left = _sourcePos.left - diff + this.options.offsetX; 3627 } else { 3628 pos.left = (_sourcePos.left) + this.options.offsetX; 3629 } 3630 3631 if( ( _sourcePos.top + menuDim.height ) > screenDim.height ){ 3632 pos.top = _sourcePos.top - menuDim.height + this.options.offsetY; 3633 } else { 3634 pos.top = _sourcePos.top + sourceDim.height + this.options.offsetY; 3635 } 3636 3637 3638 $( this.target ).setStyle( 'top: ' + (pos.top-1) + 'px; left: ' + pos.left + 'px;' ); 3639 } 3640 else 3641 { 3642 if( _a != _b ) 3643 { 3644 // Left 3645 if( ( realSourcePos.left + menuDim.width ) > screenDim.width ){ 3646 diff = menuDim.width - sourceDim.width; 3647 pos.left = _sourcePos.left - diff + this.options.offsetX; 3648 } else { 3649 if( Prototype.Browser.IE7 ){ 3650 pos.left = (_sourcePos.left) + this.options.offsetX; 3651 } else { 3652 pos.left = (_sourcePos.left) + this.options.offsetX; 3653 } 3654 } 3655 3656 // Top 3657 /* If there's no space to open downwards, open upwards *unless* 3658 /* it would go off the top of the screen (i.e. < 0px ) Bug #18270 */ 3659 if( 3660 ( ( ( realSourcePos.top + sourceDim.height ) + menuDim.height ) > screenDim.height ) && 3661 ( _sourcePos.top - menuDim.height + this.options.offsetY ) > 0 ) 3662 { 3663 pos.top = _sourcePos.top - menuDim.height + this.options.offsetY; 3664 } else { 3665 pos.top = _sourcePos.top + sourceDim.height + this.options.offsetY; 3666 } 3667 } 3668 else 3669 {
3670 Debug.write("MENU: source offset EQUALS target offset"); 3671 3672 // Left 3673 if( ( realSourcePos.left + menuDim.width ) > screenDim.width ){ 3674 diff = menuDim.width - sourceDim.width; 3675 pos.left = sourcePos.left - diff + this.options.offsetX; 3676 } else { 3677 pos.left = sourcePos.left + this.options.offsetX; 3678 } 3679 3680 // Top 3681 /* If there's no space to open downwards, open upwards *unless* 3682 /* it would go off the top of the screen (i.e. < 0px ) Bug #18270 */ 3683 if( 3684 ( ( ( realSourcePos.top + sourceDim.height ) + menuDim.height ) > screenDim.height ) && 3685 ( _sourcePos.top - menuDim.height + this.options.offsetY ) > 0 ) 3686 { 3687 pos.top = sourcePos.top - menuDim.height + this.options.offsetY; 3688 } else { 3689 pos.top = sourcePos.top + sourceDim.height + this.options.offsetY; 3690 } 3691 } 3692 3693 $( this.target ).setStyle( 'top: ' + (pos.top-1) + 'px; left: ' + pos.left + 'px;' ); 3694 } 3695 3696 $( this.source ).addClassName('menu_active'); // Set active class on the source 3697 3698 // Now set pos 3699 Debug.write("Menu position: " + pos.top + " x " + pos.left ); 3700 3701 3702 // If we have any fixed ancestors, then fix the menu too 3703 /*if( isFixed ){ 3704 $( this.target ).setStyle('position: fixed'); 3705 }*/ 3706 3707 // And show 3708 new Effect.Appear( $( this.target ), { duration: 0.2, afterFinish: function(e){ 3709 if( Object.isFunction( this.callbacks['afterOpen'] ) ) 3710 { 3711 this.callbacks['afterOpen']( this ); 3712 } 3713 }.bind(this) } ); 3714 3715 // Set key event so we can close on ESC 3716 Event.observe( document, 'keypress', this.checkKeyPress.bindAsEventListener( this ) ); 3717 }, 3718 3719 checkKeyPress: function( e ) 3720 { 3721 //Debug.write( e ); 3722 3723 if( e.keyCode == Event.KEY_ESC ) 3724 { 3725 this.doClose(); 3726 } 3727 }, 3728 3729 mouseOutClose: function() 3730 { 3731 this.doClose(); 3732 }, 3733 3734 doClose: function() 3735 { 3736 new Effect.Fade( $( this.target ), { duration: 0.3, afterFinish: function(e){ 3737 if( Object.isFunction( this.callbacks['afterClose'] ) ) 3738 { 3739 this.callbacks['afterClose']( this ); 3740 } 3741 }.bind( this ) } ); 3742 3743 //Debug.write( "Closing " + $( this.source ).id ); 3744 this.source.removeClassName('menu_active'); 3745 }, 3746 3747 targetClick: function(e) 3748 { 3749 if( !this.options.stopClose ){ 3750 this.doClose(); 3751 } 3752 3753 /*try 3754 { 3755 var elem = Event.findElement(e); 3756 3757 if ( elem.hasClassName('_noCloseMenuUponClick') ) 3758 { 3759 Event.stop(e); 3760 } 3761 } 3762 catch(e) { } 3763 3764 if( ( this.options.stopClose && !$(elem).match("input") || $(elem).match("input") ) ){ 3765 //if( $(e.target).match("[type=checkbox]")){ 3766 // Debug.write( $(e.target).checked ); 3767 // $(e.target).checked = true; 3768 //} 3769 Event.stop(e); 3770 }*/ 3771 }, 3772 3773 eventClick: function(e, elem) 3774 { 3775 if( this.options['eventType'] == 'click' ) 3776 { 3777 Event.stop(e); 3778 3779 if( $( this.target ).visible() ){ 3780 3781 if( Object.isFunction( this.callbacks['beforeClose'] ) ) 3782 { 3783 this.callbacks['beforeClose']( this ); 3784 } 3785 3786 this.doClose(); 3787 } else { 3788 ipb.menus.closeAll( $(this.source).id ); 3789 3790 if( Object.isFunction( this.callbacks['beforeOpen'] ) ) 3791 { 3792 this.callbacks['beforeOpen']( this ); 3793 } 3794 3795 this.doOpen(elem); 3796 } 3797 } 3798 }, 3799 3800 eventOver: function() 3801 { 3802 if( this.options['eventType'] == 'mouseover' ) 3803 { 3804 if( !$( this.target ).visible() ){ 3805 3806 ipb.menus.closeAll( $(this.source).id ); 3807 3808 if( Object.isFunction( this.callbacks['beforeOpen'] ) ) 3809 { 3810 this.callbacks['beforeOpen']( this ); 3811 } 3812 3813 this.doOpen(); 3814 } 3815 } 3816 } 3817}); 3818 3819 3820/************************************************/ 3821/* IPB3 Javascript */ 3822/* -------------------------------------------- */ 3823/* ips.popup.js - Popup creator */ 3824/* (c) IPS, Inc 2008 */ 3825/* -------------------------------------------- */ 3826/* Author: Rikki Tissier */ 3827/************************************************/ 3828 3829/** 3830 * Full list of options: 3831 * 3832 * type: balloon, pane 3833 * modal: true/false 3834 * w: width 3835 * h: height 3836 * classname: classname to be applied to wrapper 3837 * initial: initial content 3838 * ajaxURL: If supplied, will ping URL for content and update popup 3839 * close: element that will close popup (wont work with balloon) 3840 * attach: { target, event, mouse, offset } 3841 * hideAtStart: Hide after creation (allows showing at a later time) 3842 * stem: true/false 3843 * delay: { show, hide } 3844 */ 3845_popup = window.IPBoard; 3846_popup.prototype.Popup = Class.create({ 3847 3848 initialize: function( id, options, callbacks ) 3849 { 3850 /* Set up properties */ 3851 this.id = ''; 3852 this.wrapper = null; 3853 this.inner = null; 3854 this.stem = null; 3855 this.options = {}; 3856 this.timer = []; 3857 this.ready = false;
3858 this.visible = false; 3859 this._startup = null; 3860 this.hideAfterSetup = false; 3861 this.eventPairs = { 'mouseover': 'mouseout', 3862 'mousedown': 'mouseup' 3863 }; 3864 this._tmpEvent = null; 3865 3866 /* Now run */ 3867 this.id = id; 3868 this.options = Object.extend({ 3869 type: 'pane', 3870 w: '500px', 3871 modal: false, 3872 modalOpacity: 0.4, 3873 hideAtStart: true, 3874 delay: { show: 0, hide: 0 }, 3875 defer: false, 3876 hideClose: false, 3877 black: false, 3878 warning: false, 3879 evalJs: true, 3880 evalScript: true, /* Should we render JS in DOM.innerHTML */ 3881 closeContents: ipb.templates['close_popup'] 3882 }, arguments[1] || {}); 3883 3884 this.callbacks = callbacks || {}; 3885 3886 // Are we deferring the load? 3887 if( this.options.defer && $( this.options.attach.target ) ) 3888 { 3889 this._defer = this.init.bindAsEventListener( this ); 3890 $( this.options.attach.target ).observe( this.options.attach.event, this._defer ); 3891 3892 if( this.eventPairs[ this.options.attach.event ] ) 3893 { 3894 this._startup = function(e){ this.hideAfterSetup = true; this.hide(); }.bindAsEventListener( this ); 3895 $( this.options.attach.target ).observe( this.eventPairs[ this.options.attach.event ], this._startup ); 3896 } 3897 } 3898 else 3899 { 3900 this.init(); 3901 } 3902 }, 3903 3904 init: function() 3905 { 3906 try { 3907 Event.stopObserving( $( this.options.attach.target ), this.options.attach.event, this._defer ); 3908 3909 if ( $(this.options.attach.target) ) 3910 { 3911 var toff = $( this.options.attach.target ).positionedOffset(); 3912 var menu = $(this.options.attach.target).up('.ipbmenu_content'); 3913 3914 if ( toff.top == 0 && toff.left == 0 || $(menu) ) 3915 { 3916 /* make it a centered box */ 3917 this.options.type = 'modal'; 3918 this.options.attach = {}; 3919 } 3920 } 3921 3922 } catch(err) { } 3923 3924 3925 this.wrapper = new Element('div', { 'id': this.id + '_popup' } ).setStyle('z-index: 10001').hide().addClassName('popupWrapper'); 3926 3927 this.inner = new Element('div', { 'id': this.id + '_inner' } ).addClassName('popupInner'); 3928 3929 if ( this.options.black ) 3930 { 3931 this.inner.addClassName('black_mode'); 3932 } 3933 3934 if ( this.options.warning ) 3935 { 3936 this.inner.addClassName('warning_mode'); 3937 } 3938 3939 if( this.options.w ){ this.inner.setStyle( 'width: ' + this.options.w ); } 3940 3941 this.wrapper.insert( this.inner ); 3942 3943 if( this.options.hideClose != true ) 3944 { 3945 this.closeLink = new Element('div', { 'id': this.id + '_close' } ).addClassName('popupClose').addClassName('clickable'); 3946 this.closeLink.update( this.options.closeContents ); 3947 this.closeLink.observe('click', this.hide.bindAsEventListener( this ) ); 3948 this.wrapper.insert( this.closeLink ); 3949 3950 if ( this.options.black || this.options.warning ) 3951 { 3952 this.closeLink.addClassName('light_close_button'); 3953 } 3954 } 3955 3956 $$('body')[0].insert( this.wrapper ); 3957 3958 if( this.options.classname ){ this.wrapper.addClassName( this.options.classname ); } 3959 3960 if( this.options.initial ){ 3961 this.update( this.options.initial, this.options.evalScript ); 3962 } 3963 3964 // Callback 3965 if( Object.isFunction( this.callbacks['beforeAjax'] ) ) 3966 { 3967 this.callbacks['beforeAjax']( this ); 3968 } 3969 3970 // If we are updating with ajax, handle the show there 3971 if( this.options.ajaxURL ){ 3972 this.updateAjax(); 3973 setTimeout( this.continueInit.bind(this), 80 ); 3974 } else { 3975 this.ready = true; 3976 this.continueInit(); 3977 } 3978 }, 3979 3980 continueInit: function() 3981 { 3982 if( !this.ready ) 3983 { 3984 setTimeout( this.continueInit.bind(this), 80 ); 3985 return; 3986 } 3987 3988 /* Ensure pop-up isn't larger than viewport */ 3989 if( this.inner.select(".fixed_inner").size() ) 3990 { 3991 Debug.write("Found fixed_inner"); 3992 this.inner.select(".fixed_inner")[0].setStyle( 'height: ' + this.options.h + 'px; max-height: ' + this.options.h + 'px; overflow: auto' ); 3993 } 3994 else 3995 { 3996 var _vph = document.viewport.getDimensions().height - 25; 3997 this.options.h = ( this.options.h && _vph > this.options.h ) ? this.options.h : _vph; 3998 this.inner.setStyle( 'max-height: ' + this.options.h + 'px' ); 3999 } 4000 4001 //Debug.write("Continuing..."); 4002 // What are we making? 4003 if( this.options.type == 'balloon' ){ 4004 this.setUpBalloon(); 4005 } else { 4006 this.setUpPane(); 4007 } 4008 4009 // Set up close event 4010 try { 4011 if( this.options.close ){ 4012 closeElem = $( this.wrapper ).select( this.options.close )[0]; 4013 4014 if( Object.isElement( closeElem ) ) 4015 { 4016 $( closeElem ).observe( 'click', this.hide.bindAsEventListener( this ) ); 4017 } 4018 } 4019 } catch( err ) { 4020 Debug.write( err ); 4021 } 4022 4023 // Callback 4024 if( Object.isFunction( this.callbacks['afterInit'] ) ) 4025 { 4026 this.callbacks['afterInit']( this ); 4027 } 4028 4029 if( !this.options.hideAtStart && !this.hideAfterSetup ) 4030 { 4031 this.show(); 4032 } 4033 if( this.hideAfterSetup && this._startup ) 4034 { 4035 Event.stopObserving( $( this.options.attach.target ), this.eventPairs[ this.options.attach.event ], this._startup ); 4036 } 4037 }, 4038 4039 updateAjax: function() 4040 { 4041 Debug.write( this.options.ajaxURL ); 4042 new Ajax.Request( this.options.ajaxURL, 4043 { 4044 method: 'get', 4045 evalJS: this.options.evalJs, 4046 onSuccess: function(t) 4047 { 4048 if ( t.responseText != 'error' ) 4049 { 4050 try 4051 { 4052 if ( ! Object.isUndefined( t.responseJSON ) && ! Object.isUndefined( t.responseJSON['error'] ) ) 4053 { 4054 if ( t.responseJSON['__board_offline__'] ) 4055 { 4056 ipb.global.errorDialogue( ipb.lang['board_offline'] ); 4057 ipb.menus.closeAll(e); 4058 } 4059 else 4060 { 4061 ipb.global.errorDialogue( t.responseJSON['error'] ); 4062 } 4063 4064 return false;
4065 } 4066 } catch(e){} 4067 4068 if ( t.responseText == 'nopermission' ) 4069 { 4070 ipb.global.errorDialogue( ipb.lang['no_permission'] ); 4071 return; 4072 } 4073 4074 /* Check for log out */ 4075 if ( t.responseText.match( "__session__expired__log__out__" ) ) 4076 { 4077 this.update(''); 4078 alert( ipb.lang['session_timed_out'] ); 4079 return false; 4080 } 4081 4082 Debug.write( "AJAX done!" ); 4083 this.update( t.responseText ); 4084 this.ready = true; 4085 4086 // Callback 4087 if( Object.isFunction( this.callbacks['afterAjax'] ) ) 4088 { 4089 this.callbacks['afterAjax']( this, t.responseText ); 4090 } 4091 } 4092 else 4093 { 4094 Debug.write( t.responseText ); 4095 return; 4096 } 4097 }.bind(this) 4098 }); 4099 }, 4100 4101 show: function(e) 4102 { 4103 if( e ){ Event.stop(e); } 4104 4105 if( this.timer['show'] ){ 4106 clearTimeout( this.timer['show'] ); 4107 } 4108 4109 if( this.options.delay.show != 0 ){ 4110 4111 this.timer['show'] = setTimeout( this._show.bind( this ), this.options.delay.show ); 4112 } else { 4113 this._show(); // Just show it 4114 } 4115 }, 4116 4117 hide: function(e) 4118 { 4119 if( e ){ Event.stop(e); } 4120 if( this.document_event ){ 4121 Event.stopObserving( document, 'click', this.document_event ); 4122 Event.stopObserving( document, 'touchstart', this.document_event ); 4123 } 4124 4125 if( this.timer['hide'] ){ 4126 clearTimeout( this.timer['hide'] ); 4127 } 4128 4129 if( this.options.delay.hide != 0 ){ 4130 this.timer['hide'] = setTimeout( this._hide.bind( this ), this.options.delay.hide ); 4131 } else { 4132 this._hide(); // Just hide it 4133 } 4134 }, 4135 4136 /* remove wrapper and kill timeouts */ 4137 kill: function() 4138 { 4139 if( this.timer['hide'] ){ 4140 clearTimeout( this.timer['hide'] ); 4141 } 4142 4143 if( this.timer['show'] ){ 4144 clearTimeout( this.timer['show'] ); 4145 } 4146 4147 if( $( this.wrapper ) ) 4148 { 4149 $( this.wrapper ).remove(); 4150 } 4151 }, 4152 4153 _show: function() 4154 { 4155 this.visible = true; 4156 4157 /* Experimental */ 4158 try 4159 { 4160 if ( this.options.warning ) 4161 { 4162 _wrap = this.inner.down('h3').next('div'); 4163 4164 if ( _wrap ) 4165 { 4166 if ( ! _wrap.className.match( /moderated/ ) ) 4167 { 4168 _wrap.addClassName('moderated'); 4169 } 4170 } 4171 } 4172 } catch(e){} 4173 4174 if( this.options.modal == false ){ 4175 new Effect.Appear( $( this.wrapper ), { duration: 0.3, afterFinish: function(){ 4176 if( Object.isFunction( this.callbacks['afterShow'] ) ) 4177 { 4178 this.callbacks['afterShow']( this ); 4179 } 4180 }.bind(this) } ); 4181 this.document_event = this.handleDocumentClick.bindAsEventListener(this); 4182 this.setDocumentEvent(); 4183 } else { 4184 new Effect.Appear( $('document_modal'), { duration: 0.3, to: this.options.modalOpacity, afterFinish: function(){ 4185 new Effect.Appear( $( this.wrapper ), { duration: 0.4, afterFinish: function(){ 4186 if( Object.isFunction( this.callbacks['afterShow'] ) ) 4187 { 4188 this.callbacks['afterShow']( this ); 4189 } 4190 }.bind(this) } ); 4191 }.bind(this) }); 4192 } 4193 }, 4194 4195 _hide: function() 4196 { 4197 this.visible = false; 4198 4199 if( this._tmpEvent != null ) 4200 { 4201 Event.stopObserving( $( this.wrapper ), 'mouseout', this._tmpEvent ); 4202 this._tmpEvent = null; 4203 } 4204 4205 if( this.options.modal == false ){ 4206 new Effect.Fade( $( this.wrapper ), { duration: 0.3, afterFinish: function(){ 4207 if( Object.isFunction( this.callbacks['afterHide'] ) ) 4208 { 4209 this.callbacks['afterHide']( this ); 4210 } 4211 }.bind(this) } ); 4212 } else { 4213 new Effect.Fade( $( this.wrapper ), { duration: 0.3, afterFinish: function(){ 4214 new Effect.Fade( $('document_modal'), { duration: 0.2, afterFinish: function(){ 4215 if( Object.isFunction( this.callbacks['afterHide'] ) ) 4216 { 4217 this.callbacks['afterHide']( this ); 4218 } 4219 }.bind(this) } ); 4220 }.bind(this) }); 4221 } 4222 }, 4223 4224 setDocumentEvent: function() 4225 { 4226 if( !ipb.vars['is_touch'] ){ 4227 Event.observe( document, 'click', this.document_event ); 4228 return; 4229 } 4230 4231 // Touch event 4232 Event.observe( document, 'touchstart', this.document_event ); 4233 }, 4234 4235 handleDocumentClick: function(e) 4236 { 4237 Debug.write( 'document click: ' + Event.element(e).id); 4238 4239 if( !Event.element(e).descendantOf( this.wrapper ) && ( this.options.attach && ( Event.element(e).id != this.options.attach.target.id ) ) ) 4240 { 4241 this.hide(e); 4242 } 4243 }, 4244 4245 update: function( content, evalScript ) 4246 { 4247 var scripts = []; 4248 4249 if( Object.isElement( content ) ){ 4250 this.inner.insert( { bottom: content } ); 4251 } else { 4252 // Get the scripts we'll need to execute 4253 scripts = content.extractScripts(); 4254 content = content.stripScripts(); 4255 4256 this.inner.update( content ); 4257 } 4258 4259 // Should this popup eval scripts? Default is YES 4260 if( ( Object.isUndefined( evalScript ) || evalScript != false ) && this.options.evalScript ){ 4261 scripts.map( function (script) { 4262 eval( script ); 4263 }); 4264 } 4265 }, 4266 4267 setUpBalloon: function() 4268 { 4269 // Are we attaching? 4270 if( this.options.attach ) 4271 { 4272 var attach = this.options.attach; 4273 4274 if( attach.target && $( attach.target ) ) 4275 { 4276 if( this.options.stem == true ) 4277 { 4278 this.createStem(); 4279 } 4280 4281 // Get position 4282 if( !attach.position ){ attach.position = 'auto'; } 4283 4284 if( isRTL ) 4285 { 4286 if( Object.isUndefined( attach.offset ) ){ attach.offset = { top: 0, right: 0 }; } 4287 if( Object.isUndefined( attach.offset.top ) ){ attach.offset.top = 0; } 4288 if( Object.isUndefined( attach.offset.left ) ){ attach.offset.right = 0; }else{ attach.offset.right = attach.offset.left; } 4289 } 4290 else 4291 { 4292 if( Object.isUndefined( attach.offset ) ){ attach.offset = { top: 0, left: 0 }; } 4293 if( Object.isUndefined( attach.offset.top ) ){ attach.offset.top = 0; } 4294 if( Object.isUndefined( attach.offset.left ) ){ attach.offset.left = 0; } 4295 } 4296 4297 if( attach.position == 'auto' ) 4298 { 4299 Debug.write("Popup: auto-positioning"); 4300 var screendims = document.viewport.getDimensions(); 4301 var screenscroll = document.viewport.getScrollOffsets(); 4302 var toff = $( attach.target ).viewportOffset(); 4303 var wrapSize = $( this.wrapper ).getDimensions(); 4304 var delta = [0,0]; 4305 4306 if (Element.getStyle( $( attach.target ), 'position') == 'absolute') 4307 { 4308 var parent = attach.target.getOffsetParent(); 4309 delta = parent.viewportOffset(); 4310 } 4311 4312 if( isRTL ) 4313 { 4314 toff['right'] = screendims.width - ( toff[0] - delta[0] ); 4315 } 4316 else 4317 { 4318 toff['left'] = toff[0] - delta[0]; 4319 } 4320 toff['top'] = toff[1] - delta[1] + screenscroll.top; 4321 4322 //Debug.write( toff['left'] + " " + toff['top'] ); 4323 // Need to figure out if it will be off-screen 4324 var start = 'top'; 4325 4326 if( isRTL ){ 4327 var end = 'right'; 4328 } else { 4329 var end = 'left'; 4330 } 4331 4332 //Debug.write( "Target offset top: " + toff.top + ", wrapSize Height: " + wrapSize.height + ", screenscroll top: " + screenscroll.top); 4333 if( ( toff.top - wrapSize.height - attach.offset.top ) < ( 0 + screenscroll.top ) ){ 4334 var start = 'bottom'; 4335 } 4336 4337 if( isRTL ) 4338 { 4339 if( ( toff.right + wrapSize.width - attach.offset.right ) < ( screendims.width - screenscroll.left ) ){ 4340 var end = 'left'; 4341 } 4342 } 4343 else 4344 { 4345 if( ( toff.left + wrapSize.width - attach.offset.left ) > ( screendims.width - screenscroll.left ) ){ 4346 var end = 'right'; 4347 } 4348 } 4349 4350 finalPos = this.position( start + end, { target: $( attach.target ), content: $( this.wrapper ), offset: attach.offset } ); 4351 4352 if( this.options.stem == true ) 4353 { 4354 finalPos = this.positionStem( start + end, finalPos ); 4355 } 4356 } 4357 else 4358 { 4359 Debug.write("Popup: manual positioning"); 4360 4361 finalPos = this.position( attach.position, { target: $( attach.target ), content: $( this.wrapper ), offset: attach.offset } ); 4362 4363 if( this.options.stem == true ) 4364 { 4365 finalPos = this.positionStem( attach.position, finalPos ); 4366 } 4367 } 4368 4369 // Add mouse events 4370 if( !Object.isUndefined( attach.event ) ){ 4371 $( attach.target ).observe( attach.event, this.show.bindAsEventListener( this ) ); 4372 4373 if( attach.event != 'click' && !Object.isUndefined( this.eventPairs[ attach.event ] ) ){ 4374 $( attach.target ).observe( this.eventPairs[ attach.event ], this.hide.bindAsEventListener( this ) ); 4375 } 4376 4377 $( this.wrapper ).observe( 'mouseover', this.wrapperEvent.bindAsEventListener( this ) ); 4378 } 4379 } 4380 } 4381 4382 if( isRTL ) 4383 { 4384 Debug.write("Popup: Right: " + finalPos.right + "; Top: " + finalPos.top); 4385 $( this.wrapper ).setStyle( 'top: ' + finalPos.top + 'px; right: ' + finalPos.right + 'px; position: absolute;' ); 4386 } 4387 else 4388 { 4389 Debug.write("Popup: Left: " + finalPos.left + "; Top: " + finalPos.top); 4390 $( this.wrapper ).setStyle( 'top: ' + finalPos.top + 'px; left: ' + finalPos.left + 'px; position: absolute;' ); 4391 } 4392 }, 4393 4394 wrapperEvent: function(e) 4395 { 4396 if( this.timer['hide'] ) 4397 { 4398 // Cancel event now 4399 clearTimeout( this.timer['hide'] ); 4400 this.timer['hide'] = null; 4401 4402 if( this.options.attach.event && this.options.attach.event == 'mouseover' ) 4403 { 4404 // Set new event to account for mouseout of the popup, 4405 // but only if we don't already have one - otherwise we get 4406 // expontentially more event calls. Bad. 4407 if( this._tmpEvent == null ){ 4408 this._tmpEvent = this.hide.bindAsEventListener( this ); 4409 $( this.wrapper ).observe('mouseout', this._tmpEvent ); 4410 } 4411 } 4412 } 4413 }, 4414 4415 positionStem: function( pos, finalPos ) 4416 { 4417 var stemSize = { height: 16, width: 31 }; 4418 var wrapStyle = {}; 4419 var stemStyle = {}; 4420 4421 switch( pos.toLowerCase() ) 4422 { 4423 case 'topleft': 4424 wrapStyle = { marginBottom: stemSize.height + 'px' }; 4425 4426 if( isRTL ) 4427 { 4428 stemStyle = { bottom: -(stemSize.height) + 'px', right: '5px' }; 4429 finalPos.right = finalPos.right - 15; 4430 } 4431 else 4432 { 4433 stemStyle = { bottom: -(stemSize.height) + 'px', left: '5px' }; 4434 finalPos.left = finalPos.left - 15; 4435 } 4436 break; 4437 case 'topright': 4438 wrapStyle = { marginBottom: stemSize.height + 'px' }; 4439 4440 if( isRTL ) 4441 { 4442 stemStyle = { bottom: -(stemSize.height) + 'px', left: '5px' }; 4443 finalPos.right = finalPos.right + 15; 4444 } 4445 else 4446 { 4447 stemStyle = { bottom: -(stemSize.height) + 'px', right: '5px' }; 4448 finalPos.left = finalPos.left + 15; 4449 } 4450 break; 4451 case 'bottomleft': 4452 wrapStyle = { marginTop: stemSize.height + 'px' }; 4453 4454 if( isRTL ) 4455 { 4456 stemStyle = { top: -(stemSize.height) + 'px', right: '5px' }; 4457 finalPos.right = finalPos.right - 15; 4458 } 4459 else 4460 { 4461 stemStyle = { top: -(stemSize.height) + 'px', left: '5px' }; 4462 finalPos.left = finalPos.left - 15; 4463 } 4464 break; 4465 case 'bottomright': 4466 wrapStyle = { marginTop: stemSize.height + 'px' }; 4467 4468 if( isRTL ) 4469 { 4470 stemStyle = { top: -(stemSize.height) + 'px', left: '5px' }; 4471 finalPos.right = finalPos.right + 15; 4472 } 4473 else 4474 { 4475 stemStyle = { top: -(stemSize.height) + 'px', right: '5px' }; 4476 finalPos.left = finalPos.left + 15; 4477 } 4478 break; 4479 } 4480 4481 $( this.wrapper ).setStyle( wrapStyle ); 4482 $( this.stem ).setStyle( stemStyle ).setStyle('z-index: 6000').addClassName( pos.toLowerCase() ); 4483 4484 return finalPos; 4485 }, 4486 4487 position: function( pos, v ) 4488 { 4489 finalPos = {}; 4490 4491 v.target.identify(); 4492 4493 var toff = $( v.target.id ).viewportOffset(); 4494 var tsize = $( v.target.id ).getDimensions(); 4495 var wrapSize = $( v.content ).getDimensions(); 4496 var screenscroll = document.viewport.getScrollOffsets(); 4497 var offset = v.offset; 4498 var delta = [0,0]; 4499 4500 if (Element.getStyle( $( v.target.id ), 'position') == 'absolute') 4501 { 4502 var parent = $( v.target.id ).getOffsetParent(); 4503 delta = parent.viewportOffset(); 4504 4505 /* That above doesn't help */ 4506 delta = [0,0]; 4507 4508 } 4509 4510 if( isRTL ) 4511 { 4512 toff['right'] = document.viewport.getDimensions().width - ( toff[0] - delta[0] ); 4513 } 4514 else 4515 { 4516 toff['left'] = toff[0] - delta[0]; 4517 } 4518 4519 toff['top'] = toff['top'] - delta[1] + screenscroll.top; 4520 4521 switch( pos.toLowerCase() ) 4522 { 4523 case 'topleft': 4524 finalPos.top = ( toff.top - wrapSize.height - ( tsize.height / 2 ) ) - offset.top; 4525 4526 if( isRTL ) 4527 { 4528 finalPos.right = toff.right + offset.right; 4529 } 4530 else 4531 { 4532 finalPos.left = toff.left + offset.left; 4533 } 4534 break; 4535 case 'topright': 4536 finalPos.top = ( toff.top - wrapSize.height - ( tsize.height / 2 ) ) - offset.top; 4537 4538 if( isRTL ) 4539 { 4540 finalPos.right = ( toff.right - ( wrapSize.width - tsize.width ) ) - offset.right; 4541 } 4542 else 4543 { 4544 finalPos.left = ( toff.left - ( wrapSize.width - tsize.width ) ) - offset.left; 4545 } 4546 break; 4547 case 'bottomleft': 4548 finalPos.top = ( toff.top + tsize.height ) + offset.top; 4549 4550 if( isRTL ) 4551 { 4552 finalPos.right = toff.right + offset.right; 4553 } 4554 else 4555 { 4556 finalPos.left = toff.left + offset.left; 4557 } 4558 break; 4559 case 'bottomright': 4560 finalPos.top = ( toff.top + tsize.height ) + offset.top; 4561 4562 if( isRTL ) 4563 { 4564 finalPos.right = ( toff.right - ( wrapSize.width - tsize.width ) ) - offset.right; 4565 } 4566 else 4567 { 4568 finalPos.left = ( toff.left - ( wrapSize.width - tsize.width ) ) - offset.left; 4569 } 4570 break; 4571 } 4572 4573 return finalPos; 4574 }, 4575 4576 createStem: function() 4577 { 4578 this.stem = new Element('div', { id: this.id + '_stem' } ).update(' ').addClassName('stem'); 4579 this.wrapper.insert( { top: this.stem } ); 4580 }, 4581 4582 setUpPane: function() 4583 { 4584 // Does the document have a modal blackout? 4585 if( !$('document_modal') ){
4586 this.createDocumentModal(); 4587 } 4588 4589 this.positionPane(); 4590 }, 4591 4592 positionPane: function() 4593 { 4594 // Position it in the middle 4595 var elem_s = $( this.wrapper ).getDimensions(); 4596 var window_s = document.viewport.getDimensions(); 4597 var window_offsets = document.viewport.getScrollOffsets(); 4598 4599 if( ipb.vars['is_touch'] ){ 4600 // #35826: On some skins, webkit will handle the document viewport sizes incorrectly. 4601 // To try and protect against this, we'll use the actual screen size for these 4602 // calculations. 4603 window_s = { width: window.innerWidth, height: window.innerHeight }; 4604 } 4605 4606 if( isRTL ) 4607 { 4608 var center = { right: ((window_s['width'] - elem_s['width']) / 2), 4609 top: (((window_s['height'] - elem_s['height']) / 2)/2) 4610 }; 4611 4612 if( center.top < 10 ){ center.top = 10; } 4613 4614 $( this.wrapper ).setStyle('top: ' + center['top'] + 'px; right: ' + center['right'] + 'px; position: fixed;'); 4615 4616 } 4617 else 4618 { 4619 var center = { left: ((window_s['width'] - elem_s['width']) / 2), 4620 top: (((window_s['height'] - elem_s['height']) / 2)/2) 4621 }; 4622 4623 if( center.top < 10 ){ center.top = 10; } 4624 4625 $( this.wrapper ).setStyle('top: ' + center['top'] + 'px; left: ' + center['left'] + 'px; position: fixed;'); 4626 } 4627 }, 4628 4629 createDocumentModal: function() 4630 { 4631 var pageLayout = $( document.body ).getLayout(); 4632 var pageSize = { width: pageLayout.get('width'), height: pageLayout.get('margin-box-height') }; 4633 var viewSize = document.viewport.getDimensions(); 4634 4635 var dims = []; 4636 4637 Debug.dir( pageSize ); 4638 Debug.dir( viewSize ); 4639 4640 if( viewSize['height'] < pageSize['height'] ){ 4641 dims['height'] = pageSize['height']; 4642 } else { 4643 dims['height'] = viewSize['height']; 4644 } 4645 4646 if( viewSize['width'] < pageSize['width'] ){ 4647 dims['width'] = pageSize['width']; 4648 } else { 4649 dims['width'] = viewSize['width']; 4650 } 4651 4652 var modal = new Element( 'div', { 'id': 'document_modal' } ).addClassName('modal').hide(); 4653 4654 if( isRTL ){ 4655 modal.setStyle('width: ' + dims['width'] + 'px; height: ' + dims['height'] + 'px; position: fixed; top: 0px; right: 0px; z-index: 10000;'); 4656 } else { 4657 modal.setStyle('width: ' + dims['width'] + 'px; height: ' + dims['height'] + 'px; position: fixed; top: 0px; left: 0px; z-index: 10000;'); 4658 } 4659 4660 $$('body')[0].insert( modal ); 4661 }, 4662 4663 getObj: function() 4664 { 4665 return $( this.wrapper ); 4666 } 4667}); 4668 4669/************************************************/ 4670/* IPB3 Javascript */ 4671/* -------------------------------------------- */ 4672/* ips.ticker.js - Popup creator */ 4673/* (c) IPS, Inc 2008 */ 4674/* -------------------------------------------- */ 4675/* Author: Rikki Tissier */ 4676/************************************************/ 4677_ticker = window.IPBoard; 4678_ticker.prototype.Ticker = Class.create({ 4679 initialize: function( root, options, callbacks ) 4680 { 4681 if( !$(root) ){ 4682 return; 4683 } 4684 this.root = $(root); 4685 this.options = Object.extend({ 4686 duration: 4, 4687 select: "li" 4688 }, options || {}); 4689 4690 this.items = $( root ).select( this.options.select ); 4691 if( !this.items.length ){ return; } 4692 4693 // Hide all items except first 4694 this.items.invoke('hide').first().show(); 4695 4696 // Start timer 4697 this.timer = this.nextItem.bind( this ).delay( this.options.duration ); 4698 4699 // Pause event 4700 $( this.root ).observe('mouseenter', this.pauseTicker.bindAsEventListener( this ) ); 4701 $( this.root ).observe('mouseleave', this.unpauseTicker.bindAsEventListener( this ) ); 4702 }, 4703 pauseTicker: function(e) 4704 { 4705 clearTimeout( this.timer ); 4706 }, 4707 unpauseTicker: function(e) 4708 { 4709 this.timer = this.nextItem.bind(this).delay( this.options.duration ); 4710 }, 4711 nextItem: function() 4712 { 4713 // Find current item 4714 var cur = this.items.find( function(elem){ 4715 return elem.visible(); 4716 }); 4717 4718 var next = $( cur ).next( this.options.select ); 4719 4720 if( Object.isUndefined( next ) ){ 4721 next = this.items.first(); 4722 } 4723 4724 // Fade current 4725 new Effect.Fade( $( cur ), { duration: 0.4, queue: 'end', afterFinish: function(){ 4726 new Effect.Appear( $( next ), { duration: 0.8, queue: 'end' } ); 4727 } } ); 4728 4729 // Reset timer 4730 this.timer = this.nextItem.bind( this ).delay( this.options.duration ); 4731 } 4732}); 4733 4734function warningPopup( elem, id ) 4735{ 4736 var _url = ipb.vars['base_url'] + '&app=members&module=ajax&secure_key='
4736+ ipb.vars['secure_hash'] + '§ion=warnings&id=' + id; 4737 new ipb.Popup( 'warning' + id, { 4738 type: 'balloon', 4739 stem: true, 4740 attach: { target: elem, position: 'auto' }, 4741 hideAtStart: false, 4742 ajaxURL: _url, 4743 w: '600px', 4744 h: 800 4745 }); 4746} 4747 4748ipb = new IPBoard; 4749ipb.global.init(); 4750ipb.menus.init();
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.