Donleeve is a library for exit intent actions!
15 |On this page, exit intent can occur more than one time, but it has a 1 minute block. Additionally, the global block from the home page still applies.
16 | 17 | 24 |" + text + "
"; 22 | logger.scrollTop = logger.scrollHeight; 23 | } 24 | 25 | purgeLink.addEventListener("click", function (e) { 26 | e.preventDefault(); 27 | 28 | Donleeve.purgeBlocks(Date.now()); 29 | log("All blocks have been purged!"); 30 | }); 31 | 32 | Donleeve.onTrigger = function (e) { 33 | log("" + getNow() + " " + e.type, logsTriggers); 34 | }; 35 | 36 | Donleeve.onStorageBlock = function (str, time) { 37 | log("Blocked on " + str + " URLs for " + time + " milliseconds."); 38 | }; 39 | 40 | Donleeve.init({ 41 | bindDelay: 2000, 42 | ignoreFlagBlocking: true 43 | }, function (e) { 44 | Modalite.open("the-modal"); 45 | log("Exit intent activated by " + e.type + "!"); 46 | 47 | if (!Donleeve.options.ignoreFlagBlocking) { 48 | log("On this page, an option is set that prevents multiple exit intents to appear on one page load. Refresh the page to see how Storage Blocking works. The block will last " + Math.ceil(Donleeve.options.storageBlockingMinutes * 60) + " seconds."); 49 | } 50 | }); 51 | 52 | ActiveTimeout.set(function () { 53 | log("Events bound!"); 54 | }, function (left) { 55 | if (left < 0) left = 0; 56 | timer.innerHTML = Math.ceil(left / 1000); 57 | }, Donleeve.options.bindDelay); -------------------------------------------------------------------------------- /dist/donleeve.min.js: -------------------------------------------------------------------------------- 1 | window.Donleeve=function(){function n(n){return 60*n*1e3}function e(n){return"number"==typeof n?n-Date.now():0}function o(){var n;try{n=JSON.parse(localStorage[d])}catch(n){console.warn("Couldn't parse blocks JSON.")}return Array.isArray(n)||(t(n=[]),console.warn("Blocks were not an array.")),n}function t(n){for(var o=n.length-1;o>=0;o--)e(n[o][1])<=0&&n.splice(o,1);localStorage[d]=JSON.stringify(n)}function i(){if(f){var e=o(),i=n(s.storageBlockingMinutes);e.push([s.storageBlockingRegex,Date.now()+i]),t(e)}}function r(){if(!f)return!1;var n=!1;return o().forEach(function(o){var t=o[0],i=o[1];if(t===g||null!==new RegExp(t).exec(window.location.href)){var r=e(i);r>0&&(n=!0,"function"==typeof v.onStorageBlock&&v.onStorageBlock(t,r))}}),n}function c(){return!!v.enabled&&(!(!s.ignoreFlagBlocking&&v.acted)&&!(!s.ignoreStorageBlocking&&r()))}function u(n){"function"==typeof v.onAction&&!1!==v.onAction(n)&&(v.acted=!0,i())}function l(n){"function"==typeof v.onTrigger&&v.onTrigger(n),!0===c()&&u(n)}function a(){s.bindEventBlur&&window.addEventListener("blur",l),s.bindEventMouseLeave&&document.documentElement.addEventListener("mouseleave",function(n){(n.y<=0||n.clientY<=0)&&l(n)}),s.bindEventMouseMove&&document.documentElement.addEventListener("mousemove",function(n){n.movementY<0&&(n.y<=0||n.movementY<-n.y)&&l(n)}),"function"==typeof v.onBound&&v.onBound()}var g="*",f="undefined"!=typeof localStorage,d="donleevBlocks",s={bindDelay:3e3,bindEventBlur:!0,bindEventMouseLeave:!0,bindEventMouseMove:!0,storageBlockingRegex:g,storageBlockingMinutes:10,ignoreStorageBlocking:!1,ignoreFlagBlocking:!1},v={options:s,enabled:!0,acted:!1,onBound:null,onTrigger:null,onStorageBlock:null,onAction:null};return v.setOptions=function(n){if("object"==typeof n&&null!==n)for(var e in n)s[e]=n[e]},v.init=function(n,e){"function"==typeof n&&(e=n,n=void 0),v.setOptions(n),v.onAction=e,ActiveTimeout.set(function(){a()},s.bindDelay)},v.purgeBlocks=function(n){f&&localStorage.donleevPurge!==n&&(localStorage.donleevPurge=n,t([]),console.warn("Blocks purged with string:",n))},v}(); 2 | -------------------------------------------------------------------------------- /docs/donleeve.min.js: -------------------------------------------------------------------------------- 1 | window.Donleeve=function(){function n(n){return 60*n*1e3}function e(n){return"number"==typeof n?n-Date.now():0}function o(){var n;try{n=JSON.parse(localStorage[d])}catch(n){console.warn("Couldn't parse blocks JSON.")}return Array.isArray(n)||(t(n=[]),console.warn("Blocks were not an array.")),n}function t(n){for(var o=n.length-1;o>=0;o--)e(n[o][1])<=0&&n.splice(o,1);localStorage[d]=JSON.stringify(n)}function i(){if(f){var e=o(),i=n(s.storageBlockingMinutes);e.push([s.storageBlockingRegex,Date.now()+i]),t(e)}}function r(){if(!f)return!1;var n=!1;return o().forEach(function(o){var t=o[0],i=o[1];if(t===g||null!==new RegExp(t).exec(window.location.href)){var r=e(i);r>0&&(n=!0,"function"==typeof v.onStorageBlock&&v.onStorageBlock(t,r))}}),n}function c(){return!!v.enabled&&(!(!s.ignoreFlagBlocking&&v.acted)&&!(!s.ignoreStorageBlocking&&r()))}function u(n){"function"==typeof v.onAction&&!1!==v.onAction(n)&&(v.acted=!0,i())}function l(n){"function"==typeof v.onTrigger&&v.onTrigger(n),!0===c()&&u(n)}function a(){s.bindEventBlur&&window.addEventListener("blur",l),s.bindEventMouseLeave&&document.documentElement.addEventListener("mouseleave",function(n){(n.y<=0||n.clientY<=0)&&l(n)}),s.bindEventMouseMove&&document.documentElement.addEventListener("mousemove",function(n){n.movementY<0&&(n.y<=0||n.movementY<-n.y)&&l(n)}),"function"==typeof v.onBound&&v.onBound()}var g="*",f="undefined"!=typeof localStorage,d="donleevBlocks",s={bindDelay:3e3,bindEventBlur:!0,bindEventMouseLeave:!0,bindEventMouseMove:!0,storageBlockingRegex:g,storageBlockingMinutes:10,ignoreStorageBlocking:!1,ignoreFlagBlocking:!1},v={options:s,enabled:!0,acted:!1,onBound:null,onTrigger:null,onStorageBlock:null,onAction:null};return v.setOptions=function(n){if("object"==typeof n&&null!==n)for(var e in n)s[e]=n[e]},v.init=function(n,e){"function"==typeof n&&(e=n,n=void 0),v.setOptions(n),v.onAction=e,ActiveTimeout.set(function(){a()},s.bindDelay)},v.purgeBlocks=function(n){f&&localStorage.donleevPurge!==n&&(localStorage.donleevPurge=n,t([]),console.warn("Blocks purged with string:",n))},v}(); 2 | -------------------------------------------------------------------------------- /docs/node_modules/modalite/dist/modalite.min.css: -------------------------------------------------------------------------------- 1 | @keyframes modalite-spinner-animation{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.modal{display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.8);position:fixed;z-index:9999;top:0;left:0;width:100%;height:100%;box-sizing:border-box;padding:3rem;visibility:hidden;opacity:0;transition:visibility 0.3s linear, opacity 0.3s ease-out}.modal.modal-visible{visibility:visible;opacity:1}.modal-container{position:relative;background:#eee;border-radius:5px;width:100%;height:100%;max-width:650px;max-width:40rem;max-height:500px;max-height:30rem;transform:scale(0.9);transition:transform 0.3s ease-out}.modal.modal-visible .modal-container{transform:scale(1)}.modal.modal-remote .modal-container:before{content:"";display:none;background:#eee;position:absolute;left:0;top:0;width:100%;height:100%;border-radius:5px;visibility:visible;opacity:0.9;transition:all 0.3s ease-out}.modal.modal-remote.modal-visible .modal-container:before{display:block}.modal.modal-remote.modal-loaded .modal-container:before{visibility:hidden;opacity:0}.modal.modal-remote .modal-container:after{content:"";background:url("assets/spinner.png");animation:modalite-spinner-animation 0.8s linear infinite;margin-left:-15px;margin-right:-15px;width:30px;height:30px;position:absolute;top:50%;left:50%;visibility:hidden;opacity:0;transition:all 0.3s ease-out}.modal.modal-remote.modal-loading .modal-container:after{visibility:visible;opacity:1}.modal-container .modal-close{position:absolute;top:-16px;right:-16px}.modal-close{background-color:#d12626;background-image:url("assets/close.png");background-repeat:no-repeat;background-position:center center;display:inline-block;width:32px;height:32px;border-radius:32px;transition:all 0.3s ease-out;cursor:pointer}.modal-close:hover{background-color:#ea3c3c}.modal-content{width:100%;height:100%;box-sizing:border-box;padding:2rem;overflow:auto}@media (max-width: 480px){.modal{display:block;padding:1.5rem}.modal-container{max-height:none}.modal-content{padding:1rem}} 2 | /*# sourceMappingURL=modalite.min.css.map */ 3 | -------------------------------------------------------------------------------- /docs/node_modules/modalite/dist/modalite.min.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "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", 4 | "sources": ["modalite.scss"], 5 | "names": [], 6 | "file": "modalite.min.css" 7 | } 8 | -------------------------------------------------------------------------------- /docs/node_modules/modalite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "modalite@^1.1.0", 5 | "C:\\Users\\Hristiyan\\Desktop\\focustest" 6 | ] 7 | ], 8 | "_from": "modalite@>=1.1.0 <2.0.0", 9 | "_id": "modalite@1.2.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/modalite", 13 | "_nodeVersion": "6.1.0", 14 | "_npmOperationalInternal": { 15 | "host": "s3://npm-registry-packages", 16 | "tmp": "tmp/modalite-1.2.0.tgz_1501571236685_0.36102276272140443" 17 | }, 18 | "_npmUser": { 19 | "email": "h.dodov@gmail.com", 20 | "name": "hdodov" 21 | }, 22 | "_npmVersion": "3.8.6", 23 | "_phantomChildren": {}, 24 | "_requested": { 25 | "name": "modalite", 26 | "raw": "modalite@^1.1.0", 27 | "rawSpec": "^1.1.0", 28 | "scope": null, 29 | "spec": ">=1.1.0 <2.0.0", 30 | "type": "range" 31 | }, 32 | "_requiredBy": [ 33 | "/" 34 | ], 35 | "_shasum": "90ebbc3ab52543b8d7526d6a76a36b1e82df7968", 36 | "_shrinkwrap": null, 37 | "_spec": "modalite@^1.1.0", 38 | "_where": "C:\\Users\\Hristiyan\\Desktop\\focustest", 39 | "author": { 40 | "name": "Hristiyan Dodov" 41 | }, 42 | "bugs": { 43 | "url": "https://github.com/hdodov/modalite/issues" 44 | }, 45 | "dependencies": {}, 46 | "description": "The most simple and flexible modal library, now with support for remote resources and iframes.", 47 | "devDependencies": {}, 48 | "directories": {}, 49 | "dist": { 50 | "shasum": "90ebbc3ab52543b8d7526d6a76a36b1e82df7968", 51 | "tarball": "https://registry.npmjs.org/modalite/-/modalite-1.2.0.tgz" 52 | }, 53 | "gitHead": "f17083ae053ca16dc6c096e9843792db3e63ec39", 54 | "homepage": "https://github.com/hdodov/modalite#readme", 55 | "keywords": [ 56 | "modal", 57 | "dialog", 58 | "box", 59 | "overlay", 60 | "page", 61 | "remote", 62 | "external", 63 | "resource", 64 | "iframe", 65 | "ajax", 66 | "xhr", 67 | "css", 68 | "flexible", 69 | "simple", 70 | "easy", 71 | "quick", 72 | "fast", 73 | "smooth" 74 | ], 75 | "license": "MIT", 76 | "main": "index.js", 77 | "maintainers": [ 78 | { 79 | "email": "h.dodov@gmail.com", 80 | "name": "hdodov" 81 | } 82 | ], 83 | "name": "modalite", 84 | "optionalDependencies": {}, 85 | "readme": "ERROR: No README data found!", 86 | "repository": { 87 | "type": "git", 88 | "url": "git+https://github.com/hdodov/modalite.git" 89 | }, 90 | "scripts": { 91 | "test": "echo \"Error: no test specified\" && exit 1" 92 | }, 93 | "version": "1.2.0" 94 | } 95 | -------------------------------------------------------------------------------- /docs/node_modules/active-timeout.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "active-timeout.js", 5 | "C:\\Users\\Hristiyan\\Desktop\\donleeve\\docs" 6 | ] 7 | ], 8 | "_from": "active-timeout.js@latest", 9 | "_id": "active-timeout.js@2.0.0", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/active-timeout.js", 13 | "_nodeVersion": "6.1.0", 14 | "_npmOperationalInternal": { 15 | "host": "s3://npm-registry-packages", 16 | "tmp": "tmp/active-timeout.js-2.0.0.tgz_1501414596837_0.4918554031755775" 17 | }, 18 | "_npmUser": { 19 | "email": "h.dodov@gmail.com", 20 | "name": "hdodov" 21 | }, 22 | "_npmVersion": "3.8.6", 23 | "_phantomChildren": {}, 24 | "_requested": { 25 | "name": "active-timeout.js", 26 | "raw": "active-timeout.js", 27 | "rawSpec": "", 28 | "scope": null, 29 | "spec": "latest", 30 | "type": "tag" 31 | }, 32 | "_requiredBy": [ 33 | "#USER" 34 | ], 35 | "_resolved": "file:active-timeout.js", 36 | "_shasum": "6e758bc44f11490a67e2835c3de0d1f919dacd7c", 37 | "_shrinkwrap": null, 38 | "_spec": "active-timeout.js", 39 | "_where": "C:\\Users\\Hristiyan\\Desktop\\donleeve\\docs", 40 | "author": { 41 | "name": "Hristiyan Dodov" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/hdodov/active-timeout.js/issues" 45 | }, 46 | "dependencies": {}, 47 | "description": "JavaScript library that lets you measure time the user has spent viewing your page. Inactive time will not be counted.", 48 | "devDependencies": {}, 49 | "directories": {}, 50 | "dist": { 51 | "shasum": "6e758bc44f11490a67e2835c3de0d1f919dacd7c", 52 | "tarball": "https://registry.npmjs.org/active-timeout.js/-/active-timeout.js-2.0.0.tgz" 53 | }, 54 | "gitHead": "7c56922643c425d70252300a4c43edbfc2b03be8", 55 | "homepage": "https://github.com/hdodov/active-timeout.js#readme", 56 | "keywords": [ 57 | "javascript", 58 | "active", 59 | "accurate", 60 | "measure", 61 | "timeout", 62 | "interval", 63 | "analytics", 64 | "visibility", 65 | "settimeout", 66 | "setinterval", 67 | "wait", 68 | "delay", 69 | "viewing", 70 | "view" 71 | ], 72 | "license": "MIT", 73 | "main": "index.js", 74 | "maintainers": [ 75 | { 76 | "email": "h.dodov@gmail.com", 77 | "name": "hdodov" 78 | } 79 | ], 80 | "name": "active-timeout.js", 81 | "optionalDependencies": {}, 82 | "readme": "ERROR: No README data found!", 83 | "repository": { 84 | "type": "git", 85 | "url": "git+https://github.com/hdodov/active-timeout.js.git" 86 | }, 87 | "scripts": { 88 | "test": "echo \"Error: no test specified\" && exit 1" 89 | }, 90 | "version": "2.0.0" 91 | } 92 | -------------------------------------------------------------------------------- /docs/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |On this page, exit intent can occur more than one time, but it has a 1 minute block. Additionally, the global block from the home page still applies.
16 | 17 | 24 |Active means the user must be viewing the page. If he opened your site in a new tab for later reading, the counter will start when he focuses the tab. This functionality is achieved via the active-timeout.js dependency.
30 |For better user experience, Donleeve uses localStorage to prevent exit intents from popping up too often. You can specify parts of the URL to block with regex if you have different modals on your pages and you want a different block for each. For example, take a look at these links:
Active means the user must be viewing the page. If he opened your site in a new tab for later reading, the counter will start when he focuses the tab. This functionality is achieved via the active-timeout.js dependency.
30 |Here, the block time will be different based on the parameter in the URL. If it's equal to 3, the block will be 3 seconds, for example. In your website, you can utilize this for A/B testing. Notice, however, that the block on ?p=10 doesn't affect a page with ?p=7, it only affects occurences of ?p=10 in the URL.
Active means the user must be viewing the page. If he opened your site in a new tab for later reading, the counter will start when he focuses the tab. This functionality is achieved via the active-timeout.js dependency.
30 |Click me for a juicy modal!
52 | ``` 53 | 54 | **Note: If you have a remote modal, it won't have the loading spinner and overlay unless you add the `modal-remote` class to the element with the `data-modal` attribute.** 55 | 56 | ## Building a modal with your own CSS 57 | To create a modal, you simply need to: 58 | 59 | 1. Add an element with the `data-modal` attribute and give it an `id`. Style it so that it's hidden. You can use the `visibility` CSS property. It's good for the job, because it's affected by transitions and makes animations a whole lot easier. Add another rule-set for when the element has the `modal-visible` class. This should define its visible behaviour. 60 | 2. Add an element with the `data-open-modal` attribute and set its value to equal the previously created modal's `id`. When clicked, this will add the `modal-visible` class to the targeted modal. 61 | 3. **Optionally**, you can add a close button for the modal. By default, it will be closed when its background is clicked, as this is expected modal behaviour. To add a close functionality, simply add any element **inside** the modal and give it the `data-close-modal` attribute. 62 | 63 | For example, this would be a fully functional modal: 64 | 65 | ```html 66 |Close me!
68 |Open the modal!
71 | ``` 72 | 73 | You just need to add some CSS to make the modal is invisible by default and add some styles that show it when it has the `modal-visible` class. 74 | 75 | # Remote modals 76 | They rock! Have a 5MB Privacy Policy? Remote modals are the way. No need to demolish your page loading times with something nobody's going to read. **Ever.** Simply load all of that text when the modal is opened. This way, if your visitor isn't interested in your Privacy Policy, he wouldn't have to wait for it to load in those crucial first seconds of his visit. 77 | 78 | ## How do remote modals work? 79 | When the modal is opened, its contents are searched for elements with the `data-modal-remote` attribute. It should hold the URL to the desired resource. However, DOM operations can be costly, especially for large elements. Suppose you decided to actually splatter that Privacy Policy directly in your modal. It could have thousands of elements... searching for an attribute that might not even exist will be a huge overhead. 80 | 81 | That's why the remote modal must have the `data-modal-has-remote` attribute. Without it, you'll just be having a regular modal. No DOM searches will be performed whatsoever. 82 | 83 | When an element with `data-modal-remote` is found, two things can happen: 84 | 85 | - If the element is **not** an `