1/* ========================================================== 2 * api.js 3 * http://optinmonster.com/ 4 * ========================================================== 5 * Copyright 2014 Thomas Griffin. 6 * 7 * Use of this file is bound by the Terms of Service agreed 8 * upon when purchasing OptinMonster. 9 * http://optinmonster.com/terms/ 10 * ========================================================== */ 11// Global variable to hold optins and determine their visibility. 12var OptinMonsterOptins = {}; 13 14// Main OptinMonster API class for handling optin operations. 15function OptinMonster(){ 16 // Holds all of the class properties. 17 this.public = {}, 18 19 // The init method that loads the object class. 20 this.init = function(params){ 21 // Set the optin properties. 22 for ( key in params ) { 23 this.public[key] = params[key]; 24 } 25 26 // Only check for popup-type optins. 27 if ( 'sidebar' !== this.getProp('type') && 'post' !== this.getProp('type') ) { 28 // Only check against cookies if this is in live mode. 29 if ( ! this.getProp('preview') ) { 30 // Only process if this is a normal loading optin. If manually triggered, ignore cookie settings. 31 if ( ! this.getProp('click') ) { 32 // If cookies are not enabled, we can't reliably determine optin visibility, so return early. 33 if ( ! this.cookiesEnabled() ) { 34 if ( ! this.getProp('test') ) { 35 return; 36 } 37 } 38 39 // If we have a cookie set and it is valid, bail out and do nothing. 40 var global_cookie = this.getCookie('om-global-cookie'), 41 cookie = this.getCookie('om-' + this.getProp('id')), 42 second = this.getProp('second'), 43 test = this.getProp('test'), 44 type = this.getProp('type'); 45 46 // If on a mobile device and not a mobile type of optin, return early regardless of any other settings. 47 if ( this.isMobile() && ! this.getProp('mobile') ) { 48 return; 49 } 50 51 // If not on a mobile device but a mobile optin is present, return early. 52 if ( ! this.isMobile() && this.getProp('mobile') ) { 53 return; 54 } 55 56 // Check for 2nd pageview, and return early if it is the first pageview (meaning the "second" cookie does not exist yet). 57 if ( second && ! test ) { 58 if ( ! this.getCookie('om-second-' + this.getProp('id')) ) { 59 return this.createCookie('om-second-' + this.getProp('id'), true, this.getProp('cookie')); 60 } 61 } 62 63 // Now run our other optin-specific checks. 64 if ( global_cookie || cookie ) { 65 if ( ! test ) { 66 if ( 'slide' !== type ) { 67 return; 68 } 69 } 70 } 71 } 72 } 73 } 74 75 // Load the optin now that the checks are complete. 76 this.run(); 77 }, 78 79 // Loads the optin. 80 this.run = function(){ 81 // Prepare variables. 82 var self = this, 83 type = self.getProp('type'); 84 85 // If this is a slide-in, set a handler to toggle its current visible state. 86 if ( 'slide' == type ) { 87 this.setProp('slide_open', false); 88 } 89 90 // Store the optin in the global variable and set its visibility to false by default. 91 OptinMonsterOptins[this.getProp('optin_js')] = { type: type, visible: false }; 92 93 // Possibly load jQuery and handle the rest of the output based on response. 94 this.loadjQuery(); 95 }, 96 97 // Checks to see if jQuery is loaded and if it is a high enough version; if not, jQuery is loaded. 98 this.loadjQuery = function(){ 99 // Store localized copy of our main object instance. 100 var self = this, 101 loaded = false;
102 103 // If jQuery is not present or not the correct version, load it asynchronously and fire the rest of the app once jQuery has loaded. 104 if ( window.jQuery === undefined ) { 105 var om = document.createElement('script'); 106 om.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'; 107 om.onload = om.onreadystatechange = function(){ 108 var s = this.readyState; 109 if (s) if (s != 'complete') if (s != 'loaded') return; 110 try { 111 if ( ! loaded ) { 112 self.loadjQueryHandler(false); 113 loaded = true; 114 } 115 } catch(e){} 116 }; 117 118 // Attempt to append it to the <head>, otherwise append to the document. 119 (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(om); 120 } else if ( window.jQuery.fn.jquery !== '1.11.1' ) { 121 // Set the window jQuery property for event triggering. 122 this.public.ejQuery = window.jQuery; 123 124 // jQuery exists, but it is not the version we want, so we need to manage duplicate jQuery objects. 125 var om = document.createElement('script'); 126 om.src = 'http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js'; 127 om.onload = om.onreadystatechange = function(){ 128 var s = this.readyState; 129 if (s) if (s != 'complete') if (s != 'loaded') return; 130 try { 131 if ( ! loaded ) { 132 self.loadjQueryHandler(true); 133 loaded = true; 134 } 135 } catch(e){} 136 }; 137 138 // Attempt to append it to the <head>, otherwise append to the document. 139 (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(om); 140 } else { 141 // The version of jQuery loaded into the window is what we want to use, so we can just load the handler and output content. 142 self.public.$ = self.public.ejQuery = window.jQuery; 143 self.loadApp(); 144 } 145 }, 146 147 // Stores a localized copy of jQuery and removes any potential conflicts with other jQuery libraries. 148 this.loadjQueryHandler = function(exists){ 149 // If jQuery already exists, don't overwrite the global but set noConflict to properly handle multiple versions. 150 if ( exists ) { 151 // Don't set global jQuery object - just store in our property. 152 this.public.$ = window.jQuery.noConflict(true); 153 this.loadApp(); 154 } else { 155 // Store the global jQuery object since it does not exist yet. 156 jQuery = window.jQuery.noConflict(true); 157 this.public.$ = this.public.ejQuery = jQuery; 158 this.loadApp(); 159 } 160 }, 161 162 // Callback to load the content of our app into the page. 163 this.loadApp = function(){ 164 // Store localized copy of our main object instance. 165 var self = this; 166 167 // Woot, we now have jQuery! Run our application inside of our jQuery callback to have full access to jQuery. 168 self.public.$(document).ready(function($){ 169 self.runOptinMonster(); 170 }); 171 }, 172 173 // Runs the OptinMonster JS engine! 174 this.runOptinMonster = function(){ 175 // Store localized copy of our main object instance. 176 var self = this, 177 click = self.getProp('click'); 178 179 // Trigger event to say OptinMonster is initialized. 180 self.trigger('OptinMonsterInit'); 181 182 // Initialize based on click or normal load. 183 if ( click ) { 184 self.manual(); 185 } else { 186 self.load(); 187 } 188 }, 189 190 // Processes loading optins in manually via MonsterLinks. 191 this.manual = function(){ 192 // Prepare variables. 193 var self = this, 194 data = { 195 optin_monster_ajax_action: 'get_optinmonster', 196 slug: self.getProp('optin') 197 }, 198 success = self.manualSuccess(self), 199 error = self.manualError(self); 200 201 // Make the request to retrieve the optin. 202 self.request(data, success, error); 203 }, 204 205 // Processes the success callback for manually loading an optin on click. 206 this.manualSuccess = function(self){ 207 // Prepare variables. 208 var $ = self.getProp('$'), 209 loaded = false;
210 211 // Return the callback. 212 return function(params){ 213 // Set the optin properties. 214 for ( key in params.success ) { 215 self.public[key] = params.success[key]; 216 } 217 218 // Set the global optin object with the proper type and set some property values (we want this to load immediately). 219 OptinMonsterOptins[self.getProp('optin_js')].type = self.getProp('type'); 220 self.setProp('delay', 0); 221 self.setProp('exit', false); 222 223 // Load the WebFont loader and then proceed with the optin output. 224 var wf = document.createElement('script'); 225 wf.src = 'http://ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js'; 226 wf.onload = wf.onreadystatechange = function(){ 227 var s = this.readyState; 228 if (s) if (s != 'complete') if (s != 'loaded') return; 229 try { 230 // Only apply once. 231 if ( ! loaded ) { 232 // Place the optin HTML output into the DOM. 233 $('body').append(self.getProp('html')); 234 235 // Trigger event to say OptinMonster has been loaded successfully via click. 236 self.trigger('OptinMonsterManualOptinSuccess'); 237 238 // Now proceed with loading the optin. 239 self.load(); 240 241 // Set the loaded flag to true. 242 loaded = true; 243 } 244 } catch(e){} 245 }; 246 247 // Attempt to append it to the <head>, otherwise append to the document. 248 (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(wf); 249 } 250 }, 251 252 // Processes the error callback for manually loading an optin on click. 253 this.manualError = function(self){ 254 // Return the callback. 255 return function(err, xhr, message){ 256 // Trigger event to say OptinMonster has encountered an error processing the optin. 257 self.trigger('OptinMonsterManualOptinError'); 258 }; 259 }, 260 261 // Loads an optin. 262 this.load = function(){ 263 // Prepare variables. 264 var self = this; 265 266 // Sanitize the form. 267 self.sanitize(); 268 269 // Runs fixes for IE. 270 self.iehotfix(); 271 272 // Open the optin. 273 self.open(); 274 }, 275 276 // Sanitize form fields to ensure they will submit properly. 277 this.sanitize = function(){ 278 // Prepare variables. 279 var self = this, 280 $ = self.getProp('$'), 281 form_html = $('#om-' + self.getProp('optin')), 282 submit_field = $('#om-' + self.getProp('optin')).find(':submit'), 283 name_attr = submit_field.attr('name'), 284 name_id = submit_field.attr('id'); 285 286 // Fix any name attributes on the submit button. 287 if ( 'submit' == name_attr ) { 288 form_html.find(':submit').attr('name', 'submit-om'); 289 } 290 291 // Fix any ID attributes on the submit button. 292 if ( 'submit' == name_id ) { 293 form_html.find(':submit').attr('id', 'submit-om'); 294 } 295 }, 296 297 // Adds hotfixes for IE bugs. 298 this.iehotfix = function(){ 299 // If not IE, return early. 300 if ( ! this.isIE() ) { 301 return; 302 } 303 304 // Load the placeholder shim for IE. 305 this.loadPlaceholder(); 306 307 // Apply the placeholder shim. 308 this.doPlaceholder(); 309 }, 310 311 // Opens the optin. 312 this.open = function(onclick){ 313 // Prepare variables. 314 var self = this, 315 exit = self.getProp('exit'), 316 click = self.getProp('click'), 317 optin_js = self.getProp('optin_js'), 318 delay = 'slide' == self.getProp('type') ? 0 : self.getProp('delay'), 319 onclick = onclick || false; 320 321 // If the popup is already opened, don't do anything. 322 if ( OptinMonsterOptins.hasOwnProperty(optin_js) && true === OptinMonsterOptins[optin_js].visible ) { 323 return; 324 } 325 326 // Set delay to 0 if forced click or exit intent. 327 if ( onclick || exit ) {
328 delay = 0; 329 } 330 331 // Only load if not a manual click optin. 332 if ( ! click || onclick ) { 333 // Wrap everything in a timeout to catch any optin loading delays. 334 setTimeout(function(){ 335 // Possibly append the optin holder to the body and style. 336 if ( 'mobile' !== self.getProp('type') ) { 337 self.appendHolder(); 338 } 339 340 // If we have a custom HTML optin, we need to prepare it now. 341 if ( self.getProp('custom') ) { 342 self.prepareCustomOptin(); 343 } 344 345 // Trigger event that OptinMonster is now fully loaded and about to fire the open mechanism. 346 self.trigger('OptinMonsterLoaded'); 347 348 // Handle the type of open. 349 if ( exit && ! click && ! onclick ) { 350 self.exitOpen(); 351 } else { 352 self.normalOpen(); 353 } 354 }, delay || 0); 355 } 356 }, 357 358 // Handle the opening of exit intent optins. 359 this.exitOpen = function(){ 360 // Prepare variables. 361 var self = this, 362 $ = self.getProp('$'), 363 optin_js = self.getProp('optin_js'), 364 opened = false; 365 366 // Load the exit intent handler. 367 $(document).on('mouseleave', function(e){ 368 // Only trigger if leaving near the top of the page, a cookie has not been set or the optin is not yet open. 369 if ( e.clientY > (self.getProp('exit_sensitivity') || 20) || self.getCookie('om-' + self.getProp('id')) || self.getCookie('om-global-cookie') || OptinMonsterOptins.hasOwnProperty(optin_js) && true === OptinMonsterOptins[optin_js].visible || opened ) { 370 return; 371 } 372 373 // Set the flag that the optin has been opened once and cannot be opened again, 374 opened = true; 375 376 // Show (and possibly animate showing) the optin. 377 self.show(true); 378 }); 379 }, 380 381 // Handle the opening of normal optins. 382 this.normalOpen = function(){ 383 // Prepare variables. 384 var self = this, 385 type = self.getProp('type'), 386 $ = self.getProp('$'); 387 388 // Show (and possibly animate showing) the optin. 389 // Make a check for mobile optins to check the window scrolling feature first before showing the optin. 390 if ( 'mobile' == type && ! self.getProp('preview') ) { 391 self.setProp('dw', $(document).width()); 392 if ( ! $(window).scrollTop() ) { 393 self.show(); 394 } else { 395 $(window).on('scroll.omMobile', function(){ 396 clearTimeout($.data(this, 'omScrollTimer')); 397 $.data(this, 'omScrollTimer', setTimeout(function(){ 398 self.show(); 399 }, 300)); 400 }); 401 } 402 } else { 403 self.show(); 404 } 405 }, 406 407 // Handle actually displaying the optin. 408 this.show = function(exit){ 409 // Prepare variables. 410 var self = this, 411 $ = self.getProp('$'), 412 id = self.getProp('id'), 413 optin = self.getProp('optin'), 414 optin_js = self.getProp('optin_js'), 415 type = self.getProp('type'), 416 theme = self.getProp('theme'), 417 preview = self.getProp('preview'), 418 exit = exit || false; 419 420 // If the popup is already opened, don't do anything. 421 if ( OptinMonsterOptins.hasOwnProperty(optin_js) && true === OptinMonsterOptins[optin_js].visible ) { 422 return; 423 } 424 425 // If the global cookie has been set at some point between the time loading the page and actually showing this optin (and this is not a click event or preview), don't show it. 426 if ( self.getCookie('om-global-cookie') && ! self.getProp('click') && ! preview ) { 427 if ( 'sidebar' !== type && 'post' !== type ) { 428 return; 429 } 430 } 431 432 // Trigger event to say OptinMonster is about to be shown. 433 self.trigger('OptinMonsterBeforeShow'); 434 435 // Determine the type of loading pattern based on exit intent or not. 436 if ( exit ) { 437 // Only process for lightbox and canvas optin types. 438 if ( 'lightbox' == type || 'canvas' == type ) { 439 // Check optin visibility to ensure no other popup optins are showing on the page at the same time. 440 if ( ! self.hasVisiblePopup() ) { 441 // Immediately display the optin. 442 $('#om-' + optin).show().css('display', 'block'); 443 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').show().css('display', 'block'); 444 self.positionOptin(); 445 446 // Set the visibility of the optin to open. 447 OptinMonsterOptins[self.getProp('optin_js')].visible = true; 448 449 // Fix focus jumping. 450 self.fixFocusJumping(); 451 452 // Trigger event to say OptinMonster is now open. 453 self.trigger('OptinMonsterOnShow'); 454 } 455 } else if ( 'footer' == type || 'slide' == type ) {
456 // If an optin is successful with the slide-in, don't show it at all. 457 if ( ! self.getCookie('om-' + id + '-closed') ) { 458 // Make the optin visible. 459 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').show().css('display', 'block'); 460 var position = preview ? 78 : 0; 461 462 // Display the optin. 463 $('#om-' + optin).css('bottom', '-' + $('#om-' + optin).outerHeight() + 'px').show().animate({ 'bottom' : parseInt(position) }, 300, function(){ 464 // Set the visibility of the optin to open. 465 OptinMonsterOptins[self.getProp('optin_js')].visible = true; 466 467 // Trigger event to say OptinMonster is now open. 468 self.trigger('OptinMonsterOnShow'); 469 470 // Do some extra custom opening/closing magic for slide-in optins. 471 if ( 'slide' == type ) { 472 // Register open/close handlers now. 473 self.slideHandlers(); 474 475 setTimeout(function(){ 476 self.showSlide(); 477 }, 0); 478 } 479 }); 480 } 481 } 482 } else { 483 if ( 'lightbox' == type || 'canvas' == type || 'mobile' == type ) { 484 // Check optin visibility to ensure no other popup optins are showing on the page at the same time. 485 if ( ! self.hasVisiblePopup() ) { 486 if ( 'mobile' == type ) { 487 // Style other items necessary for mobile optins. 488 $('#om-' + optin + ', #om-' + optin + '-overlay').appendTo('body'); 489 $('#om-' + optin + '-overlay').height($(document).height()).show().css('display', 'block'); 490 $('#om-' + optin).show().css('display', 'block'); 491 492 // Fix scaling. 493 if ( ! preview ) { 494 self.fixMobileScaling(); 495 } 496 497 // Fade the optin into view. 498 $('#om-' + type + '-' + theme + '-optin').hide().fadeIn(300, function(){ 499 // Set the proper position of the optin. 500 $('#om-' + optin).css('top', $(document).scrollTop()); 501 502 // Set the visibility of the optin to open. 503 OptinMonsterOptins[self.getProp('optin_js')].visible = true; 504 505 // Trigger event to say OptinMonster is now open. 506 self.trigger('OptinMonsterOnShow'); 507 }); 508 } else { 509 // Fade the optin into view. 510 $('#om-' + optin).fadeIn(300, function(){ 511 // Show the optin. 512 $(this).find('#om-' + type + '-' + theme + '-optin').show().css('display', 'block'); 513 self.positionOptin(); 514 515 // Set the visibility of the optin to open. 516 OptinMonsterOptins[self.getProp('optin_js')].visible = true; 517 518 // Fix focus jumping. 519 self.fixFocusJumping(); 520 521 // Trigger event to say OptinMonster is now open. 522 self.trigger('OptinMonsterOnShow'); 523 }); 524 } 525 } 526 } else if ( 'footer' == type || 'slide' == type ) { 527 // If an optin is successful with the slide-in, don't show it at all. 528 if ( ! self.getCookie('om-' + id + '-closed') || preview ) { 529 // Make the optin visible. 530 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').show().css('display', 'block'); 531 var position = preview ? 78 : 0; 532 533 // Display the optin. 534 $('#om-' + optin).css('bottom', '-' + $('#om-' + optin).outerHeight() + 'px').show().animate({ 'bottom' : parseInt(position) }, 300, function(){ 535 // Set the visibility of the optin to open. 536 OptinMonsterOptins[self.getProp('optin_js')].visible = true; 537 538 // Trigger event to say OptinMonster is now open. 539 self.trigger('OptinMonsterOnShow'); 540 541 // Do some extra custom opening/closing magic for slide-in optins. 542 if ( 'slide' == type ) { 543 // Register open/close handlers now. 544 self.slideHandlers(); 545 546 // Open the slide-in optin. 547 setTimeout(function(){ 548 self.showSlide(); 549 }, self.getProp('delay') || 0); 550 } 551 }); 552 } 553 } else { 554 // Trigger event to say OptinMonster is now open. 555 self.trigger('OptinMonsterOnShow'); 556 } 557 } 558 559 // Register the submit handler. 560 self.submit(); 561 562 // Register the close handler. 563 self.close(); 564 565 // Track the impression stats. 566 self.track(); 567 }, 568 569 // Fixes mobile scaling. 570 this.fixMobileScaling = function(){ 571 // Prepare variables. 572 var self = this, 573 type = this.getProp('type'), 574 optin = this.getProp('optin'), 575 $ = this.getProp('$'); 576 577 // If the meta viewport tag exists, return. This is a responsive site. 578 var meta_exists = $('meta[name="viewport"]'); 579 if ( meta_exists.length > 0 ) { 580 return; 581 } 582 583 // Append our tag and start doing things. 584 $('head').append('<meta id="optin-monster-viewport" name="viewport" content="width=device-width, initial-scale=1.0">'); 585 $('html, body').css('overflow', 'hidden'); 586 }, 587
588 // Fixes focus jumping. 589 this.fixFocusJumping = function(){ 590 // Prepare variables. 591 var self = this, 592 optin = self.getProp('optin'), 593 type = self.getProp('type'), 594 $ = self.getProp('$'), 595 pos = $('body').css('position'); 596 597 // If this is not a mobile device, do nothing. 598 if ( ! self.isMobile() ) { 599 return; 600 } 601 602 // Do nothing if not the proper optin types. 603 if ( 'lightbox' !== type && 'canvas' !== type ) { 604 return; 605 } 606 607 // Store current position of window. 608 self.setProp('dt', $(document).scrollTop()); 609 610 // Fix focus scrolling. 611 $('html, body').css('overflow', 'hidden'); 612 window.scrollTo(0, 0); 613 614 // Prevent window shifting on input, select and textarea blurs. 615 $(document).on('focus.omFocusJump', 'input, select, textarea', function(){ 616 $('#om-' + optin).css({ position: 'absolute', height: $(document).height() }); 617 }).on('blur.omFocusJump', 'input, select, textarea', function(){ 618 $('#om-' + optin).css({ position: 'fixed', height: '100%' }); 619 $('html, body').css('overflow', 'hidden'); 620 }); 621 }, 622 623 // Positions an optin according to the optin type. 624 this.positionOptin = function(){ 625 // Prepare variables. 626 var self = this, 627 $ = self.getProp('$'), 628 id = self.getProp('id'), 629 optin = self.getProp('optin'), 630 type = self.getProp('type'), 631 theme = self.getProp('theme'), 632 preview = self.getProp('preview'), 633 width = $('#om-' + optin + ' .om-theme-' + theme).width(), 634 height = $('#om-' + optin + ' .om-theme-' + theme).height(), 635 wheight = $(window).height(), 636 dims = preview ? ((wheight - height) / 2) - 39 : (wheight - height) / 2; 637 638 // Position based on type and if the optin is taller than the window or not. 639 if ( height >= wheight ) { 640 if ( 'lightbox' == type || 'canvas' == type ) { 641 $('#om-' + optin).css({ overflow: 'scroll' }); 642 $('#om-' + optin + ' .optin-monster-powered-by').css({ bottom: -55, paddingBottom: 20 }); 643 $('#om-' + optin + ' .om-theme-' + theme + ', #om-' + optin + ' .optin-monster-success-overlay').css({ 644 top: 20, 645 left: ($(window).width() - width) / 2, 646 position: 'absolute' 647 }); 648 $(window).resize(function(){ 649 $('#om-' + optin).css({ overflow: 'scroll' }); 650 $('#om-' + optin + ' .optin-monster-powered-by').css({ bottom: -55, paddingBottom: 20 }); 651 $('#om-' + optin + ' .om-theme-' + theme + ', #om-' + optin + ' .optin-monster-success-overlay').css({ 652 top: 20, 653 left: ($(window).width() - $('#om-' + optin + ' .om-theme-' + theme).width()) / 2, 654 position: 'absolute' 655 }); 656 }); 657 658 // Trigger event to say OptinMonster has now been positioned. 659 self.trigger('OptinMonsterPositionOptin'); 660 } else { 661 // Trigger event to say OptinMonster has now been positioned. 662 self.trigger('OptinMonsterPositionOptin'); 663 } 664 } else { 665 if ( 'lightbox' == type || 'canvas' == type ) { 666 $('#om-' + optin + ' .om-theme-' + theme + ', #om-' + optin + ' .optin-monster-success-overlay').css({ 667 top: dims, 668 left: ($(window).width() - width) / 2 669 }); 670 $(window).resize(function(){ 671 var height = $('#om-' + optin + ' .om-theme-' + theme).height(), 672 wheight = $(window).height(); 673 if ( height >= wheight ) { 674 $('#om-' + optin).css({ overflow: 'scroll' }); 675 $('#om-' + optin + ' .optin-monster-powered-by').css({ bottom: -55, paddingBottom: 20 }); 676 $('#om-' + optin + ' .om-theme-' + theme + ', #om-' + optin + ' .optin-monster-success-overlay').css({ 677 top: 20, 678 left: ($(window).width() - $('#om-' + optin + ' .om-theme-' + theme).width()) / 2, 679 position: 'absolute' 680 }); 681 } else { 682 dims = preview ? (($(window).height() - $('#om-' + optin + ' .om-theme-' + theme).height()) / 2) - 39 : ($(window).height() - $('#om-' + optin + ' .om-theme-' + theme).height()) / 2; 683 $('#om-' + optin + ' .om-theme-' + theme + ', #om-' + optin + ' .optin-monster-success-overlay').css({ 684 top: dims, 685 left: ($(window).width() - $('#om-' + optin + ' .om-theme-' + theme).width()) / 2 686 }); 687 } 688 }); 689 690 // Trigger event to say OptinMonster has now been positioned. 691 self.trigger('OptinMonsterPositionOptin'); 692 } else { 693 // Trigger event to say OptinMonster has now been positioned. 694 self.trigger('OptinMonsterPositionOptin'); 695 } 696 } 697 }, 698 699 // Registers the open/close slide-in handlers. 700 this.slideHandlers = function(){ 701 // Prepare variables. 702 var self = this, 703 $ = self.getProp('$'), 704 id = self.getProp('id'), 705 optin = self.getProp('optin'), 706 type = self.getProp('type'), 707 theme = self.getProp('theme'); 708 709 // Register handler to close the optin. 710 $(document).on('click.closeOptin', '#om-' + optin + ' .om-slide-close-content, #om-' + optin + ' .om-close', function(e){ 711 // Don't allow event bubbling. 712 if ( e.target !== this ) { 713 return; 714 } 715 716 e.preventDefault(); 717 718 // Trigger event to say OptinMonster is about to close. 719 self.trigger('OptinMonsterBeforeClose'); 720 721 // Set the optin to closed. 722 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').removeClass('om-slide-open').addClass('om-slide-closed'); 723 724 // Remove any success messages. 725 $('#om-' + optin).find('.optin-monster-success-overlay').remove(); 726 727 // Run the cleanup method. 728 self.cleanup(); 729 }); 730 731 // Register handler to open the optin. 732 $(document).on('click.openOptin', '#om-' + optin + ' .om-slide-open-content', function(e){ 733 // Don't allow event bubbling. 734 if ( e.target !== this ) { 735 return; 736 } 737 738 e.preventDefault(); 739 740 // Trigger event to say OptinMonster is about to open. 741 self.trigger('OptinMonsterBeforeOpen'); 742 743 // Set the optin to open. 744 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').removeClass('om-slide-closed').addClass('om-slide-open'); 745 746 // Set the visibility to open. 747 self.setProp('slide_open', true); 748 }); 749 }, 750 751 // Displays the slide-in optin. 752 this.showSlide = function(){ 753 // Prepare variables. 754 var self = this, 755 $ = self.getProp('$'), 756 id = self.getProp('id'), 757 optin = self.getProp('optin'), 758 type = self.getProp('type'), 759 theme = self.getProp('theme'); 760 761 // Open up the optin if the cookie has not been set. 762 if ( ! self.getCookie('om-' + id) && ! self.getProp('slide_open') || self.getProp('preview') ) { 763 $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin').removeClass('om-slide-closed').addClass('om-slide-open'); 764 self.setProp('slide_open', true); 765 } 766 }, 767 768 // Handles optin submissions. 769 this.submit = function(){ 770 // Prepare variables. 771 var self = this, 772 $ = self.getProp('$'), 773 optin = self.getProp('optin'), 774 type = self.getProp('type'), 775 theme = self.getProp('theme'), 776 custom = self.getProp('custom'); 777 778 // Prevent submitting if in Preview mode. 779 if ( self.getProp('preview') ) { 780 return; 781 } 782 783 // Handle the submission process based on custom or native implementation. 784 if ( custom ) { 785 // Create flag to test if submitted yet or not. 786 var submitted = false;
787 788 // Register event on custom form submission. 789 $(document).on('submit.doCustomOptin', '.om-custom-html-form form', function(e){ 790 if ( ! submitted ) { 791 // Set submitted flag to true. 792 submitted = true; 793 794 // Trigger event to say OptinMonster is about to process an optin. 795 self.trigger('OptinMonsterBeforeOptin'); 796 797 // Run a custom ajax request to capture the conversion for custom HTML forms. 798 self.optin(e.target, true); 799 800 // Prevent the default action AND return false to prevent form submission. 801 e.preventDefault(); 802 return false; 803 } else { 804 // Close out the optin based on the type of optin. 805 if ( 'lightbox' == type || 'canvas' == type ) { 806 $('#om-' + optin).fadeOut(300, self.onClose(self)); 807 } else if ( 'footer' == type || 'slide' == type ) { 808 $('#om-' + optin).animate({ 'bottom' : '-' + $('#om-' + optin).outerHeight() + 'px' }, 300, self.onClose(self)); 809 } 810 } 811 }); 812 } else { 813 // Process the native submission 814 $(document).on('click.doOptin', '#om-' + optin + ' #om-' + type + '-' + theme + '-optin-submit', function(e){ 815 // Prevent the default event from occurring. 816 e.preventDefault(); 817 818 // Trigger event to say OptinMonster is about to process an optin. 819 self.trigger('OptinMonsterBeforeOptin'); 820 821 // Handle the action. 822 self.optin(e.target); 823 }); 824 } 825 }, 826 827 // Handles closing the optin. 828 this.close = function(close){ 829 // Prepare variables. 830 var self = this, 831 $ = self.getProp('$'), 832 optin = self.getProp('optin'), 833 type = self.getProp('type'), 834 close = close || false; 835 836 // Prevent closing if in Preview mode. 837 if ( self.getProp('preview') ) { 838 return; 839 } 840 841 // If close is true, force closing the optin instead of registering it to a click event. 842 if ( close ) { 843 // Close out the optin based on the type of optin. 844 if ( 'lightbox' == type || 'canvas' == type || 'mobile' == type ) { 845 $('#om-' + optin).fadeOut(300, self.onClose); 846 } else if ( 'footer' == type || 'slide' == type ) { 847 $('#om-' + optin).animate({ 'bottom' : '-' + $('#om-' + optin).outerHeight() + 'px' }, 300, self.onClose); 848 } 849 } else { 850 // Close out the optin. 851 $(document).on('click.closeOptin', '#om-' + optin + ' .om-close, #om-' + optin + '.optin-monster-overlay', function(e){ 852 // Don't allow event bubbling or closing by clicking on the overlay for mobile optins. 853 if ( e.target !== this || 'mobile' == type && $(e.target).hasClass('optin-monster-overlay') ) { 854 return; 855 } 856 857 // Prevent the default from occurring. 858 e.preventDefault(); 859 860 // Trigger event to say OptinMonster is about to close. 861 self.trigger('OptinMonsterBeforeClose'); 862 863 // Close out the optin based on the type of optin. 864 if ( 'lightbox' == type || 'canvas' == type || 'mobile' == type ) { 865 $('#om-' + optin).fadeOut(300, self.onClose(self)); 866 } else if ( 'footer' == type ) { 867 $('#om-' + optin).animate({ 'bottom' : '-' + $('#om-' + optin).outerHeight() + 'px' }, 300, self.onClose(self)); 868 } 869 }); 870 } 871 }, 872 873 // Handles all of the close operations for optins, including setting cookies and visibility. 874 this.onClose = function(self){ 875 return function(){ 876 // Prepare variables. 877 var $ = self.getProp('$'); 878 879 // Set cookies and visibility. 880 self.cleanup(); 881 882 // If on a mobile optin, we need to do some extra cleanup. 883 if ( 'mobile' == self.getProp('type') ) { 884 // Prepare variables. 885 var $ = self.getProp('$'), 886 w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0), 887 d = self.getProp('dw'), 888 s = Math.round((w/d) * 100) / 100; 889 890 // Unbind our scroll event. 891 $(window).off('scroll.omMobile'); 892 893 // If our custom viewport tag exists, reset scaling back to the proper state. 894 if ( $('#optin-monster-viewport').length > 0 ) { 895 $('#optin-monster-viewport').attr('content', 'width=device-width, initial-scale=' + s + ', minimum-scale=' + s + ', maximum-scale=' + s); 896 $('html, body').css('overflow', ''); 897 898 // Reset user zooming. 899 setTimeout(function(){ 900 $('#optin-monster-viewport').attr('content', 'width=device-width, maximum-scale=10.0'); 901 }, 1000); 902 } 903 904 // Hide the overlay. 905 $('#om-' + self.getProp('optin') + '-overlay').hide(); 906 } 907 908 // Unbind our focus fix jump handler. 909 if ( self.isMobile() ) { 910 if ( 'lightbox' == self.getProp('type') || 'canvas' == self.getProp('type') ) { 911 $(document).scrollTop(self.getProp('dt')); 912 } 913 914 $(document).off('focus.omFocusJump'); 915 $(document).off('blur.omFocusJump'); 916 $('html, body').css('overflow', ''); 917 } 918 919 // Trigger the optin as closed. 920 self.trigger('OptinMonsterOnClose'); 921 }; 922 }, 923 924 // Run some cleanup to set cookies and optin visibility. 925 this.cleanup = function(success){ 926 // Prepare variables. 927 var self = this, 928 $ = self.getProp('$'), 929 id = self.getProp('id'), 930 scs = success || false; 931 932 // Set the visibility of the optin back to closed. 933 OptinMonsterOptins[self.getProp('optin_js')].visible = false;
934 935 // If on a mobile optin, make sure we unbind our scroll event at all times. 936 if ( 'mobile' == self.getProp('type') ) { 937 $(window).off('scroll.omMobile'); 938 } 939 940 // If the cookie value is 0, don't set any cookies. 941 if ( 0 === self.getProp('cookie') ) { 942 return; 943 } 944 945 // Set the cookie for the optin. 946 self.createCookie('om-' + id, true, self.getProp('cookie')); 947 948 // If using a clone, make sure the clone has a cookie as well to prevent it from displaying. 949 if ( self.getProp('clones') ) { 950 $.each(self.getProp('clones'), function(i, id){ 951 if ( 0 === id.length ) { 952 return; 953 } 954 self.createCookie('om-' + id, true, self.getProp('cookie')); 955 }); 956 } 957 958 // If the global setting is populated (and this is a success cleanup), set a global cookie. 959 if ( self.getProp('global_cookie') && scs ) { 960 self.createCookie('om-global-cookie'); 961 } 962 963 // If a success cleanup and the optin type is slide, set another cookie to prevent it from displaying period. 964 if ( scs && 'slide' == self.getProp('type') ) { 965 self.createCookie('om-' + id + '-closed', true, self.getProp('cookie')); 966 } 967 }, 968 969 // Track impression stats for optins. 970 this.track = function(conversion){ 971 // If we have already tracked this optin, are in preview mode or test mode, don't do anything. 972 if ( this.getProp('tracked') || this.getProp('preview') ) { 973 return; 974 } 975 976 // Prepare variables. 977 var self = this, 978 $ = self.getProp('$'), 979 optin = self.getProp('optin'), 980 ga = self.getProp('ga_uaid'), 981 conversion = conversion || false, 982 data = { 983 optin_monster_ajax_action: 'track_optinmonster', 984 optin_id: self.getProp('id'), 985 post_id: self.getProp('post_id'), 986 referrer: window.location.href, 987 user_agent: navigator.userAgent, 988 previous: document.referer 989 }; 990 991 // Make the ajax request to track the data. If using Google Analytics, send to GA, otherwise track locally. 992 if ( ga ) { 993 self.trackGoogleAnalytics(ga, conversion); 994 } else { 995 self.request(data); 996 } 997 998 // Fire an event to say the OptinMonster is about to track stats. 999 self.trigger('OptinMonsterTracked'); 1000 }, 1001 1002 // Method for tracking impression data via Google Analytics. 1003 this.trackGoogleAnalytics = function(id, track){ 1004 // Prepare variables. 1005 var self = this, 1006 type = track ? 'conversion' : 'impression', 1007 campaign = self.getProp('campaign') || self.getProp('optin'), 1008 init = self.getProp('ga_init'); 1009 1010 // Create a custom event tracker and dimensions if it has not been initialized. 1011 if ( ! init ) { 1012 ga('create', id, 'auto', { 'name' : 'omTracker' }); 1013 ga('omTracker.set', { 1014 'appName': self.getProp('app_name'), 1015 'appId': self.getProp('app_id'), 1016 'appVersion': self.getProp('app_version') 1017 }); 1018 self.setProp('ga_init', true); 1019 } 1020 1021 // Send the event tracking data to Google Analytics. 1022 ga('omTracker.send', 'event', campaign, type, self.getProp('id').toString()); 1023 1024 // Trigger event for type of tracking that has just occurred. 1025 if ( 'impression' == type ) { 1026 self.trigger('OptinMonsterTrackedImpression'); 1027 } else { 1028 self.trigger('OptinMonsterTrackedConversion'); 1029 } 1030 }, 1031 1032 // Handles the actual optin process for any type of optin. 1033 this.optin = function(target, custom){ 1034 // Prepare variables. 1035 var self = this, 1036 $ = self.getProp('$'), 1037 optin = self.getProp('optin'), 1038 custom = custom || false; 1039 1040 // Prevent submitting if in Preview mode. 1041 if ( self.getProp('preview') ) { 1042 return; 1043 } 1044 1045 // Output the loading icon and styles. 1046 self.loading(target); 1047 1048 // Prepare the ajax data variable and callbacks. 1049 var data = { 1050 optin_monster_ajax_action: 'do_optinmonster', 1051 optin_id: self.getProp('id'), 1052 post_id: self.getProp('post_id'), 1053 referrer: window.location.href, 1054 user_agent: navigator.userAgent, 1055 previous: document.referrer, 1056 email: $('#om-' + optin + ' #om-' + self.getProp('type') + '-' + self.getProp('theme') + '-optin-email').val(), 1057 name: $('#om-' + optin + ' #om-' + self.getProp('type') + '-' + self.getProp('theme') + '-optin-name').val() 1058 }, 1059 success = false, 1060 error = false;
1061 1062 // Handle the submission differently based on custom vs native implementation. 1063 if ( custom ) { 1064 // Prepare the callbacks. 1065 success = self.optinCustomSuccess(self, target); 1066 } else { 1067 // Verify the fields. If there is an error, output the error. 1068 var error = self.verify(); 1069 if ( error ) { 1070 // Return and output the error response. 1071 return self.error(target, error); 1072 } 1073 1074 // Prepare the callbacks. 1075 success = self.optinSuccess(self, target); 1076 error = self.optinError(self, target); 1077 } 1078 1079 // Make the request. 1080 self.request(data, success, error); 1081 }, 1082 1083 // Handle any errors with the optin output. 1084 this.error = function(target, error){ 1085 // Prepare variables. 1086 var self = this, 1087 $ = self.getProp('$'), 1088 submit = $(target), 1089 optin = self.getProp('optin'); 1090 1091 // Remove the loading icon. 1092 self.removeLoading(target); 1093 1094 // Output the error message on the screen. 1095 submit.parent().append('<p class="optin-monster-error" style="font-family:Georgia;font-size:13px;font-style:italic;color:#ff0000;margin:10px 0;text-align:center;line-height:18px;">' + error + '</p>'); 1096 1097 // Trigger the error event. 1098 self.trigger('OptinMonsterOnError'); 1099 }, 1100 1101 // Output the loading icon and overlay. 1102 this.loading = function(target){ 1103 // Prepare variables. 1104 var self = this, 1105 $ = self.getProp('$'), 1106 submit = $(target), 1107 position = submit.position(), 1108 margin = parseInt(submit.css('marginTop')), 1109 width = submit.outerWidth(), 1110 height = submit.outerHeight(); 1111 1112 // Remove any error messages. 1113 $('#om-' + self.getProp('optin')).find('.optin-monster-error').remove(); 1114 1115 // Add the loading icon overlay on the button. 1116 submit.after('<span class="optin-monster-loading"></span>').css('opacity', '.25'); 1117 $('#om-' + self.getProp('optin')).find('.optin-monster-loading').css({ width: width, height: height, top: position.top + margin, left: position.left, background: 'url(' + self.getProp('preloader') + ') no-repeat 50% 50%', position: 'absolute', zIndex: 84736365452, backgroundSize: '20px' }); 1118 }, 1119 1120 // Verifies that all fields are populated correctly before proceeding to process the optin. 1121 this.verify = function(){ 1122 // Prepare variables. 1123 var self = this, 1124 $ = self.getProp('$'), 1125 optin = self.getProp('optin'), 1126 type = self.getProp('type'), 1127 theme = self.getProp('theme'), 1128 name = $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin-name'), 1129 email = $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin-email'), 1130 honeye = $('#om-' + type + '-' + theme + '-optin').find('input[name="email"]').val(), 1131 honeyw = $('#om-' + type + '-' + theme + '-optin').find('input[name="website"]').val(), 1132 ret = false; 1133 1134 // If the name field is present, verify that it is filled out. 1135 if ( name && name.length > 0 ) { 1136 if ( name.val().length == 0 ) { 1137 ret = self.getProp('name_error') || self.getProp('error'); 1138 } 1139 } 1140 1141 // If the email field is present, verify that the email address is correct. 1142 if ( email && email.length > 0 ) { 1143 if ( email.val().length == 0 || ! self.isValidEmail(email.val()) ) { 1144 ret = self.getProp('email_error') || self.getProp('error'); 1145 } 1146 } 1147 1148 // If either of the honeypot fields are filled in, return an error. 1149 if ( honeye && honeye.length > 0 || honeyw && honeyw.length > 0 ) { 1150 ret = self.getProp('bot_error') || self.getProp('error'); 1151 } 1152 1153 // Return the error response. 1154 return ret; 1155 }, 1156 1157 // Remove the loading icon from view. 1158 this.removeLoading = function(target){ 1159 // Prepare variables. 1160 var self = this, 1161 $ = self.getProp('$'), 1162 submit = $(target); 1163 1164 // Remove the loading icon helper and holder. 1165 submit.css('opacity', '1'); 1166 $('#om-' + self.getProp('optin')).find('.optin-monster-loading').remove(); 1167 }, 1168 1169 // Handle the optin success callback. 1170 this.optinSuccess = function(self, target){ 1171 // Prepare variables. 1172 var $ = self.getProp('$'), 1173 optin = self.getProp('optin'), 1174 type = self.getProp('type'), 1175 theme = self.getProp('theme'); 1176 1177 // Return the callback. 1178 return function(res){ 1179 // If for some reason we do not receive a response or the response is empty, return with an error. 1180 if ( ! res || $.isEmptyObject(res) ) { 1181 return self.error(target, self.getProp('error')); 1182 } 1183 1184 // If we have an error, output the error message. 1185 if ( res && res.error ) { 1186 return self.error(target, res.error); 1187 } 1188 1189 // Handle the success pieces of the optin. 1190 self.cleanup(true); 1191 1192 // Trigger event to say OptinMonster has processed the optin successfully. 1193 self.trigger('OptinMonsterOptinSuccess'); 1194 1195 // If using GA, track the conversion. 1196 if ( self.getProp('ga_id') ) { 1197 self.trackGoogleAnalytics(self.getProp('ga_uaid'), true); 1198 } 1199 1200 // If a redirect is specified, redirect to the specified page. 1201 if ( self.getProp('redirect') ) { 1202 // Trigger event to say OptinMonster is about to redirect to the success page. 1203 self.trigger('OptinMonsterOnRedirect'); 1204 1205 // Possibly append lead data to the redirect url. 1206 var redirect = self.getProp('redirect'), 1207 pass = self.getProp('redirect_pass'); 1208 if ( pass && res && res.success && ! $.isEmptyObject(res.success) ) { 1209 var i = 0, 1210 sep = '&'; 1211 for ( key in res.success ) { 1212 if ( 0 == i ) { 1213 if ( redirect.indexOf('?') < 0 ) { 1214 sep = '?'; 1215 } 1216 } else { 1217 sep = '&'; 1218 } 1219 redirect = redirect + sep + key + '=' + res.success[key]; 1220 i++; 1221 } 1222 } 1223 1224 // Redirect the user. 1225 window.location.href = redirect; 1226 } else { 1227 // If we have a success message, fade in the success message, otherwise close the optin. 1228 if ( self.getProp('success') ) { 1229 self.successMessage(target); 1230 } else { 1231 self.close(true); 1232 1233 // Remove the loading icon. 1234 self.removeLoading(target); 1235 } 1236 1237 // Trigger event to say OptinMonster has processed the optin successfully and has closed. 1238 self.trigger('OptinMonsterOptinSuccessClose'); 1239 } 1240 }; 1241 }, 1242 1243 // Handle outputting success messages for the optin. 1244 this.successMessage = function(target){ 1245 // Prepare variables. 1246 var self = this, 1247 $ = self.getProp('$'), 1248 optin = self.getProp('optin'), 1249 type = self.getProp('type'), 1250 theme = self.getProp('theme'), 1251 container = $('#om-' + optin + ' #om-' + type + '-' + theme + '-optin'), 1252 position = container.position(), 1253 width = container.outerWidth(), 1254 height = container.outerHeight(), 1255 zindex = 'sidebar' == type || 'post' == type ? 7271832 : 1000098373462; 1256 1257 // Remove the old close button. 1258 if ( 'slide' !== type ) { 1259 $('#om-' + optin).find('.om-close').remove(); 1260 } 1261 1262 // Prepare the success message overlay and actual success message.
1263 var overlay = ( 'sidebar' == type || 'post' == type ) ? '<div class="optin-monster-success-overlay" style="display:none;"></div>' : '<div class="optin-monster-success-overlay" style="display:none;"><a href="#" class="om-close om-success-close">×</a></div>', 1264 message = self.getProp('success'); 1265 1266 // Output the overlay. 1267 container.after(overlay); 1268 1269 // Style the new overlay. 1270 $('#om-' + optin).find('.optin-monster-success-overlay').css({ width: width, height: height, top: position.top, left: position.left, background: '#fff', position: 'absolute', zIndex: zindex, padding: '0px 20px', opacity: 0, display: 'block' }).append('<div class="optin-monster-success-message">' + message + '</div>'); 1271 1272 // Position the content inside of the div to be vertically centered. 1273 $('#om-' + optin).find('.optin-monster-success-message').css({ 'margin-top': (height - $('#om-' + optin).find('.optin-monster-success-message').height()) / 2 }); 1274 1275 // Display the overlay. 1276 $('#om-' + optin).find('.optin-monster-success-overlay').fadeTo(300, 1, function(){ 1277 // Remove the loading icon. 1278 self.removeLoading(target); 1279 1280 // Initialize social services if neeeded. 1281 self.socialServices(); 1282 }); 1283 1284 // Bind to window resize to ensure the success message is always the proper dimensions. 1285 $(window).resize(function(){ 1286 $('.optin-monster-success-overlay').css({ width: $('#om-' + type + '-' + theme + '-optin').outerWidth(), height: $('#om-' + type + '-' + theme + '-optin').outerHeight(), top: $('#om-' + type + '-' + theme + '-optin').position().top, left: $('#om-' + type + '-' + theme + '-optin').position().left }); 1287 $('.optin-monster-success-message').css({ 'margin-top': ($('#om-' + type + '-' + theme + '-optin').outerHeight() - $('.optin-monster-success-message').height()) / 2 }); 1288 }); 1289 }, 1290 1291 // Handle the custom HTML optin success callback. 1292 this.optinCustomSuccess = function(self, target){ 1293 // Prepare variables. 1294 var $ = self.getProp('$'); 1295 1296 // Return the callback. 1297 return function(res){ 1298 // Trigger event to say OptinMonster has processed the optin. 1299 self.trigger('OptinMonsterOptinSuccess'); 1300 1301 // If using GA, track the conversion. 1302 if ( self.getProp('ga_id') ) { 1303 self.trackGoogleAnalytics(self.getProp('ga_uaid'), true); 1304 } 1305 1306 // Resubmit the form with extra data passed to it. 1307 $(target).submit(); 1308 }; 1309 }, 1310 1311 // Handle the optin error callback. 1312 this.optinError = function(self, target){ 1313 // Prepare variables. 1314 var $ = self.getProp('$'), 1315 optin = self.getProp('optin'), 1316 type = self.getProp('type'), 1317 theme = self.getProp('theme'); 1318 1319 // Return the callback. 1320 return function(err, xhr, message){ 1321 // Trigger event to say OptinMonster has encountered an error processing the optin. 1322 self.trigger('OptinMonsterOptinError'); 1323 1324 return self.error(target, self.getProp('ajax_error') + message); 1325 }; 1326 }, 1327 1328 // Utility method for making ajax requests. 1329 this.request = function(data, success_cb, error_cb){ 1330 // Prepare variables. 1331 var self = this, 1332 $ = self.getProp('$'), 1333 ajax = { 1334 url: self.getProp('ajax'), 1335 data: data, 1336 cache: false, 1337 type: 'POST', 1338 dataType: 'json', 1339 timeout: 30000 1340 }, 1341 success_cb = success_cb || false, 1342 error_cb = error_cb || false; 1343 1344 // Set success callback if supplied.
1345 if ( success_cb ) { 1346 ajax.success = success_cb; 1347 } 1348 1349 // Set error callback if supplied. 1350 if ( error_cb ) { 1351 ajax.error = error_cb; 1352 } 1353 1354 // Make the ajax request. 1355 $.ajax(ajax); 1356 }, 1357 1358 // Appends the optin holder to the <body> tag on the page. 1359 this.appendHolder = function(){ 1360 var $ = this.getProp('$') 1361 type = this.getProp('type'), 1362 styles = false; 1363 1364 // Prepare the styles. 1365 if ( 'lightbox' == type || 'canvas' == type ) { 1366 styles = { 'position' : 'fixed', 'z-index' : '7371832', 'top' : '0', 'left' : '0', 'zoom' : '1', 'width' : '100%', 'height' : '100%', 'margin' : '0', 'padding' : '0' }; 1367 } else if ( 'footer' == type ) { 1368 styles = { 'position' : 'fixed', 'z-index' : '7371832', 'bottom' : '0', 'left' : '0', 'zoom' : '1', 'width' : '100%', 'margin' : '0', 'padding' : '0' }; 1369 } 1370 1371 // If we have styles, output them now. 1372 if ( styles ) { 1373 $('#om-' + this.getProp('optin')).css(styles).appendTo('body'); 1374 } 1375 1376 // Fire event for handling styles. 1377 this.trigger('OptinMonsterAppendHolder'); 1378 }, 1379 1380 // Prepare custom optin output. 1381 this.prepareCustomOptin = function(){ 1382 // Store localized copy of our main object instance. 1383 var self = this, 1384 optin = self.getProp('optin'), 1385 $ = self.getProp('$'), 1386 inputs = $('#om-' + optin + ' input[data-om-render=label]'); 1387 1388 // Only proceed if we have inputs to parse. 1389 if ( inputs.length > 0 ) { 1390 // Load the element changer. 1391 self.loadElementChange(); 1392 1393 // Loop through each input and make adjustments as necessary. 1394 inputs.each(function(){ 1395 if ( $.fn.changeElementType ) { 1396 $(this).changeElementType('label'); 1397 } 1398 }); 1399 1400 // Now convert the labels to their proper format. 1401 $('#om-' + optin + ' label[data-om-render=label]').each(function(){ 1402 $(this).text($(this).attr('value')).removeAttr('value type'); 1403 }); 1404 } 1405 1406 // Trigger event to say OptinMonster has finished preparing custom optins. 1407 self.trigger('OptinMonsterCustomDone'); 1408 }, 1409 1410 // Polling helper for executing at certain intervals. 1411 this.poll = (function(){ 1412 var timer = 0; 1413 return function(callback, ms){ 1414 clearInterval(timer); 1415 timer = setInterval(callback, ms); 1416 }; 1417 })(), 1418 1419 // Tests the validity of an email address entered by the user. 1420 this.isValidEmail = function(email){ 1421 return (new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i)).test(email); 1422 }, 1423 1424 // Sets cookies. 1425 this.createCookie = function(name, value, days){ 1426 // If test mode, do nothing. 1427 if ( this.getProp('test') ) { 1428 return; 1429 } 1430 1431 // If we have a days value, set it in the expiry of the cookie. 1432 if ( days ) { 1433 var date = new Date(); 1434 date.setTime(date.getTime() + (days*24*60*60*1000)); 1435 var expires = '; expires=' + date.toGMTString(); 1436 } else { 1437 var expires = ''; 1438 } 1439 1440 // Write the cookie. 1441 document.cookie = name + '=' + value + expires + '; path=/'; 1442 }, 1443 1444 // Retrieves cookies. 1445 this.getCookie = function(name){ 1446 var nameEQ = name + '='; 1447 var ca = document.cookie.split(';'); 1448 for ( var i = 0; i < ca.length; i++ ) { 1449 var c = ca[i]; 1450 while ( c.charAt(0) == ' ' ) { 1451 c = c.substring(1, c.length); 1452 } 1453 if ( c.indexOf(nameEQ) == 0 ) { 1454 return c.substring(nameEQ.length, c.length); 1455 } 1456 } 1457 1458 return null; 1459 }, 1460 1461 // Removes cookies. 1462 this.removeCookie = function(name){ 1463 this.createCookie(name, '', -1); 1464 }, 1465 1466 // Generic logging utility. 1467 this.log = function(text){ 1468 (typeof console === 'object') ? console.log(text) : ''; 1469 }, 1470 1471 // Generic event triggering utility for OptinMonster. 1472 this.trigger = function(event_name){ 1473 var self = this; 1474 self.public.ejQuery(document).trigger(event_name, [self.public, self]); 1475 }, 1476 1477 // Loads the element changer. 1478 this.loadElementChange = function(){ 1479 (function(a){a.fn.changeElementType=function(c){var b={};a.each(this[0].attributes,function(e,d){b[d.nodeName]=d.nodeValue});this.replaceWith(function(){return a('<'+c+'/>',b).append(a(this).contents())})}})(this.getProp('$')); 1480 } 1481 1482 // Checks to ensure cookies are enabled for the current visitor. 1483 this.cookiesEnabled = function(){ 1484 var cookieEnabled = (navigator.cookieEnabled) ? true : false;
1485 if ( typeof navigator.cookieEnabled == 'undefined' && ! cookieEnabled ) { 1486 document.cookie = 'testcookie'; 1487 cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false; 1488 } 1489 return (cookieEnabled); 1490 }, 1491 1492 // Retrieves properties inside of the class. 1493 this.getProp = function(name){ 1494 return this.public.hasOwnProperty(name) ? this.public[name] : false; 1495 }, 1496 1497 // Set properties inside of the class. 1498 this.setProp = function(name, value){ 1499 this.public[name] = value; 1500 }, 1501 1502 // Mobile device check. 1503 this.isMobile = function(){ 1504 var check = false; 1505 (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check=true})(navigator.userAgent||navigator.vendor||window.opera); 1506 return check; 1507 }, 1508 1509 // Checks to see if a popup is visible or not. 1510 this.hasVisiblePopup = function(){ 1511 // Get all optins currently visible and check against their type. 1512 var visible = [], 1513 $ = this.getProp('$'), 1514 i = 0; 1515 1516 // Loop through and grab all visible optins. 1517 for ( var prop in OptinMonsterOptins ) { 1518 if ( OptinMonsterOptins[prop].hasOwnProperty('visible') ) { 1519 if ( true === OptinMonsterOptins[prop]['visible'] ) { 1520 visible[i] = OptinMonsterOptins[prop]['type']; 1521 i++; 1522 } 1523 } 1524 } 1525 1526 // Return the flag based on if the type of optin is visible or not. 1527 return $.inArray('lightbox', visible) > -1 || $.inArray('canvas', visible) > -1 || false; 1528 }, 1529 1530 // Placeholder polyfill for IE 8/9. 1531 this.loadPlaceholder = function(){ 1532 var self = this, 1533 $ = self.getProp('$'); 1534 !function(e,a,t){function l(e){var a={},l=/^jQuery\d+$/;return t.each(e.attributes,function(e,t){t.specified&&!l.test(t.name)&&(a[t.name]=t.value)}),a}function r(e,a){var l=this,r=t(l);if(l.value==r.attr("placeholder")&&r.hasClass("placeholder"))if(r.data("placeholder-password")){if(r=r.hide().next().show().attr("id",r.removeAttr("id").data("placeholder-id")),e===!0)return r[0].value=a;r.focus()}else l.value="",r.removeClass("placeholder"),l==d()&&l.select()}function o(){var e,a=this,o=t(a),d=this.id;if(""==a.value){if("password"==a.type){if(!o.data("placeholder-textinput")){try{e=o.clone().attr({type:"text"})}catch(c){e=t("<input>").attr(t.extend(l(this),{type:"text"}))}e.removeAttr("name").data({"placeholder-password":o,"placeholder-id":d}).bind("focus.placeholder",r),o.data({"placeholder-textinput":e,"placeholder-id":d}).before(e)}o=o.removeAttr("id").hide().prev().attr("id",d).show()}o.addClass("placeholder"),o[0].value=o.attr("placeholder")}else o.removeClass("placeholder")}function d(){try{return a.activeElement}catch(e){}}var c,n,i="[object OperaMini]"==Object.prototype.toString.call(e.operamini),p="placeholder"in a.createElement("input")&&!i,u="placeholder"in a.createElement("textarea")&&!i,h=t.fn,s=t.valHooks,v=t.propHooks;p&&u?(n=h.placeholder=function(){return this},n.input=n.textarea=!0):(n=h.placeholder=function(){var e=this;return e.filter((p?"textarea":":input")+"[placeholder]").not(".placeholder").bind({"focus.placeholder":r,"blur.placeholder":o}).data("placeholder-enabled",!0).trigger("blur.placeholder"),e},n.input=p,n.textarea=u,c={get:function(e){var a=t(e),l=a.data("placeholder-password");return l?l[0].value:a.data("placeholder-enabled")&&a.hasClass("placeholder")?"":e.value}
1534,set:function(e,a){var l=t(e),c=l.data("placeholder-password");return c?c[0].value=a:l.data("placeholder-enabled")?(""==a?(e.value=a,e!=d()&&o.call(e)):l.hasClass("placeholder")?r.call(e,!0,a)||(e.value=a):e.value=a,l):e.value=a}},p||(s.input=c,v.value=c),u||(s.textarea=c,v.value=c),t(function(){t(a).delegate("form","submit.placeholder",function(){var e=t(".placeholder",this).each(r);setTimeout(function(){e.each(o)},10)})}),t(e).bind("beforeunload.placeholder",function(){t(".placeholder").each(function(){this.value=""})}))}(this,document,$); 1535 }, 1536 1537 // Ensure that the placeholder polyfill is added. 1538 this.doPlaceholder = function(){ 1539 // Store localized copy of our main object instance. 1540 var self = this, 1541 $ = self.getProp('$'), 1542 fields = $('#om-' + self.getProp('optin') + ' input'); 1543 1544 if ( fields.length > 0 && $.fn.placeholder ) { 1545 fields.each(function(){ 1546 $(this).placeholder(); 1547 }); 1548 } 1549 1550 // Trigger event to say OptinMonster placeholders are set. 1551 self.trigger('OptinMonsterPlaceholderDone'); 1552 }, 1553 1554 // Helper to see if we are using IE8 or 9. 1555 this.isIE = function(){ 1556 return typeof om_ie_browser != 'undefined'; 1557 }, 1558 1559 // Helper for some custom social services. 1560 this.socialServices = function(){ 1561 // Prepare variables. 1562 var self = this, 1563 $ = self.getProp('$'); 1564 1565 // Facebook helper. 1566 if ( typeof(FB) != 'undefined' && FB != null ) { 1567 FB.XFBML.parse(); 1568 } 1569 1570 // Twitter helper. 1571 if ( typeof(twttr) != 'undefined' && twttr != null ) { 1572 twttr.widgets.load(); 1573 } 1574 1575 // Trigger an event for social services. 1576 self.trigger('OptinMonsterSocial'); 1577 } 1578} 1579 1580// Handle loading manual trigger optins. 1581jQuery(document).ready(function($){ 1582 var OptinMonsterLinkSlugs = {}; 1583 $(document).find('.manual-optin-trigger, .om-monster-link').each(function(i, el){ 1584 var $this = $(this), 1585 slug = $this.data('optin-slug'); 1586 1587 // If the slug is not found, return early. 1588 if ( ! slug ) { 1589 return; 1590 } 1591 1592 // Prepare the JS checker. 1593 var slugjs = slug.replace('-', '_'), 1594 optin = $('#om-' + slug); 1595 1596 // If the slug already exists in the global variable, return early. 1597 if ( OptinMonsterLinkSlugs.hasOwnProperty(slugjs) ) { 1598 return; 1599 } 1600 1601 // If the optin already exists on the page and it is defined in the global scope, set our holder to it, otherwise remove the optin because it won't be used (a cookie has been set). 1602 if ( 0 !== optin.length ) { 1603 if ( typeof OptinMonsterOptins != 'undefined' && OptinMonsterOptins.hasOwnProperty(slugjs) ) { 1604 OptinMonsterLinkSlugs[slugjs] = window[slugjs]; 1605 return; 1606 } else { 1607 // Remove the optin to pave the way for the proper one to be loaded. 1608 $(optin).remove(); 1609 } 1610 } 1611 1612 // Load the optin on the page. 1613 var monsterlink = new OptinMonster(); 1614 monsterlink.init({ 1615 optin: slug, 1616 optin_js: slugjs, 1617 click: true 1618 }); 1619 OptinMonsterLinkSlugs[slugjs] = monsterlink; 1620 }); 1621 1622 // Register the click handler to open the optin. 1623 $(document).on('click', '.manual-optin-trigger, .om-monster-link', function(e){ 1624 e.preventDefault(); 1625 1626 // Prepare variables. 1627 var $this = $(this), 1628 slug = $this.data('optin-slug'); 1629 1630 // If the slug is not found, return early. 1631 if ( ! slug ) { 1632 return; 1633 } 1634 1635 // Prepare the JS checker. 1636 var slugjs = slug.replace('-', '_'), 1637 optin = $('#om-' + slug); 1638 1639 // If the slug does not exist in the global variable, return early. 1640 if ( ! OptinMonsterLinkSlugs.hasOwnProperty(slugjs) ) { 1641 return; 1642 } 1643 1644 // Open the optin. 1645 OptinMonsterLinkSlugs[slugjs].open(true); 1646 }); 1647});
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.