/**
* jQuery TypeIt
* @author Alex MacArthur (http://macarthur.me)
* @version 1.2.0
* @copyright 2015 Alex MacArthur
* @description Types out a given string or strings.
*/
(function($){
// the actual jQuery function
$.fn.typeIt = function(options, callback){
// now call a callback function
return this.each(function(){
$(this).data("typeit", new $.fn.typeIt.typeItClass($(this), options, callback));
});
};
// create the class
$.fn.typeIt.typeItClass = function(theElement, options, callback){
// plugin default settings
this.defaults = {
whatToType:'This is the default string. Please replace this string with your own.',
typeSpeed: 200,
lifeLike: false,
showCursor: true,
breakLines: true,
breakWait: 500,
delayStart: 250
};
this.dataDefaults = {
whatToType : theElement.data('typeitWhattotype'),
typeSpeed: theElement.data('typeitSpeed'),
lifeLike: theElement.data('typeitLifelike'),
showCursor: theElement.data('typeitShowcursor'),
breakLines: theElement.data('typeitBreaklines'),
breakWait: theElement.data('typeitBreakWait'),
delayStart : theElement.data('typeitDelayStart')
};
// the element that holds the text
this.theElement = theElement;
// callback function that executes after strings have been printed
this.callback = callback;
// the settings for the plugin instance
this.settings = $.extend({}, this.defaults, options, this.dataDefaults);
// the number of types a character has been typed for each pass over a string
this.typeCount = 0;
// the character number of a string that's currently being deleted
this.deleteCount = 0;
// the string number that's currently being typed or deleted
this.stringCount = 0;
this.stringPlaceCount = 0;
// the length of the current string being handled
this.phraseLength = 0;
this.cursor = '';
this.deleteTimeout = null;
this.typeTimeout = null;
this.shortenedText = null;
if(typeof this.callback != 'function'){
console.log('Your callback is not a valid function. Please format your callback as "function(){...}" when it is defined.');
}
this.init(theElement);
};
// create a new prototype
var _proto = $.fn.typeIt.typeItClass.prototype;
// initialize the plugin
_proto.init = function(theElement){
this.stringArray = this.settings.whatToType;
// check if the value is an array or just a string
if(Object.prototype.toString.call(this.stringArray) !== '[object Array]'){
// since it's not already an array, turn it into one, since later functionality depends on it being one
this.stringArray = '["' + this.stringArray + '"]';
this.stringArray = JSON.parse(this.stringArray);
}
this.mergedStrings = this.stringArray.join('');
this.stringLengths = {};
this.phraseLength = this.stringLengths[this.stringCount];
// get the string lengths and save to array, set up ti-containers for each string
for(j=0; j < this.stringArray.length; j++){
this.stringLengths[j] = this.stringArray[j].length;
// set up the number of ti-containers we'll need to hold the strings
theElement.append('');
}
// add .active-container to the first .ti-text-container so the cursor starts blinking before a string is printed
theElement.find('.ti-container:first-child').find('.ti-text-container').addClass('active-container');
// if breakLines is false, then we for sure only need ONE ti-container even if there multiple strings, so make sure of that
if(this.settings.breakLines === false) {
theElement.find('.ti-container').remove();
theElement.append('');
}
// if showCursor is false, then remove the ti-cursor class
if(this.settings.showCursor === false) {
$(this.theElement).find('.ti-text-container').removeClass('ti-cursor');
}
// start to type the string(s)
setTimeout(function() {
this.typeLoop();
}.bind(this), this.settings.delayStart);
};
_proto.typeLoop = function(){
// set the length of the current phrase being typed
this.phraseLength = this.stringLengths[this.stringCount];
// make it human-like if specified in the settings
if(this.settings.lifeLike === true){
this.delayTime = this.settings.typeSpeed*Math.random();
} else {
this.delayTime = this.settings.typeSpeed;
}
this.typeTimeout = setTimeout(function () {
// append the string of letters to the respective .ti-text-container
var characterToAppend = this.mergedStrings[this.typeCount+this.stringPlaceCount];
// if breakLines is set to true, add the 'active-container' class to the next .ti-text-container in the list.
if(this.settings.breakLines === true) {
$(this.theElement).find('.ti-text-container:eq('+ this.stringCount +')').addClass('active-container').append(characterToAppend);
} else {
$(this.theElement).find('.ti-text-container').addClass('active-container').append(characterToAppend);
}
this.typeCount++;
// if there are still characters to be typed, call the same function again
if (this.typeCount < this.phraseLength) {
this.typeLoop(this.stringLengths[this.stringCount]);
// if there are no more characters to print and there is more than one string to be typed, delete the string just printed
} else if(this.stringArray.length > 1) {
// update the this.stringPlaceCount so that we're appending starting at the correct spot in the merged string
this.stringPlaceCount = this.stringPlaceCount + this.phraseLength;
// reset this.typeCount in case this function needs to be reused
this.typeCount = 0;
// if the stringCount is the same as the number of strings we started with, we're done, so call the callback function
if(this.stringCount+1 === this.stringArray.length) {
this.callback();
// if we're not on the last string, then move on to to delete, unless the user wants to break lines
} else if((this.stringCount+1 < this.stringArray.length) && this.settings.breakLines === false){
setTimeout(function(){
this.deleteLoop();
}.bind(this), this.settings.breakWait);
// if breakLines is true and we still have strings left to type, break it and continue
} else if (this.stringCount+1 < this.stringArray.length && this.settings.breakLines === true){
this.stringCount++;
setTimeout(function(){
// remove any 'active-container' classes fromt the elements
$(this.theElement).find('.ti-text-container').removeClass('active-container');
// give 'active-container' class to next container, so the cursor can start blinking
$(this.theElement).find('.ti-text-container:eq('+ this.stringCount +')').addClass('active-container');
// after another slight delay, continue typing the next string
setTimeout(function(){
this.typeLoop();
}.bind(this), this.settings.breakWait);
}.bind(this), this.settings.breakWait);
}
// since there are no more strings to be typed, we're done and can call the callback function
} else {
this.callback();
}
}.bind(this), this.delayTime);
};
_proto.deleteLoop = function() {
this.deleteTimeout = setTimeout(function () {
// get the string from the element and cut it by one character at the end
shortenedText = $(this.theElement).find('.ti-text-container').text().substring(0, $(this.theElement).find('.ti-text-container').text().length - 1);
// then, put that shortened text into the element so it looks like it's being deleted
$(this.theElement).find('.ti-text-container').text(shortenedText);
this.deleteCount++;
// if there are still characters in the string, run the function again
if (this.deleteCount < this.phraseLength) {
this.deleteLoop();
// if there are still strings in the array, go back to typing.
} else if(this.stringArray[this.stringCount+1] !== undefined){
this.deleteCount = 0;
this.stringCount++;
this.typeLoop();
}
// make backspacing much quicker by dividing delayTime (arbitrarily) by three
}.bind(this), this.delayTime/3);
};
// stop the plugin from typing or deleting stuff whenever it's called
_proto.stopTyping = function() {
clearTimeout(this.typeTimeout);
clearTimeout(this.deleteTimeout);
};
}(jQuery));