2020-08-29 14:38:25 +00:00
// Let's not step on anybody else's toes.
2020-08-29 11:59:02 +00:00
var mailymailyApp = mailymailyApp || { } ;
2020-08-29 11:55:43 +00:00
( function ( app ) {
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The html element .
* @ type { Element }
* /
2020-08-29 14:38:25 +00:00
var html = document . getElementsByTagName ( 'html' ) [ 0 ] ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The body element .
* @ type { Element }
* /
2020-08-29 14:38:25 +00:00
var body = document . getElementsByTagName ( 'body' ) [ 0 ] ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:59:02 +00:00
* The active mailymaily modal .
2020-08-29 11:55:43 +00:00
* @ type { Element }
* /
2020-08-29 14:38:25 +00:00
var modal = null ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* List of focusable elements within modal .
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
var focusable = 'a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The last document element to have focus before the modal was opened .
* Focus is to be set back on this element after the modal is closed .
* @ type { Element }
* /
2020-08-29 14:38:25 +00:00
var lastDocElementFocused = null ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The default svg icon for email client buttons .
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
var worldSvg = ` <svg viewBox="0 0 24 24"><g class="nc-icon-wrapper" stroke-linecap="square" stroke-linejoin="miter" stroke-width="2" fill="currentColor" stroke="currentColor"><path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M5.704,2.979 c0.694,0.513,1.257,1.164,1.767,2.02C7.917,5.746,8.908,7.826,8,9c-1.027,1.328-4,1.776-4,3c0,0.921,1.304,1.972,2,3 c1.047,1.546,0.571,3.044,0,4c-0.296,0.496-0.769,0.92-1.293,1.234" stroke-linecap="butt"/> <path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M20.668,5.227 C18.509,6.262,15.542,6.961,15,7c-1.045,0.075-1.2-0.784-2-2c-0.6-0.912-2-2.053-2-3c0-0.371,0.036-0.672,0.131-0.966" stroke-linecap="butt"/> <circle fill="none" stroke="currentColor" stroke-miterlimit="10" cx="12" cy="12" r="11"/> <path data-cap="butt" data-color="color-2" fill="none" stroke-miterlimit="10" d="M19.014,12.903 C19.056,15.987,15.042,19.833,13,19c-1.79-0.73-0.527-2.138-0.986-6.097c-0.191-1.646,1.567-3,3.5-3S18.992,11.247,19.014,12.903z" stroke-linecap="butt"/></g></svg> ` ;
2020-08-29 11:55:43 +00:00
2020-08-29 15:16:31 +00:00
/ * *
* The svg icon for outlook web .
* @ type { String }
* /
var outlookSvg = ` <svg viewBox="0 0 48 48" width="48px" height="48px"><path fill="#03A9F4" d="M21,31c0,1.104,0.896,2,2,2h17c1.104,0,2-0.896,2-2V16c0-1.104-0.896-2-2-2H23c-1.104,0-2,0.896-2,2V31z"/><path fill="#B3E5FC" d="M42,16.975V16c0-0.428-0.137-0.823-0.367-1.148l-11.264,6.932l-7.542-4.656L22.125,19l8.459,5L42,16.975z"/><path fill="#0277BD" d="M27 41.46L6 37.46 6 9.46 27 5.46z"/><path fill="#FFF" d="M21.216,18.311c-1.098-1.275-2.546-1.913-4.328-1.913c-1.892,0-3.408,0.669-4.554,2.003c-1.144,1.337-1.719,3.088-1.719,5.246c0,2.045,0.564,3.714,1.69,4.986c1.126,1.273,2.592,1.91,4.378,1.91c1.84,0,3.331-0.652,4.474-1.975c1.143-1.313,1.712-3.043,1.712-5.199C22.869,21.281,22.318,19.595,21.216,18.311z M19.049,26.735c-0.568,0.769-1.339,1.152-2.313,1.152c-0.939,0-1.699-0.394-2.285-1.187c-0.581-0.785-0.87-1.861-0.87-3.211c0-1.336,0.289-2.414,0.87-3.225c0.586-0.81,1.368-1.211,2.355-1.211c0.962,0,1.718,0.393,2.267,1.178c0.555,0.795,0.833,1.895,0.833,3.31C19.907,24.906,19.618,25.968,19.049,26.735z"/></svg> ` ;
/ * *
* The svg icon for gmail .
* @ type { String }
* /
var gmailSvg = ` <svg viewBox="0 0 48 48" width="48px" height="48px"><path fill="#e0e0e0" d="M5.5,40.5h37c1.933,0,3.5-1.567,3.5-3.5V11c0-1.933-1.567-3.5-3.5-3.5h-37C3.567,7.5,2,9.067,2,11v26C2,38.933,3.567,40.5,5.5,40.5z"/><path fill="#d9d9d9" d="M26,40.5h16.5c1.933,0,3.5-1.567,3.5-3.5V11c0-1.933-1.567-3.5-3.5-3.5h-37C3.567,7.5,2,9.067,2,11L26,40.5z"/><path fill="#eee" d="M6.745,40.5H42.5c1.933,0,3.5-1.567,3.5-3.5V11.5L6.745,40.5z"/><path fill="#e0e0e0" d="M25.745,40.5H42.5c1.933,0,3.5-1.567,3.5-3.5V11.5L18.771,31.616L25.745,40.5z"/><path fill="#ca3737" d="M42.5,9.5h-37C3.567,9.5,2,9.067,2,11v26c0,1.933,1.567,3.5,3.5,3.5H7V12h34v28.5h1.5c1.933,0,3.5-1.567,3.5-3.5V11C46,9.067,44.433,9.5,42.5,9.5z"/><path fill="#f5f5f5" d="M42.5,7.5H24H5.5C3.567,7.5,2,9.036,2,11c0,1.206,1.518,2.258,1.518,2.258L24,27.756l20.482-14.497c0,0,1.518-1.053,1.518-2.258C46,9.036,44.433,7.5,42.5,7.5z"/><path fill="#e84f4b" d="M43.246,7.582L24,21L4.754,7.582C3.18,7.919,2,9.297,2,11c0,1.206,1.518,2.258,1.518,2.258L24,27.756l20.482-14.497c0,0,1.518-1.053,1.518-2.258C46,9.297,44.82,7.919,43.246,7.582z"/></svg> ` ;
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The default svg icon for default email app button .
* @ type { String }
* /
2020-08-29 15:43:50 +00:00
var uiSvg = ` <svg viewBox="0 0 32 32"><path d="M 3 8 L 3 26 L 29 26 L 29 8 Z M 7.3125 10 L 24.6875 10 L 16 15.78125 Z M 5 10.875 L 15.4375 17.84375 L 16 18.1875 L 16.5625 17.84375 L 27 10.875 L 27 24 L 5 24 Z"/></svg> ` ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The default svg icon for copy button .
* @ type { String }
* /
2020-08-29 15:43:50 +00:00
var clipboardSvg = ` <svg viewBox="0 0 32 32"><path d="M 16 3 C 14.742188 3 13.847656 3.890625 13.40625 5 L 6 5 L 6 28 L 26 28 L 26 5 L 18.59375 5 C 18.152344 3.890625 17.257813 3 16 3 Z M 16 5 C 16.554688 5 17 5.445313 17 6 L 17 7 L 20 7 L 20 9 L 12 9 L 12 7 L 15 7 L 15 6 C 15 5.445313 15.445313 5 16 5 Z M 8 7 L 10 7 L 10 11 L 22 11 L 22 7 L 24 7 L 24 26 L 8 26 Z"/></svg> ` ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:59:02 +00:00
* User options to change mailymaily ' s behavior and / or appearance .
2020-08-29 11:55:43 +00:00
* @ type { Object }
* /
2020-08-29 14:38:25 +00:00
var options = new Object ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Allows for a custom class to namespace css classes .
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
options . linkClass = 'mailymaily' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* When set to true , the modal is closed automatically when email client is clicked .
* @ type { Boolean }
* /
2020-08-29 14:38:25 +00:00
options . autoClose = true ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* When set to true , the modal is not displayed on mobile devices , and the local email app is used automatically .
* @ type { Boolean }
* /
2020-08-29 14:38:25 +00:00
options . disableOnMobile = true ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* The modal title .
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . title = 'Neue E-Mail schreiben mit' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Text for button 1.
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonText1 = 'Gmail' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Text for button 2.
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonText2 = 'Outlook Web' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Text for button 4.
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonText4 = 'Standardanwendung' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* URL of svg file used as icon for button 1.
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonIcon1 = gmailSvg ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* URL of svg file used as icon for button 2.
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonIcon2 = outlookSvg ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* URL of svg file used as icon for button 3.
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
options . buttonIcon3 = worldSvg ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* URL of svg file used as icon for button 4.
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
options . buttonIcon4 = uiSvg ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* URL of svg file used as icon for Copy button .
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
options . buttonIconCopy = clipboardSvg ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Text for Copy button .
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonTextCopy = 'Kopieren' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Text for Copy button when clicked .
* @ type { String }
* /
2020-08-29 15:16:31 +00:00
options . buttonTextCopyAction = 'Kopiert!' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Keep track of the page ' s scroll position .
* @ type { Number }
* /
2020-08-29 14:38:25 +00:00
var scrollPosition = 0 ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Keep track of the page ' s original scroll behavior .
* @ type { String }
* /
2020-08-29 14:38:25 +00:00
var scrollBehavior = 'auto' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Build a style tag with default styling to be embedded on the page .
*
* @ return { String } The style tag markup .
* /
2020-08-29 14:38:25 +00:00
app . buildStyleTag = function ( ) {
var styleTag = document . createElement ( 'style' ) ;
2020-08-29 15:30:37 +00:00
var css = ` .mailymaily-modal{background-color:#000;background-color:rgba(0,0,0,.4);bottom:0;color:#303131;display:none;height:100%;left:0;margin:0;padding:0;position:fixed;right:0;top:0;width:100%;z-index:1000}.mailymaily-modal-content{-webkit-animation:mailymaily-appear .4s;animation:mailymaily-appear .4s;background-color:#f1f5f8;border-radius:8px;bottom:auto;box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);left:50%;max-height:calc(100% - 100px);overflow:auto;padding:0;position:fixed;right:-45%;top:50%;transform:translate(-50%,-50%)}.mailymaily-modal-content:focus,.mailymaily-modal-content:hover{overflow-y:auto}@media only screen and (min-width:768px){.mailymaily-modal-content{right:auto}}.mailymaily-modal-head{align-items:center;background-color:#fff;clear:both;display:flex;min-width:0;padding:10px 20px}.mailymaily-modal-title{color:#303131;flex:1;font-family:sans-serif;font-size:120%;font-weight:700;margin:0;overflow:hidden;padding:0;text-overflow:ellipsis;white-space:nowrap}.mailymaily-modal-close{color:#aaa;flex:initial;font-size:38px;margin-left:20px;position:relative;text-align:right;text-decoration:none;top:-4px}.mailymaily-modal-close:focus,.mailymaily-modal-close:hover{color:#000;cursor:pointer;font-weight:700;outline:0}.mailymaily-modal-body{height:100%;padding:20px}.mailymaily-button{color:#333;text-decoration:none}.mailymaily-button:focus{outline:0}.mailymaily-button:focus .mailymaily-button-content{background-color:#555;color:#fff}.mailymaily-button-content{background-color:#fff;border:none;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,.18);margin-bottom:20px;overflow:hidden;padding:15px 20px;text-overflow:ellipsis;white-space:nowrap}.mailymaily-button-content:hover{background-color:#555;color:#fff}.mailymaily-button:last-child .mailymaily-button-content{margin-bottom:0}.mailymaily-button-icon{display:inline-block;font-weight:700;position:relative;top:4px}.mailymaily-button-icon svg{height:24px;width:24px}.mailymaily-button-text{display:inline-block;margin-left:0;position:relative;top:-2px}.mailymaily-copy{border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,.18);height:59px;margin-top:20px;position:relative}.mailymaily-button-copy{background-color:#fff;border:none;border-bottom-left-radius:8px;border-top-left-radius:8px;bottom:21px;color:#333;font-size:100%;height:100%;left:0;overflow:false;padding:15px 20px;position:absolute;text-overflow:ellipsis;top:0;white-space:nowrap;width:130px}.mailymaily-button-copy:focus,.mailymaily-button-copy:hover{background-color:#555;color:#fff;cursor:pointer;outline:0}.mailymaily-button-copy-clicked,.mailymaily-button-copy-clicked:focus,.mailymaily-button-copy-clicked:hover{background-color:#1f9d55;color:#fff}.mailymaily-button-copy-clicked .mailymaily-button-icon,.mailymaily-button-copy-clicked:focus .mailymaily-button-icon,.mailymaily-button-copy-clicked:hover .mailymaily-button-icon{display:none;visibility:hidden}.mailymaily-button-copy-clicked .mailymaily-button-text,.mailymaily-button-copy-clicked:focus .mailymaily-button-text,.mailymaily-button-copy-clicked:hover .mailymaily-button-text{color:#fff;top:2px}.mailymaily-email-address{background-color:#d8dcdf;border:none;border-radius:8px;box-shadow:unset;box-sizing:border-box;color:#48494a;font-size:100%;height:100%;overflow:hidden;padding:20px 20px 20px 140px;text-overflow:ellipsis;white-space:nowrap;width:100%}.mailymaily-brand{color:#888;font-size:80%;margin-top:20px;text-align:center}.mailymaily-brand a{color:#888}.mailymaily-brand a:focus,.mailymaily-brand a:hover{font-weight:700;outline:0}.mailymaily-no-scroll{overflow:hidden;position:fixed;width:100%}.mailymaily-is-hidden{display:none;visibility:hidden}@-webkit-keyframes mailymaily-appear{0%{opacity:0;transform:translate(-50%,-50%) scale(0,0)}100%{opacity:1;transform:translate(-50%,-50%) scale(1,1)}}@keyframes mailymaily-appear{0%{opacity:0;transform:translate(-50%,-50%) scale(0,0)}100%{opacity:1;transform:translate(-50%,-50%) scale(1,1)}} ` ;
2020-08-29 14:38:25 +00:00
css = css . replace ( /mailymaily/g , app . prefix ( ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
styleTag . setAttribute ( 'id' , app . prefix ( '-styles' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( styleTag . styleSheet ) {
// Required for IE8 and below.
styleTag . styleSheet . cssText = css ;
} else {
styleTag . appendChild ( document . createTextNode ( css ) ) ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return styleTag ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Embed style tag on the page .
* /
2020-08-29 14:38:25 +00:00
app . embedStyleTag = function ( ) {
if ( app . styleTagExists ( ) ) {
return ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
var firstHeadChild = document . head . firstChild ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
document . head . insertBefore ( app . buildStyleTag ( ) , firstHeadChild ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if style tag has already been embedded on the page .
*
* @ return { Boolean } True if style tag is already embedded .
* /
2020-08-29 14:38:25 +00:00
app . styleTagExists = function ( ) {
if ( document . getElementById ( app . prefix ( '-styles' ) ) ) {
return true ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return false ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Build the modal markup .
*
* @ return { String } The modal markup .
* /
2020-08-29 14:38:25 +00:00
app . buildModal = function ( ) {
var modal = document . createElement ( 'div' ) ;
2020-08-29 15:30:08 +00:00
var markup = ` <div class="mailymaily-modal-content"><div class="mailymaily-modal-head"><div id="mailymaily-modal-title" class="mailymaily-modal-title"> ${ options . title } </div><a id="mailymaily-modal-close" class="mailymaily-modal-close" href="#">×</a></div><div class="mailymaily-modal-body"><div class="mailymaily-clients"><a id="mailymaily-button-1" class="mailymaily-button" href="#"><div class="mailymaily-button-content"><span id="mailymaily-button-icon-1" class="mailymaily-button-icon"> ${ options . buttonIcon1 } </span> <span id="mailymaily-button-text-1" class="mailymaily-button-text"> ${ options . buttonText1 } </span></div></a><a id="mailymaily-button-2" class="mailymaily-button" href="#"><div class="mailymaily-button-content"><span id="mailymaily-button-icon-2" class="mailymaily-button-icon"> ${ options . buttonIcon2 } </span> <span id="mailymaily-button-text-2" class="mailymaily-button-text"> ${ options . buttonText2 } </span></div></a></a><a id="mailymaily-button-4" class="mailymaily-button" href="#"><div class="mailymaily-button-content"><span id="mailymaily-button-icon-4" class="mailymaily-button-icon"> ${ options . buttonIcon4 } </span> <span id="mailymaily-button-text-4" class="mailymaily-button-text"> ${ options . buttonText4 } </span></div></a></div><div id="mailymaily-copy" class="mailymaily-copy"><div id="mailymaily-email-address" class="mailymaily-email-address"></div><button id="mailymaily-button-copy" class="mailymaily-button-copy" data-copytargetid="mailymaily-email-address"><span id="mailymaily-button-icon-copy" class="mailymaily-button-icon"> ${ options . buttonIconCopy } </span> <span id="mailymaily-button-text-copy" class="mailymaily-button-text"> ${ options . buttonTextCopy } </span></button></div></div></div> ` ;
2020-08-29 14:38:25 +00:00
markup = markup . replace ( /mailymaily/g , app . prefix ( ) ) ;
modal . setAttribute ( 'id' , app . prefix ( '-modal' ) ) ;
modal . setAttribute ( 'class' , app . prefix ( '-modal' ) ) ;
modal . setAttribute ( 'style' , 'display: none;' ) ;
modal . setAttribute ( 'aria-hidden' , true ) ;
modal . innerHTML = markup ;
return modal ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Embed modal on the page .
* /
2020-08-29 14:38:25 +00:00
app . embedModal = function ( ) {
if ( app . modalExists ( ) ) {
return ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
var modal = app . buildModal ( ) ;
var firstBodyChild = document . body . firstChild ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
document . body . insertBefore ( modal , firstBodyChild ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if modal markup has already been embedded on page .
*
* @ return { Boolean } True if modal markup ia already embedded .
* /
2020-08-29 14:38:25 +00:00
app . modalExists = function ( ) {
if ( document . getElementById ( app . prefix ( '-modal' ) ) ) {
return true ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return false ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Get modal populated with data from the given link .
*
* @ param { Element } link The link that was clicked .
* @ return { Element } The modal associated with the given link .
* /
2020-08-29 14:38:25 +00:00
app . getModal = function ( link ) {
app . hydrateModal ( link ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return document . getElementById ( app . prefix ( '-modal' ) ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Populate current modal with data from the link that was clicked .
*
* @ param { Element } link The link that was clicked .
* /
2020-08-29 14:38:25 +00:00
app . hydrateModal = function ( link ) {
var email = app . getEmail ( link ) ;
var subject = app . getLinkField ( link , 'subject' ) ;
var cc = app . getLinkField ( link , 'cc' ) ;
var bcc = app . getLinkField ( link , 'bcc' ) ;
var body = app . getLinkField ( link , 'body' ) ;
var gmail = document . getElementById ( app . prefix ( '-button-1' ) ) ;
gmail . href =
'https://mail.google.com/mail/?view=cm&fs=1&to=' +
email +
'&su=' +
subject +
'&cc=' +
cc +
'&bcc=' +
bcc +
'&body=' +
body ;
var outlook = document . getElementById ( app . prefix ( '-button-2' ) ) ;
outlook . href = ` https://outlook.office.com/owa/?path=/mail/action/compose&to= ${ email } &subject= ${ subject } &body= ${ body } ` ;
var defaultApp = document . getElementById ( app . prefix ( '-button-4' ) ) ;
defaultApp . href = ` mailto: ${ email } ?subject= ${ subject } &cc= ${ cc } &bcc= ${ bcc } &body= ${ body } ` ;
var emailField = document . getElementById ( app . prefix ( '-email-address' ) ) ;
emailField . innerHTML = email ;
var buttonIcon1 = document . getElementById ( app . prefix ( '-button-icon-1' ) ) ;
buttonIcon1 . innerHTML = options . buttonIcon1 ;
var buttonIcon2 = document . getElementById ( app . prefix ( '-button-icon-2' ) ) ;
buttonIcon2 . innerHTML = options . buttonIcon2 ;
var buttonIcon4 = document . getElementById ( app . prefix ( '-button-icon-4' ) ) ;
buttonIcon4 . innerHTML = options . buttonIcon4 ;
var buttonIconCopy = document . getElementById ( app . prefix ( '-button-icon-copy' ) ) ;
buttonIconCopy . innerHTML = options . buttonIconCopy ;
app . toggleHideCopyUi ( email ) ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* When the modal is displayed , the "no-scroll" class sets the body ' s position to fixed . This has the
* side effect of the page getting scrolled to the top . To counter that , we need to save the scroll
* position when the modal is displayed , so it can be restored later on when the modal is closed .
* /
2020-08-29 14:38:25 +00:00
app . savePageScrollPosition = function ( ) {
scrollPosition = window . pageYOffset ;
body . style . top = - scrollPosition + 'px' ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* When the modal is closed , we need to reset the page scroll position . Needed due to
* the position : fixed being set by the "no-scroll" class on the body element when
* the modal is open . Refer to savePageScrollPosition ( ) method for details .
* /
2020-08-29 14:38:25 +00:00
app . restorePageScrollPosition = function ( ) {
window . scrollTo ( 0 , scrollPosition ) ;
body . style . top = 0 ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Save the page ' s current scroll behavior AND set it to auto , in case the current
* scroll behavior is set to smooth . This prevents smooth scrolling from showing
* when scrollPosition is restored via restorePageScrollPosition ( ) method .
* /
2020-08-29 14:38:25 +00:00
app . saveScrollBehavior = function ( ) {
scrollBehavior = html . style . scrollBehavior ;
html . style . scrollBehavior = 'auto' ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Restore the original page scroll behavior saved by saveScrollBehavior ( ) .
* /
2020-08-29 14:38:25 +00:00
app . restoreScrollBehavior = function ( ) {
html . style . scrollBehavior = scrollBehavior ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Save the last doc element to have focus before displaying modal ,
* so that we can reset focus to it when the modal is closed .
* /
2020-08-29 14:38:25 +00:00
app . saveLastDocElementFocused = function ( ) {
lastDocElementFocused = document . activeElement ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Open modal .
*
* @ param { Object } event The object created by the event .
* /
2020-08-29 14:38:25 +00:00
app . openModal = function ( event ) {
if ( options . disableOnMobile && app . isMobileDevice ( ) ) {
return ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
event . preventDefault ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . saveLastDocElementFocused ( ) ;
app . savePageScrollPosition ( ) ;
app . saveScrollBehavior ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . displayModal ( event ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . hideModalFromScreenReader ( false ) ;
app . enablePageScrolling ( false ) ;
app . modalFocus ( ) ;
app . triggerEvent ( modal , 'open' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Display modal and carry out other tasks needed when modal is open .
*
* @ param { Object } event The object created by the event .
* /
2020-08-29 14:38:25 +00:00
app . displayModal = function ( event ) {
var link = app . getParentElement ( event . target , 'a' ) ;
modal = app . getModal ( link ) ;
modal . style . display = 'block' ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Set focus on the first button in the modal .
* /
2020-08-29 14:38:25 +00:00
app . modalFocus = function ( ) {
modal . focusableChildren = Array . from ( modal . querySelectorAll ( focusable ) ) ;
modal . focusableChildren [ 1 ] . focus ( ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Close modal .
*
* @ param { Object } event The object created by the event .
* /
2020-08-29 14:38:25 +00:00
app . closeModal = function ( event ) {
event . preventDefault ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . hideModal ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . enablePageScrolling ( true ) ;
app . restorePageScrollPosition ( ) ;
app . restoreScrollBehavior ( ) ;
app . docRefocus ( ) ;
app . triggerEvent ( modal , 'close' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Hide modal .
* /
2020-08-29 14:38:25 +00:00
app . hideModal = function ( ) {
app . hideModalFromScreenReader ( true ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( app . isDefined ( modal ) ) {
modal . style . display = 'none' ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Set aria attributes to hide modal from screen readers .
*
* @ param { Boolean } hidden True to hide modal from screen reader . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . hideModalFromScreenReader = function ( hidden ) {
if ( app . isDefined ( modal ) ) {
modal . setAttribute ( 'aria-hidden' , hidden ) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Toggle a css class to enable / disable page scrolling .
*
* @ param { Boolean } enabled True to enable page scrolling . False to disable it .
* /
2020-08-29 14:38:25 +00:00
app . enablePageScrolling = function ( enabled ) {
if ( enabled ) {
body . classList . remove ( app . prefix ( '-no-scroll' ) ) ;
html . classList . remove ( app . prefix ( '-no-scroll' ) ) ;
} else {
body . classList . add ( app . prefix ( '-no-scroll' ) ) ;
html . classList . add ( app . prefix ( '-no-scroll' ) ) ;
}
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Set focus back on the last element focused on the page .
* /
2020-08-29 14:38:25 +00:00
app . docRefocus = function ( ) {
lastDocElementFocused . focus ( ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Open the given client link element .
*
* @ param { Element } element The client link that was clicked .
* @ param { Object } event The object created by the event .
* /
2020-08-29 14:38:25 +00:00
app . openClient = function ( element , event ) {
var target = '_blank' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( app . isDefaultEmailAppButton ( element ) ) {
target = '_self' ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
window . open ( element . href , target ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . triggerEvent ( element , 'compose' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( options . autoClose ) {
app . closeModal ( event ) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Determine if the given element is the Default Email App button .
*
* @ param { Element } element The element to be checked .
* @ return { Boolean } True if the given element ' s id corresponds to button 4.
* /
2020-08-29 14:38:25 +00:00
app . isDefaultEmailAppButton = function ( element ) {
return element . id == app . prefix ( '-button-4' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* When an anchor tag ( < a > ) contains other elements , the element returned can vary
* depending on where you click . We need to search up the DOM tree until we find
* the parent anchor tag , which is the element that was intended to be clicked .
*
* @ param { Element } element The element that was clicked .
* @ return { Element } The parent anchor tag of the element that was clicked .
* /
2020-08-29 14:38:25 +00:00
app . getParentElement = function ( element , parentTag ) {
while ( element !== null ) {
if ( element . tagName . toUpperCase ( ) == parentTag . toUpperCase ( ) ) {
return element ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
element = element . parentNode ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return null ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Fire up an event for the given element .
*
* @ param { Element } element Trigger event for this element .
* @ param { String } eventName The name of the event to be triggered .
* /
2020-08-29 14:38:25 +00:00
app . triggerEvent = function ( element , eventName ) {
var event = new Event ( eventName ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
element . dispatchEvent ( event ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if device is a mobile phone or tablet .
* /
2020-08-29 14:38:25 +00:00
app . isMobileDevice = function ( ) {
var check = false ;
( 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|android|ipad|playbook|silk/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 ) ;
return check ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Listen for events .
* /
2020-08-29 14:38:25 +00:00
app . listenForEvents = function ( ) {
app . listenForClickOnLink ( ) ;
app . listenForClickOnClient ( ) ;
app . listenForClickOnCopy ( ) ;
app . listenForClickOnClose ( ) ;
app . listenForClickOnWindow ( ) ;
app . listenForKeys ( ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Listen for click event on mailto links .
* /
2020-08-29 14:38:25 +00:00
app . listenForClickOnLink = function ( ) {
var links = document . getElementsByClassName ( app . prefix ( ) ) ;
for ( var i = 0 ; i < links . length ; i ++ ) {
links [ i ] . addEventListener (
'click' ,
function ( event ) {
app . openModal ( event ) ;
} ,
false
) ;
}
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Listen for click event on client links .
* /
2020-08-29 14:38:25 +00:00
app . listenForClickOnClient = function ( ) {
var clients = document . getElementsByClassName ( app . prefix ( '-button' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
for ( var i = 0 ; i < clients . length ; i ++ ) {
clients [ i ] . addEventListener (
'click' ,
function ( event ) {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
var client = app . getParentElement ( event . target , 'a' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . openClient ( client , event ) ;
} ,
false
) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Listen for click event on Copy button .
* /
2020-08-29 14:38:25 +00:00
app . listenForClickOnCopy = function ( ) {
var copyButton = document . getElementById ( app . prefix ( '-button-copy' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
copyButton . addEventListener (
'click' ,
function ( event ) {
app . copy ( event ) ;
} ,
false
) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Listen for click event on Close button .
* /
2020-08-29 14:38:25 +00:00
app . listenForClickOnClose = function ( ) {
var closeButton = document . getElementById ( app . prefix ( '-modal-close' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
closeButton . addEventListener (
'click' ,
function ( event ) {
app . closeModal ( event ) ;
} ,
false
) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Listen for click event on window ( to close modal ) .
* /
2020-08-29 14:38:25 +00:00
app . listenForClickOnWindow = function ( ) {
window . addEventListener (
'click' ,
function ( event ) {
var element = event . target ;
if ( element !== null && element . classList . contains ( app . prefix ( '-modal' ) ) ) {
app . closeModal ( event ) ;
}
} ,
false
) ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Listen for keydown events to escape modal or tab within it .
* /
2020-08-29 14:38:25 +00:00
app . listenForKeys = function ( ) {
document . addEventListener (
'keydown' ,
function ( event ) {
app . escapeModal ( event ) ;
app . trapTabWithinModal ( event ) ;
} ,
false
) ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Close modal when Esc key is pressed .
*
* @ param { KeyboardEvent } event The event generated by pressing a key .
* /
2020-08-29 14:38:25 +00:00
app . escapeModal = function ( event ) {
if ( event . keyCode === 27 ) {
app . closeModal ( event ) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Should not be able to tab outside the modal . Pressing the tab
* key moves focus to the next focusable element within modal .
*
* @ param { KeyboardEvent } event The event generated by pressing a key .
* /
2020-08-29 14:38:25 +00:00
app . trapTabWithinModal = function ( event ) {
if ( event . keyCode === 9 && modal !== null ) {
var currentFocus = document . activeElement ;
var totalFocusable = modal . focusableChildren . length ;
var focusedIndex = modal . focusableChildren . indexOf ( currentFocus ) ;
if ( event . shiftKey ) {
if ( focusedIndex === 0 ) {
event . preventDefault ( ) ;
modal . focusableChildren [ totalFocusable - 1 ] . focus ( ) ;
}
} else {
if ( focusedIndex == totalFocusable - 1 ) {
event . preventDefault ( ) ;
modal . focusableChildren [ 0 ] . focus ( ) ;
}
}
}
} ;
/ * *
2020-08-29 11:59:02 +00:00
* Get all "mailymaily" links on the page .
2020-08-29 11:55:43 +00:00
*
2020-08-29 11:59:02 +00:00
* @ return { HTMLCollection } All links with the class "mailymaily" ( default ) .
2020-08-29 11:55:43 +00:00
* /
2020-08-29 14:38:25 +00:00
app . getLinks = function ( ) {
return document . getElementsByClassName ( app . prefix ( ) ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Split the URL scheme of given link in two strings : the email address , and the
* key - value query string . Also remove 'mailto:' to get nice clean values .
*
* @ param { Element } link The link element clicked .
* @ return { Array } The two parts of the link scheme separated at '?' .
* /
2020-08-29 14:38:25 +00:00
app . splitLink = function ( link ) {
var scheme = link . href . replace ( 'mailto:' , '' ) . trim ( ) ;
var parts = scheme . split ( '?' , 1 ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( parts !== null && parts . length > 0 ) {
parts [ 1 ] = scheme . replace ( parts [ 0 ] + '?' , '' ) . trim ( ) ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return parts ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Extract the value of the given field from the link .
*
* @ param { Element } link The link element clicked .
* @ param { String } field The name of the field we want to get .
* @ return { String } The value corresponding to the given field .
* /
2020-08-29 14:38:25 +00:00
app . getLinkField = function ( link , field ) {
var parts = app . splitLink ( link ) ;
var query = '' ;
var terms = [ ] ;
var keyValues = [ ] ;
var value = '' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( parts !== null && parts . length > 0 ) {
query = parts [ 1 ] ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( query !== null && query . length > 0 ) {
// Encode any instance of ' & ' inside field values to prevent spliting at the wrong place.
query = query . replace ( '%20&%20' , '%20%26%20' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
terms = query . split ( '&' ) ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
for ( var i = 0 ; i < terms . length ; i ++ ) {
// Encode any instance of ' = ' inside field values to prevent spliting at the wrong place.
terms [ i ] = terms [ i ] . replace ( '%20=%20' , '%20%3D%20' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
keyValues = terms [ i ] . split ( '=' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
for ( var n = 0 ; n < keyValues . length ; n ++ ) {
if ( keyValues [ 0 ] == field ) {
return keyValues [ 1 ] ;
}
}
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return value ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Extract email address from mailto string .
*
* @ param { Element } link The link element clicked .
* @ return { String } The email address .
* /
2020-08-29 14:38:25 +00:00
app . getEmail = function ( link ) {
var parts = app . splitLink ( link ) ;
var email = '' ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( parts !== null && parts . length > 0 ) {
email = parts [ 0 ] ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return decodeURIComponent ( email ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Build and return the class name used to hide the Copy UI .
*
* @ return { String } The CSS class name needed to hide the Copy UI .
* /
2020-08-29 14:38:25 +00:00
app . getClassHideCopyUi = function ( ) {
return app . prefix ( '-is-hidden' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Show or hide the Copy UI based on email address presence .
*
* @ param { String } email The email address to be checked .
* /
2020-08-29 14:38:25 +00:00
app . toggleHideCopyUi = function ( email ) {
var copyUi = document . getElementById ( app . prefix ( '-copy' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( email . length == 0 ) {
copyUi . classList . add ( app . getClassHideCopyUi ( ) ) ;
} else {
copyUi . classList . remove ( app . getClassHideCopyUi ( ) ) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Set copy button text to indicate the email address has been copied .
* /
2020-08-29 14:38:25 +00:00
app . toggleCopyButton = function ( ) {
button = document . getElementById ( app . prefix ( '-button-copy' ) ) ;
buttonText = document . getElementById ( app . prefix ( '-button-text-copy' ) ) ;
buttonText . innerHTML = options . buttonTextCopyAction ;
button . classList . add ( app . prefix ( '-button-copy-clicked' ) ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
setTimeout ( function ( ) {
buttonText . innerHTML = options . buttonTextCopy ;
button . classList . remove ( app . prefix ( '-button-copy-clicked' ) ) ;
} , 600 ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Copy email address to the clipboard .
*
* @ param { String } event The event generated by clicking on Copy button .
* /
2020-08-29 14:38:25 +00:00
app . copy = function ( event ) {
event . preventDefault ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
var targetId = app . getParentElement ( event . target , 'button' ) . getAttribute ( 'data-copytargetid' ) ;
var email = document . getElementById ( targetId ) ;
var range = document . createRange ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
range . selectNodeContents ( email ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
var selection = window . getSelection ( ) ;
selection . removeAllRanges ( ) ;
selection . addRange ( range ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
document . execCommand ( 'copy' ) ;
app . triggerEvent ( email , 'copy' ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . toggleCopyButton ( ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if device is running iOS .
*
* @ return { Boolean } True if device runs iOS .
* /
2020-08-29 14:38:25 +00:00
app . isiOSDevice = function ( ) {
return navigator . userAgent . match ( /ipad|iphone/i ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Get user options provided as an object parameter in the run
* method , or as a JSON string provided in a data attribute
* of the script tag . Save all in the options object .
*
* @ param { Object } optionsObj An object containing user options .
* /
2020-08-29 14:38:25 +00:00
app . setOptions = function ( optionsObj ) {
if ( optionsObj ) {
var userOptions = JSON . stringify ( optionsObj ) ;
} else {
var userOptions = app . getOptionsFromScriptTag ( ) ;
}
if ( userOptions && userOptions . trim ( ) . length > 0 ) {
userOptions = JSON . parse ( userOptions ) ;
for ( var name in options ) {
if ( userOptions . hasOwnProperty ( name ) ) {
options [ name ] = app . sanitizeUserOption ( name , userOptions [ name ] ) ;
}
}
}
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Clean up given user options .
*
* @ param { String } name The name of the user option to be sanitized .
* @ param { mixed } value The value of the user option .
* @ return { mixed } The sanitized value when applicable , or the original value .
* /
2020-08-29 14:38:25 +00:00
app . sanitizeUserOption = function ( name , value ) {
if ( app . stringContains ( name , 'icon' ) ) {
return app . validateSvg ( name , value ) ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
if ( app . isString ( value ) ) {
return app . stripHtml ( value ) ;
}
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return value ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check svg file for possible problems or tampering .
*
* @ param { String } name The name of an 'icon' user option ( must exist in options array ) .
* @ param { String } url The url to an svg file .
* /
2020-08-29 14:38:25 +00:00
app . validateSvg = function ( name , url ) {
app
. getSvg ( name , url )
. then ( function ( promise ) {
if ( ! app . stringIsSvg ( promise . responseText ) ) {
throw new Error ( name + ': ' + url + ' is not an SVG file.' ) ;
}
if ( app . stringHasScript ( promise . responseText ) ) {
throw new Error ( name + ': ' + url + ' is an invalid SVG file.' ) ;
} else {
options [ name ] = promise . responseText ;
}
} )
. catch ( function ( error ) {
if ( name == 'buttonIconCopy' ) {
options [ name ] = clipboardSvg ;
} else {
options [ name ] = worldSvg ;
}
console . log ( error ) ;
} ) ;
} ;
/ * *
2020-08-29 11:55:43 +00:00
* Load an svg file from the given url .
*
* @ param { String } name The name of an 'icon' user option ( must exist in options array ) .
* @ param { String } url The url to an svg file .
* @ return { Promise } The resolved or rejected promise after ajax call to load svg file .
* /
2020-08-29 14:38:25 +00:00
app . loadSvg = function ( name , url ) {
return new Promise ( ( resolve , reject ) => {
var ajax = new XMLHttpRequest ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
ajax . open ( 'GET' , url , true ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
ajax . onload = function ( event ) {
if ( ajax . status == 200 ) {
resolve ( ajax ) ;
} else {
reject ( ajax ) ;
}
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
ajax . send ( ) ;
} ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Async method to load svg file from given url .
*
* @ param { String } name The name of an 'icon' user option ( must exist in options array ) .
* @ param { String } url The url to an svg file .
* @ return { Promise } The Promise with the result returned by loadSvg ( ) .
* /
2020-08-29 14:38:25 +00:00
app . getSvg = async function ( name , url ) {
return await app . loadSvg ( name , url ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if given value is a string .
*
* @ param { mixed } value The value to be checked .
* @ return { Boolean } True if value is a string . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . isString = function ( value ) {
return typeof value === 'string' ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Remove html tags from given string .
*
* @ param { String } text The string to be stripped .
* @ return { String } The given string stripped off markup tags .
* /
2020-08-29 14:38:25 +00:00
app . stripHtml = function ( text ) {
return text . replace ( /(<([^>]+)>)/g , '' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if a string contains another string ( "needle in a haystack" ) .
*
* @ param { String } haystack The string where to search .
* @ param { String } needle The string to search for .
* @ return { Boolean } True if needle is found in haystack . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . stringContains = function ( haystack , needle ) {
return haystack . toLowerCase ( ) . indexOf ( needle . toLowerCase ( ) ) !== - 1 ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if given string is an svg file .
*
* @ param { String } text The string to be checked .
* @ return { Boolean } True if string starts with '<svg' . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . stringIsSvg = function ( text ) {
return text . startsWith ( '<svg' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if given string contains an embedded < script > tag .
*
* @ param { String } text The string to be checked .
* @ return { Boolean } True if given string contains < script > . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . stringHasScript = function ( text ) {
return app . stringContains ( text , '<script' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Check if the given item is defined .
*
* @ param { mixed } item The item to be checked .
* @ return { Boolean } True if the given item is defined . False otherwise .
* /
2020-08-29 14:38:25 +00:00
app . isDefined = function ( item ) {
return typeof item !== 'undefined' ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Read options passed in the data - options attribute of the script tag .
*
* @ return { String } Options string provided in JSON format .
* /
2020-08-29 14:38:25 +00:00
app . getOptionsFromScriptTag = function ( ) {
var scripts = document . getElementsByTagName ( 'script' ) ;
var scriptName = scripts [ scripts . length - 1 ] ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
return scriptName . getAttribute ( 'data-options' ) ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Append the linkClass user option to the given string .
*
* @ param { String } text The string to which the linkClass will be appended .
* @ return { String } The linkClass user option appended to the given string .
* /
2020-08-29 14:38:25 +00:00
app . prefix = function ( text = '' ) {
return options . linkClass + text ;
} ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
/ * *
2020-08-29 11:55:43 +00:00
* Let ' s kick things off .
* @ param { Object } optionsObj An object containing user options .
* /
2020-08-29 14:38:25 +00:00
app . run = function ( optionsObj = null ) {
app . setOptions ( optionsObj ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . embedModal ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . embedStyleTag ( ) ;
2020-08-29 11:55:43 +00:00
2020-08-29 14:38:25 +00:00
app . listenForEvents ( ) ;
} ;
2020-08-29 11:59:02 +00:00
} ) ( mailymailyApp ) ;
2020-08-29 11:55:43 +00:00
/ * *
2020-08-29 11:59:02 +00:00
* Are we loaded in the browser ? If so , run mailymaily automatically .
* Otherwise , make mailymaily available to the outside world so
2020-08-29 11:55:43 +00:00
* it can be triggered ( run ) manually when appropriate .
* /
if ( typeof module !== 'undefined' && typeof module . exports !== 'undefined' ) {
2020-08-29 14:38:25 +00:00
// We're not in the browser.
module . exports = {
run : mailymailyApp . run
} ;
2020-08-29 11:55:43 +00:00
} else {
2020-08-29 14:38:25 +00:00
// We're in the browser.
mailymailyApp . run ( ) ;
2020-08-29 11:55:43 +00:00
}