diff --git a/README.md b/README.md index 187634ba..c166fd9d 100644 --- a/README.md +++ b/README.md @@ -299,12 +299,12 @@ new TypeIt('#element', { | cursorSpeed | (number in milliseconds) The blinking speed of the cursor. | 1000 | | cursorChar | (string) The character used for the cursor. HTML works too! | pipe | | breakLines | (boolean) Choose whether you want multiple strings to be printed on top of each other (`breakLines: true`), or if you want each string to be deleted and replaced by the next one (`breakLines: false`). | true | -| nextStringDelay | (number in milliseconds or array) The amount of time (milliseconds) between typing the next string when multiple strings are defined. You may either pass a number in milliseconds, or an array of values. The first value will be used as the delay before a new string starts, and the second value will be used as the delay after a string ends. For example, passing `[1000, 2000]` will tell TypeIt to pause 1000ms before typing a new string, and wait 2000ms after a string has just completed. | 750 | +| nextStringDelay | (number in milliseconds or array) The amount of time (milliseconds) between typing strings when multiple are defined. You may either pass a number in milliseconds, or an array of values. The first value will be used as the delay before a new string starts, and the second value will be used as the delay after a string has just ended. For example, passing `[1000, 2000]` will tell TypeIt to pause 1000ms before typing a new string, and wait 2000ms after a string has just completed. This would be equivalent to `instance.type('String 1').pause(2000).delete().pause(1000).type('String 2')`. If a number is passed, that value will be halved. | 750 | | autoStart | (boolean) Determines if the instance will typing automatically on page load, or only when the target element becomes visible in the viewport. If you don't want instances far down on the page to begin until they're visible, set this option to `false.` | true | | startDelete | (boolean) Whether to begin instance by deleting strings inside element, and then typing what strings are defined via JSON or companion functions. | false | | startDelay | (number in milliseconds) The amount of time before the plugin begins typing after initalizing. | 250 | | loop | (boolean) Have your string or strings continuously loop after completing. | false | -| loopDelay | (number in milliseconds) The amount of time between looping over a string or set of strings again. | 750 | +| loopDelay | (number in milliseconds or array) The amount of time between looping over a string or set of strings again. This option behaves identically to `nextStringDelay`. If an array is passed, the first value will be the time before typing begins again (after the set of strings has been deleted), and the second value will be the time immediately after the set of strings has finished typing, before they're deleted to restart. If left undefined, the `nextStringDelay` option will be used. | false | | html | (boolean) Handle strings as HTML, which will process tags and HTML entities. If 'false,' strings will be typed literally. | true | #### Changing Option Defaults diff --git a/dist/typeit.es.js b/dist/typeit.es.js index 72d690dd..e8164053 100644 --- a/dist/typeit.es.js +++ b/dist/typeit.es.js @@ -2,7 +2,7 @@ * * typeit - The most versatile animated typing utility on the planet. * Author: Alex MacArthur (https://macarthur.me) - * Version: v5.9.0 + * Version: v5.10.0 * URL: https://typeitjs.com * License: GPL-2.0 * @@ -81,7 +81,7 @@ window.TypeItDefaults = { startDelete: false, nextStringDelay: 750, loop: false, - loopDelay: 750, + loopDelay: false, html: true, autoStart: true, callback: false, @@ -140,21 +140,15 @@ var Instance = function () { this.style = "display:inline;position:relative;font:inherit;color:inherit;"; this.element = element; this.setOptions(options, window.TypeItDefaults, false); - this.checkElement(); - this.setNextStringDelay(); + this.prepareTargetElement(); + this.prepareDelay("nextStringDelay"); + this.prepareDelay("loopDelay"); - this.options.strings = toArray(this.options.strings); - this.options.strings = removeComments(this.options.strings); - - //-- We don't have anything. Get out of here. - if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + if (!this.prepareStrings()) { return; } - this.element.innerHTML = "\n \n "; - - this.element.setAttribute("data-typeitid", this.id); - this.elementContainer = this.element.querySelector("span"); + this.prepareDOM(); if (this.options.startDelete) { this.insert(this.stringsToDelete); @@ -170,11 +164,41 @@ var Instance = function () { } /** - * Reset the instance to new status. + * Prepares strings for processing. */ createClass(Instance, [{ + key: "prepareStrings", + value: function prepareStrings() { + this.options.strings = toArray(this.options.strings); + this.options.strings = removeComments(this.options.strings); + + //-- We don't have anything. Get out of here. + if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + return false; + } + + return true; + } + + /** + * Performs DOM-related work to prepare for typing. + */ + + }, { + key: "prepareDOM", + value: function prepareDOM() { + this.element.innerHTML = "\n \n "; + this.element.setAttribute("data-typeitid", this.id); + this.elementContainer = this.element.querySelector("span"); + } + + /** + * Reset the instance to new status. + */ + + }, { key: "reset", value: function reset() { return new Instance(this.element, this.id, this.options, this.typeit); @@ -196,33 +220,24 @@ var Instance = function () { return this.options.html ? this.elementContainer.innerHTML : this.elementContainer.innerText; } - //-- Reset the contents of the element. - if (this.options.html) { - this.elementContainer.innerHTML = content; - } else { - this.elementContainer.innerText = content; - } + this.elementContainer[this.options.html ? "innerHTML" : "innerText"] = content; return content; } - - /** - * Based on options, set the before and after values for the delay that is inserted when typing new strings. - */ - }, { - key: "setNextStringDelay", - value: function setNextStringDelay() { - var isArray = Array.isArray(this.options.nextStringDelay); + key: "prepareDelay", + value: function prepareDelay(delayType) { + var delay = this.options[delayType]; + + if (!delay) return; - var halfDelay = !isArray ? this.options.nextStringDelay / 2 : null; + var isArray = Array.isArray(delay); + var halfDelay = !isArray ? delay / 2 : null; - this.options.nextStringDelay = { - before: isArray ? this.options.nextStringDelay[0] : halfDelay, - after: isArray ? this.options.nextStringDelay[1] : halfDelay, - total: isArray ? this.options.nextStringDelay.reduce(function (accumulator, currentValue) { - return accumulator + currentValue; - }) : this.options.nextStringDelay + this.options[delayType] = { + before: isArray ? delay[0] : halfDelay, + after: isArray ? delay[1] : halfDelay, + total: isArray ? delay[0] + delay[1] : delay }; } }, { @@ -291,9 +306,10 @@ var Instance = function () { string = string[0]; } + //-- @todo Improve this check by using regex. //-- If an opening HTML tag is found and we're not already printing inside a tag if (this.options.html && startsWith(string[0], "<") && !startsWith(string[0], "/); var _doc = document.implementation.createHTMLDocument(""); _doc.body.innerHTML = "<" + matches[1] + ">"; @@ -415,8 +431,8 @@ var Instance = function () { */ }, { - key: "checkElement", - value: function checkElement() { + key: "prepareTargetElement", + value: function prepareTargetElement() { var _this2 = this; //-- If any of the existing children nodes have .ti-container, clear it out because this is a remnant of a previous instance. @@ -676,12 +692,13 @@ var Instance = function () { } if (this.options.loop) { + var delay = this.options.loopDelay ? this.options.loopDelay : this.options.nextStringDelay; this.queueDeletions(this.contents()); - this.generateQueue([this.pause, this.options.loopDelay / 2]); + this.generateQueue([this.pause, delay.before]); setTimeout(function () { _this7.next(); - }, this.options.loopDelay / 2); + }, delay.after); return; } diff --git a/dist/typeit.js b/dist/typeit.js index 67c85311..0469d141 100644 --- a/dist/typeit.js +++ b/dist/typeit.js @@ -2,7 +2,7 @@ * * typeit - The most versatile animated typing utility on the planet. * Author: Alex MacArthur (https://macarthur.me) - * Version: v5.9.0 + * Version: v5.10.0 * URL: https://typeitjs.com * License: GPL-2.0 * @@ -87,7 +87,7 @@ startDelete: false, nextStringDelay: 750, loop: false, - loopDelay: 750, + loopDelay: false, html: true, autoStart: true, callback: false, @@ -146,21 +146,15 @@ this.style = "display:inline;position:relative;font:inherit;color:inherit;"; this.element = element; this.setOptions(options, window.TypeItDefaults, false); - this.checkElement(); - this.setNextStringDelay(); + this.prepareTargetElement(); + this.prepareDelay("nextStringDelay"); + this.prepareDelay("loopDelay"); - this.options.strings = toArray(this.options.strings); - this.options.strings = removeComments(this.options.strings); - - //-- We don't have anything. Get out of here. - if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + if (!this.prepareStrings()) { return; } - this.element.innerHTML = "\n \n "; - - this.element.setAttribute("data-typeitid", this.id); - this.elementContainer = this.element.querySelector("span"); + this.prepareDOM(); if (this.options.startDelete) { this.insert(this.stringsToDelete); @@ -176,11 +170,41 @@ } /** - * Reset the instance to new status. + * Prepares strings for processing. */ createClass(Instance, [{ + key: "prepareStrings", + value: function prepareStrings() { + this.options.strings = toArray(this.options.strings); + this.options.strings = removeComments(this.options.strings); + + //-- We don't have anything. Get out of here. + if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + return false; + } + + return true; + } + + /** + * Performs DOM-related work to prepare for typing. + */ + + }, { + key: "prepareDOM", + value: function prepareDOM() { + this.element.innerHTML = "\n \n "; + this.element.setAttribute("data-typeitid", this.id); + this.elementContainer = this.element.querySelector("span"); + } + + /** + * Reset the instance to new status. + */ + + }, { key: "reset", value: function reset() { return new Instance(this.element, this.id, this.options, this.typeit); @@ -202,33 +226,24 @@ return this.options.html ? this.elementContainer.innerHTML : this.elementContainer.innerText; } - //-- Reset the contents of the element. - if (this.options.html) { - this.elementContainer.innerHTML = content; - } else { - this.elementContainer.innerText = content; - } + this.elementContainer[this.options.html ? "innerHTML" : "innerText"] = content; return content; } - - /** - * Based on options, set the before and after values for the delay that is inserted when typing new strings. - */ - }, { - key: "setNextStringDelay", - value: function setNextStringDelay() { - var isArray = Array.isArray(this.options.nextStringDelay); + key: "prepareDelay", + value: function prepareDelay(delayType) { + var delay = this.options[delayType]; + + if (!delay) return; - var halfDelay = !isArray ? this.options.nextStringDelay / 2 : null; + var isArray = Array.isArray(delay); + var halfDelay = !isArray ? delay / 2 : null; - this.options.nextStringDelay = { - before: isArray ? this.options.nextStringDelay[0] : halfDelay, - after: isArray ? this.options.nextStringDelay[1] : halfDelay, - total: isArray ? this.options.nextStringDelay.reduce(function (accumulator, currentValue) { - return accumulator + currentValue; - }) : this.options.nextStringDelay + this.options[delayType] = { + before: isArray ? delay[0] : halfDelay, + after: isArray ? delay[1] : halfDelay, + total: isArray ? delay[0] + delay[1] : delay }; } }, { @@ -297,9 +312,10 @@ string = string[0]; } + //-- @todo Improve this check by using regex. //-- If an opening HTML tag is found and we're not already printing inside a tag if (this.options.html && startsWith(string[0], "<") && !startsWith(string[0], "/); var _doc = document.implementation.createHTMLDocument(""); _doc.body.innerHTML = "<" + matches[1] + ">"; @@ -421,8 +437,8 @@ */ }, { - key: "checkElement", - value: function checkElement() { + key: "prepareTargetElement", + value: function prepareTargetElement() { var _this2 = this; //-- If any of the existing children nodes have .ti-container, clear it out because this is a remnant of a previous instance. @@ -682,12 +698,13 @@ } if (this.options.loop) { + var delay = this.options.loopDelay ? this.options.loopDelay : this.options.nextStringDelay; this.queueDeletions(this.contents()); - this.generateQueue([this.pause, this.options.loopDelay / 2]); + this.generateQueue([this.pause, delay.before]); setTimeout(function () { _this7.next(); - }, this.options.loopDelay / 2); + }, delay.after); return; } diff --git a/dist/typeit.min.js b/dist/typeit.min.js index a510c176..fb7522f8 100644 --- a/dist/typeit.min.js +++ b/dist/typeit.min.js @@ -2,9 +2,9 @@ * * typeit - The most versatile animated typing utility on the planet. * Author: Alex MacArthur (https://macarthur.me) - * Version: v5.9.0 + * Version: v5.10.0 * URL: https://typeitjs.com * License: GPL-2.0 * */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.TypeIt=e()}(this,function(){"use strict";function t(t){var e=t.getBoundingClientRect();return!(e.right>window.innerWidth||e.bottom>window.innerHeight)&&!(e.top<0||e.left<0)}function e(t,e){return Math.abs(Math.random()*(t+e-(t-e))+(t-e))}function i(t,e){return 0===t.indexOf(e)}function n(t){return Array.isArray(t)?t.slice(0):t.split("
")}window.TypeItDefaults={strings:[],speed:100,deleteSpeed:null,lifeLike:!0,cursor:!0,cursorChar:"|",cursorSpeed:1e3,breakLines:!0,startDelay:250,startDelete:!1,nextStringDelay:750,loop:!1,loopDelay:750,html:!0,autoStart:!0,callback:!1,beforeString:!1,afterString:!1,beforeStep:!1,afterStep:!1,afterComplete:!1};var s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},r=function(){function t(t,e){for(var i=0;i/g,"")}),this.options.strings.length>=1&&""===this.options.strings[0]||(this.element.innerHTML='\n \n ',this.element.setAttribute("data-typeitid",this.id),this.elementContainer=this.element.querySelector("span"),this.options.startDelete&&(this.insert(this.stringsToDelete),this.queue.push([this.delete]),this.insertSplitPause(1)),this.generateQueue(),this.autoInit&&this.init())}return r(s,[{key:"reset",value:function(){return new s(this.element,this.id,this.options,this.typeit)}},{key:"contents",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.options.html?this.elementContainer.innerHTML:this.elementContainer.innerText:(this.options.html?this.elementContainer.innerHTML=t:this.elementContainer.innerText=t,t)}},{key:"setNextStringDelay",value:function(){var t=Array.isArray(this.options.nextStringDelay),e=t?null:this.options.nextStringDelay/2;this.options.nextStringDelay={before:t?this.options.nextStringDelay[0]:e,after:t?this.options.nextStringDelay[1]:e,total:t?this.options.nextStringDelay.reduce(function(t,e){return t+e}):this.options.nextStringDelay}}},{key:"generateQueue",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;e=null===e?[this.pause,this.options.startDelay]:e,this.queue.push(e),this.options.strings.forEach(function(e,i){if(t.queueString(e),i+1!==t.options.strings.length){if(t.options.breakLines)return t.queue.push([t.break]),void t.insertSplitPause(t.queue.length);t.queueDeletions(e),t.insertSplitPause(t.queue.length,e.length)}})}},{key:"queueDeletions",value:function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e="string"==typeof t?t.length:t,i=0;i1&&void 0!==arguments[1])||arguments[1];if(t){if(t=n(t),document.implementation.createHTMLDocument("").body.innerHTML=t,e&&(t=(t=this.rake(t))[0]),this.options.html&&i(t[0],"<")&&!i(t[0],"/),o=document.implementation.createHTMLDocument("");o.body.innerHTML="<"+s[1]+">",this.queue.push([this.type,o.body.children[0]])}else this.queue.push([this.type,t[0]]);t.splice(0,1),e&&this.queue[this.queue.length-1].push("first-of-string"),t.length?this.queueString(t,!1):this.queue[this.queue.length-1].push("last-of-string")}}},{key:"insertSplitPause",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;this.queue.splice(t,0,[this.pause,this.options.nextStringDelay.before]),this.queue.splice(t-e,0,[this.pause,this.options.nextStringDelay.after])}},{key:"init",value:function(){if(!this.hasStarted){if(this.cursor(),this.options.autoStart)return this.hasStarted=!0,void this.next();if(t(this.element))return this.hasStarted=!0,void this.next();var e=this;window.addEventListener("scroll",function i(n){t(e.element)&&!e.hasStarted&&(e.hasStarted=!0,e.next(),n.currentTarget.removeEventListener(n.type,i))})}}},{key:"cursor",value:function(){var t="visibility: hidden;";if(this.options.cursor){var e=document.createElement("style");e.id=this.id;var i="\n @keyframes blink-"+this.id+" {\n 0% {opacity: 0}\n 49% {opacity: 0}\n 50% {opacity: 1}\n }\n\n [data-typeitid='"+this.id+"'] .ti-cursor {\n animation: blink-"+this.id+" "+this.options.cursorSpeed/1e3+"s infinite;\n }\n ";e.appendChild(document.createTextNode(i)),document.head.appendChild(e),t=""}this.element.insertAdjacentHTML("beforeend",''+this.options.cursorChar+"")}},{key:"insert",value:function(t){arguments.length>1&&void 0!==arguments[1]&&arguments[1]?this.elementContainer.lastChild.insertAdjacentHTML("beforeend",t):this.elementContainer.insertAdjacentHTML("beforeend",t),this.contents(this.contents().split("").join(""))}},{key:"checkElement",value:function(){var t=this;[].slice.call(this.element.childNodes).forEach(function(e){void 0!==e.classList&&e.classList.contains("ti-container")&&(t.element.innerHTML="")}),!this.options.startDelete&&this.element.innerHTML.length>0?this.options.strings=this.element.innerHTML.trim():this.stringsToDelete=this.element.innerHTML}},{key:"break",value:function(){this.insert("
"),this.next()}},{key:"pause",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];setTimeout(function(){t.next()},e||this.options.nextStringDelay.total)}},{key:"rake",value:function(t){var e=this;return t.map(function(t){return t=t.split(""),e.options.html?function(t){for(var e=[],i=void 0,n=!1,s=0;s"===t[s]||";"===t[s]&&n)&&(e[1]=s,s=0,i=t.slice(e[0],e[1]+1).join(""),t.splice(e[0],e[1]-e[0]+1,i),n=!1);return t}(t):t})}},{key:"type",value:function(t){var e=this;this.setPace(),this.timeouts[0]=setTimeout(function(){return"string"!=typeof t?(t.innerHTML="",e.elementContainer.appendChild(t),e.isInTag=!0,void e.next()):i(t,"1&&void 0!==arguments[1]?arguments[1]:null,i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],n={};for(var s in null===e&&(e=this.options),e)n[s]=e[s];for(var o in t)n[o]=t[o];this.options=n,i&&this.next()}},{key:"setPace",value:function(){var t=this.options.speed,i=null!==this.options.deleteSpeed?this.options.deleteSpeed:this.options.speed/3,n=t/2,s=i/2;this.typePace=this.options.lifeLike?e(t,n):t,this.deletePace=this.options.lifeLike?e(i,s):i}},{key:"delete",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.timeouts[1]=setTimeout(function(){t.setPace();for(var i=t.contents().split(""),n=i.length-1;n>-1;n--){if(">"!==i[n]&&";"!==i[n]||!t.options.html){i.pop();break}for(var s=n;s>-1;s--){if("
"===i.slice(s-3,s+1).join("")){i.splice(s-3,4);break}if("&"===i[s]){i.splice(s,n-s+1);break}if("<"===i[s]&&">"!==i[s-1]){if(";"===i[s-1])for(var o=s-1;o>-1;o--)if("&"===i[o]){i.splice(o,s-o);break}i.splice(s-1,1);break}}break}if(t.options.html&&t.contents().indexOf(">-1)for(var r=t.contents().indexOf(">=0;r--)if("<"===i[r]){i.splice(r,i.length-r);break}t.contents(i.join("").replace(/<[^\/>][^>]*><\/[^>]+>/,"")),null===e&&t.queue.unshift([t.delete,i.length]),e>1&&t.queue.unshift([t.delete,e-1]),t.next()},this.deletePace)}},{key:"empty",value:function(){this.contents(""),this.next()}},{key:"next",value:function(){var t=this;if(!this.isFrozen){if(this.queue.length>0)return this.step=this.queue.shift(),"first-of-string"===this.step[2]&&this.options.beforeString&&this.options.beforeString(this.step,this.queue,this.typeit),this.options.beforeStep&&this.options.beforeStep(this.step,this.queue,this.typeit),this.step[0].call(this,this.step[1],this.step[2]),"last-of-string"===this.step[2]&&this.options.afterString&&this.options.afterString(this.step,this.queue,this.typeit),void(this.options.afterStep&&this.options.afterStep(this.step,this.queue,this.typeit));if(this.options.callback&&this.options.callback(),this.options.afterComplete&&this.options.afterComplete(this.typeit),this.options.loop)return this.queueDeletions(this.contents()),this.generateQueue([this.pause,this.options.loopDelay/2]),void setTimeout(function(){t.next()},this.options.loopDelay/2);this.isComplete=!0}}}]),s}();return function(){function t(e,i){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];o(this,t),this.id=Math.random().toString(36).substring(2,15),this.instances=[],this.elements=[],this.args=i,this.autoInit=n,"object"===(void 0===e?"undefined":s(e))&&(void 0===e.length?this.elements.push(e):this.elements=e),"string"==typeof e&&(this.elements=document.querySelectorAll(e)),this.generateInstances()}return r(t,[{key:"generateInstances",value:function(){var t=this;[].slice.call(this.elements).forEach(function(e){t.instances.push(new h(e,t.id,t.args,t.autoInit,t))})}},{key:"queueUp",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.instances.forEach(function(i){i.queue.push([i[t],e]),!0===i.isComplete&&i.next()})}},{key:"type",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return this.instances.forEach(function(e){e.queueString(t),!0===e.isComplete&&e.next()}),this}},{key:"delete",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.queueUp("delete",t),this}},{key:"pause",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.queueUp("pause",t),this}},{key:"empty",value:function(){return this.queueUp("empty"),this}},{key:"break",value:function(){return this.queueUp("break"),this}},{key:"options",value:function(t){return this.queueUp("setOptions",t),this}},{key:"freeze",value:function(){this.instances.forEach(function(t){t.isFrozen=!0})}},{key:"unfreeze",value:function(){this.instances.forEach(function(t){t.isFrozen&&(t.isFrozen=!1,t.next())})}},{key:"destroy",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.instances.forEach(function(e){e.timeouts.forEach(function(t){clearTimeout(t)}),e.timeouts=[],t&&e.options.cursor&&e.element.removeChild(e.element.querySelector(".ti-cursor")),e.hasBeenDestroyed=!0})}},{key:"reset",value:function(){this.instances=this.instances.map(function(t){return t.reset()})}},{key:"init",value:function(){this.instances.forEach(function(t){t.init()})}},{key:"isComplete",get:function(){return!!this.instances.length&&this.instances[0].isComplete}},{key:"hasBeenDestroyed",get:function(){return!!this.instances.length&&this.instances[0].hasBeenDestroyed}},{key:"hasStarted",get:function(){return!!this.instances.length&&this.instances[0].hasStarted}},{key:"isFrozen",get:function(){return!!this.instances.length&&this.instances[0].isFrozen}}]),t}()}); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.TypeIt=e()}(this,function(){"use strict";function t(t){var e=t.getBoundingClientRect();return!(e.right>window.innerWidth||e.bottom>window.innerHeight)&&!(e.top<0||e.left<0)}function e(t,e){return Math.abs(Math.random()*(t+e-(t-e))+(t-e))}function i(t,e){return 0===t.indexOf(e)}function n(t){return Array.isArray(t)?t.slice(0):t.split("
")}window.TypeItDefaults={strings:[],speed:100,deleteSpeed:null,lifeLike:!0,cursor:!0,cursorChar:"|",cursorSpeed:1e3,breakLines:!0,startDelay:250,startDelete:!1,nextStringDelay:750,loop:!1,loopDelay:!1,html:!0,autoStart:!0,callback:!1,beforeString:!1,afterString:!1,beforeStep:!1,afterStep:!1,afterComplete:!1};var s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},r=function(){function t(t,e){for(var i=0;i/g,"")}),!(this.options.strings.length>=1&&""===this.options.strings[0])}},{key:"prepareDOM",value:function(){this.element.innerHTML='\n \n ',this.element.setAttribute("data-typeitid",this.id),this.elementContainer=this.element.querySelector("span")}},{key:"reset",value:function(){return new s(this.element,this.id,this.options,this.typeit)}},{key:"contents",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return null===t?this.options.html?this.elementContainer.innerHTML:this.elementContainer.innerText:(this.elementContainer[this.options.html?"innerHTML":"innerText"]=t,t)}},{key:"prepareDelay",value:function(t){var e=this.options[t];if(e){var i=Array.isArray(e),n=i?null:e/2;this.options[t]={before:i?e[0]:n,after:i?e[1]:n,total:i?e[0]+e[1]:e}}}},{key:"generateQueue",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;e=null===e?[this.pause,this.options.startDelay]:e,this.queue.push(e),this.options.strings.forEach(function(e,i){if(t.queueString(e),i+1!==t.options.strings.length){if(t.options.breakLines)return t.queue.push([t.break]),void t.insertSplitPause(t.queue.length);t.queueDeletions(e),t.insertSplitPause(t.queue.length,e.length)}})}},{key:"queueDeletions",value:function(){for(var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e="string"==typeof t?t.length:t,i=0;i1&&void 0!==arguments[1])||arguments[1];if(t){if(t=n(t),document.implementation.createHTMLDocument("").body.innerHTML=t,e&&(t=(t=this.rake(t))[0]),this.options.html&&i(t[0],"<")&&!i(t[0],"/),o=document.implementation.createHTMLDocument("");o.body.innerHTML="<"+s[1]+">",this.queue.push([this.type,o.body.children[0]])}else this.queue.push([this.type,t[0]]);t.splice(0,1),e&&this.queue[this.queue.length-1].push("first-of-string"),t.length?this.queueString(t,!1):this.queue[this.queue.length-1].push("last-of-string")}}},{key:"insertSplitPause",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1;this.queue.splice(t,0,[this.pause,this.options.nextStringDelay.before]),this.queue.splice(t-e,0,[this.pause,this.options.nextStringDelay.after])}},{key:"init",value:function(){if(!this.hasStarted){if(this.cursor(),this.options.autoStart)return this.hasStarted=!0,void this.next();if(t(this.element))return this.hasStarted=!0,void this.next();var e=this;window.addEventListener("scroll",function i(n){t(e.element)&&!e.hasStarted&&(e.hasStarted=!0,e.next(),n.currentTarget.removeEventListener(n.type,i))})}}},{key:"cursor",value:function(){var t="visibility: hidden;";if(this.options.cursor){var e=document.createElement("style");e.id=this.id;var i="\n @keyframes blink-"+this.id+" {\n 0% {opacity: 0}\n 49% {opacity: 0}\n 50% {opacity: 1}\n }\n\n [data-typeitid='"+this.id+"'] .ti-cursor {\n animation: blink-"+this.id+" "+this.options.cursorSpeed/1e3+"s infinite;\n }\n ";e.appendChild(document.createTextNode(i)),document.head.appendChild(e),t=""}this.element.insertAdjacentHTML("beforeend",''+this.options.cursorChar+"")}},{key:"insert",value:function(t){arguments.length>1&&void 0!==arguments[1]&&arguments[1]?this.elementContainer.lastChild.insertAdjacentHTML("beforeend",t):this.elementContainer.insertAdjacentHTML("beforeend",t),this.contents(this.contents().split("").join(""))}},{key:"prepareTargetElement",value:function(){var t=this;[].slice.call(this.element.childNodes).forEach(function(e){void 0!==e.classList&&e.classList.contains("ti-container")&&(t.element.innerHTML="")}),!this.options.startDelete&&this.element.innerHTML.length>0?this.options.strings=this.element.innerHTML.trim():this.stringsToDelete=this.element.innerHTML}},{key:"break",value:function(){this.insert("
"),this.next()}},{key:"pause",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];setTimeout(function(){t.next()},e||this.options.nextStringDelay.total)}},{key:"rake",value:function(t){var e=this;return t.map(function(t){return t=t.split(""),e.options.html?function(t){for(var e=[],i=void 0,n=!1,s=0;s"===t[s]||";"===t[s]&&n)&&(e[1]=s,s=0,i=t.slice(e[0],e[1]+1).join(""),t.splice(e[0],e[1]-e[0]+1,i),n=!1);return t}(t):t})}},{key:"type",value:function(t){var e=this;this.setPace(),this.timeouts[0]=setTimeout(function(){return"string"!=typeof t?(t.innerHTML="",e.elementContainer.appendChild(t),e.isInTag=!0,void e.next()):i(t,"1&&void 0!==arguments[1]?arguments[1]:null,i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],n={};for(var s in null===e&&(e=this.options),e)n[s]=e[s];for(var o in t)n[o]=t[o];this.options=n,i&&this.next()}},{key:"setPace",value:function(){var t=this.options.speed,i=null!==this.options.deleteSpeed?this.options.deleteSpeed:this.options.speed/3,n=t/2,s=i/2;this.typePace=this.options.lifeLike?e(t,n):t,this.deletePace=this.options.lifeLike?e(i,s):i}},{key:"delete",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.timeouts[1]=setTimeout(function(){t.setPace();for(var i=t.contents().split(""),n=i.length-1;n>-1;n--){if(">"!==i[n]&&";"!==i[n]||!t.options.html){i.pop();break}for(var s=n;s>-1;s--){if("
"===i.slice(s-3,s+1).join("")){i.splice(s-3,4);break}if("&"===i[s]){i.splice(s,n-s+1);break}if("<"===i[s]&&">"!==i[s-1]){if(";"===i[s-1])for(var o=s-1;o>-1;o--)if("&"===i[o]){i.splice(o,s-o);break}i.splice(s-1,1);break}}break}if(t.options.html&&t.contents().indexOf(">-1)for(var r=t.contents().indexOf(">=0;r--)if("<"===i[r]){i.splice(r,i.length-r);break}t.contents(i.join("").replace(/<[^\/>][^>]*><\/[^>]+>/,"")),null===e&&t.queue.unshift([t.delete,i.length]),e>1&&t.queue.unshift([t.delete,e-1]),t.next()},this.deletePace)}},{key:"empty",value:function(){this.contents(""),this.next()}},{key:"next",value:function(){var t=this;if(!this.isFrozen){if(this.queue.length>0)return this.step=this.queue.shift(),"first-of-string"===this.step[2]&&this.options.beforeString&&this.options.beforeString(this.step,this.queue,this.typeit),this.options.beforeStep&&this.options.beforeStep(this.step,this.queue,this.typeit),this.step[0].call(this,this.step[1],this.step[2]),"last-of-string"===this.step[2]&&this.options.afterString&&this.options.afterString(this.step,this.queue,this.typeit),void(this.options.afterStep&&this.options.afterStep(this.step,this.queue,this.typeit));if(this.options.callback&&this.options.callback(),this.options.afterComplete&&this.options.afterComplete(this.typeit),this.options.loop){var e=this.options.loopDelay?this.options.loopDelay:this.options.nextStringDelay;return this.queueDeletions(this.contents()),this.generateQueue([this.pause,e.before]),void setTimeout(function(){t.next()},e.after)}this.isComplete=!0}}}]),s}();return function(){function t(e,i){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];o(this,t),this.id=Math.random().toString(36).substring(2,15),this.instances=[],this.elements=[],this.args=i,this.autoInit=n,"object"===(void 0===e?"undefined":s(e))&&(void 0===e.length?this.elements.push(e):this.elements=e),"string"==typeof e&&(this.elements=document.querySelectorAll(e)),this.generateInstances()}return r(t,[{key:"generateInstances",value:function(){var t=this;[].slice.call(this.elements).forEach(function(e){t.instances.push(new u(e,t.id,t.args,t.autoInit,t))})}},{key:"queueUp",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.instances.forEach(function(i){i.queue.push([i[t],e]),!0===i.isComplete&&i.next()})}},{key:"type",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return this.instances.forEach(function(e){e.queueString(t),!0===e.isComplete&&e.next()}),this}},{key:"delete",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.queueUp("delete",t),this}},{key:"pause",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return this.queueUp("pause",t),this}},{key:"empty",value:function(){return this.queueUp("empty"),this}},{key:"break",value:function(){return this.queueUp("break"),this}},{key:"options",value:function(t){return this.queueUp("setOptions",t),this}},{key:"freeze",value:function(){this.instances.forEach(function(t){t.isFrozen=!0})}},{key:"unfreeze",value:function(){this.instances.forEach(function(t){t.isFrozen&&(t.isFrozen=!1,t.next())})}},{key:"destroy",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.instances.forEach(function(e){e.timeouts.forEach(function(t){clearTimeout(t)}),e.timeouts=[],t&&e.options.cursor&&e.element.removeChild(e.element.querySelector(".ti-cursor")),e.hasBeenDestroyed=!0})}},{key:"reset",value:function(){this.instances=this.instances.map(function(t){return t.reset()})}},{key:"init",value:function(){this.instances.forEach(function(t){t.init()})}},{key:"isComplete",get:function(){return!!this.instances.length&&this.instances[0].isComplete}},{key:"hasBeenDestroyed",get:function(){return!!this.instances.length&&this.instances[0].hasBeenDestroyed}},{key:"hasStarted",get:function(){return!!this.instances.length&&this.instances[0].hasStarted}},{key:"isFrozen",get:function(){return!!this.instances.length&&this.instances[0].isFrozen}}]),t}()}); diff --git a/package.json b/package.json index 9383a14b..519df689 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typeit", - "version": "5.9.0", + "version": "5.10.0", "description": "The most versatile animated typing utility on the planet.", "author": "Alex MacArthur (https://macarthur.me)", "license": "GPL-2.0", diff --git a/src/defaults.js b/src/defaults.js index 232b3211..a9377bca 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -11,7 +11,7 @@ window.TypeItDefaults = { startDelete: false, nextStringDelay: 750, loop: false, - loopDelay: 750, + loopDelay: false, html: true, autoStart: true, callback: false, diff --git a/src/instance.js b/src/instance.js index e73abe9f..1c55eafd 100644 --- a/src/instance.js +++ b/src/instance.js @@ -24,23 +24,15 @@ export default class Instance { this.style = "display:inline;position:relative;font:inherit;color:inherit;"; this.element = element; this.setOptions(options, window.TypeItDefaults, false); - this.checkElement(); - this.setNextStringDelay(); + this.prepareTargetElement(); + this.prepareDelay("nextStringDelay"); + this.prepareDelay("loopDelay"); - this.options.strings = toArray(this.options.strings); - this.options.strings = removeComments(this.options.strings); - - //-- We don't have anything. Get out of here. - if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + if (!this.prepareStrings()) { return; } - this.element.innerHTML = ` - - `; - - this.element.setAttribute("data-typeitid", this.id); - this.elementContainer = this.element.querySelector("span"); + this.prepareDOM(); if (this.options.startDelete) { this.insert(this.stringsToDelete); @@ -55,6 +47,32 @@ export default class Instance { } } + /** + * Prepares strings for processing. + */ + prepareStrings() { + this.options.strings = toArray(this.options.strings); + this.options.strings = removeComments(this.options.strings); + + //-- We don't have anything. Get out of here. + if (this.options.strings.length >= 1 && this.options.strings[0] === "") { + return false; + } + + return true; + } + + /** + * Performs DOM-related work to prepare for typing. + */ + prepareDOM() { + this.element.innerHTML = ` + + `; + this.element.setAttribute("data-typeitid", this.id); + this.elementContainer = this.element.querySelector("span"); + } + /** * Reset the instance to new status. */ @@ -75,32 +93,25 @@ export default class Instance { : this.elementContainer.innerText; } - //-- Reset the contents of the element. - if (this.options.html) { - this.elementContainer.innerHTML = content; - } else { - this.elementContainer.innerText = content; - } + this.elementContainer[ + this.options.html ? "innerHTML" : "innerText" + ] = content; return content; } - /** - * Based on options, set the before and after values for the delay that is inserted when typing new strings. - */ - setNextStringDelay() { - let isArray = Array.isArray(this.options.nextStringDelay); - - let halfDelay = !isArray ? this.options.nextStringDelay / 2 : null; - - this.options.nextStringDelay = { - before: isArray ? this.options.nextStringDelay[0] : halfDelay, - after: isArray ? this.options.nextStringDelay[1] : halfDelay, - total: isArray - ? this.options.nextStringDelay.reduce((accumulator, currentValue) => { - return accumulator + currentValue; - }) - : this.options.nextStringDelay + prepareDelay(delayType) { + let delay = this.options[delayType]; + + if (!delay) return; + + let isArray = Array.isArray(delay); + let halfDelay = !isArray ? delay / 2 : null; + + this.options[delayType] = { + before: isArray ? delay[0] : halfDelay, + after: isArray ? delay[1] : halfDelay, + total: isArray ? delay[0] + delay[1] : delay }; } @@ -160,12 +171,13 @@ export default class Instance { string = string[0]; } + //-- @todo Improve this check by using regex. //-- If an opening HTML tag is found and we're not already printing inside a tag if ( this.options.html && (startsWith(string[0], "<") && !startsWith(string[0], "/); let doc = document.implementation.createHTMLDocument(""); doc.body.innerHTML = "<" + matches[1] + ">"; @@ -299,7 +311,7 @@ export default class Instance { * Depending on if we're starting by deleting an existing string or typing * from nothing, set a specific variable to what's in the HTML. */ - checkElement() { + prepareTargetElement() { //-- If any of the existing children nodes have .ti-container, clear it out because this is a remnant of a previous instance. [].slice.call(this.element.childNodes).forEach(node => { if (node.classList === undefined) return; @@ -537,12 +549,15 @@ export default class Instance { } if (this.options.loop) { + let delay = this.options.loopDelay + ? this.options.loopDelay + : this.options.nextStringDelay; this.queueDeletions(this.contents()); - this.generateQueue([this.pause, this.options.loopDelay / 2]); + this.generateQueue([this.pause, delay.before]); setTimeout(() => { this.next(); - }, this.options.loopDelay / 2); + }, delay.after); return; } diff --git a/tests/typing.test.js b/tests/typing.test.js index 266322d1..63935250 100644 --- a/tests/typing.test.js +++ b/tests/typing.test.js @@ -109,7 +109,7 @@ test("Can type new string after completion.", () => { expect(typedString).toEqual("Ham over turkey. Obviously."); }); -test("Generates correct pauses.", () => { +test("Generates correct `nextStringDelay`.", () => { document.body.innerHTML = `
'
`; @@ -138,6 +138,34 @@ test("Generates correct pauses.", () => { expect(nextStringDelay.total).toBe(550); }); +test("Generates correct `loopDelay`.", () => { + document.body.innerHTML = `
' + +
`; + + const instance1 = new TypeIt("#element", { + nextStringDelay: 500, + strings: ["Free markets...", "win."] + }); + + let nextStringDelay = instance1.instances[0].options.nextStringDelay; + let loopDelay = instance1.instances[0].options.loopDelay; + + expect(loopDelay).toBe(false); + + const instance2 = new TypeIt("#element", { + loopDelay: [3000, 5000], + strings: ["Free markets...", "win."] + }); + + loopDelay = instance2.instances[0].options.loopDelay; + + expect(typeof loopDelay).toBe("object"); + expect(loopDelay.before).toBe(3000); + expect(loopDelay.after).toBe(5000); + expect(loopDelay.total).toBe(8000); +}); + test("Wraps pauses correctly when replacing lines.", () => { document.body.innerHTML = `
'