1/** 2 * jQuery TypeIt 3 * @author Alex MacArthur (http://macarthur.me) 4 * @version 4.2.3 5 * @copyright 2016 Alex MacArthur 6 * @description Types out a given string or strings. 7 */ 8 9 /* jslint browser: true */ 10 /* globals jQuery:false */ 11 12 (function($, undefined) { 13 14 'use strict'; 15 16 var $doc = $(document); 17 18 $.fn.typeIt = function(opt) { 19 return this.each(function() { 20 var $t = $(this); 21 $t.data('typeit', new $.typeIt($t, opt)); 22 }); 23 }; 24 25 $.typeIt = function(el, opt) { 26 this.d = { 27 strings: [], 28 speed: 100, 29 deleteSpeed: undefined, 30 lifeLike: true, 31 cursor: true, 32 cursorSpeed: 1000, 33 breakLines: true, 34 breakDelay: 750, 35 startDelay: 250, 36 startDelete: false, 37 loop: false, 38 loopDelay: 750, 39 html: true, 40 autoStart: true, 41 callback: function() {} 42 }; 43 44 this.queue = []; 45 this.queueIndex = 0; 46 this.hasStarted = false; 47 this.inTag = false; 48 this.stringsToDelete = ''; 49 this.style = 'style="display:inline;position:relative;font:inherit;color:inherit;"'; 50 this.s = $.extend({}, this.d, opt); 51 this.el = el; 52 this._init(); 53 }; 54 55 $.typeIt.prototype = { 56 57 _init: function() { 58 this.el.find('.ti-container, .ti-cursor, .ti-placeholder').remove(); 59 this._elCheck(); 60 this.s.strings = this._toArray(this.s.strings); 61 this.el.html('<i class="ti-placeholder" style="display:inline-block;width:0;line-height:0;overflow:hidden;">.</i><span ' + this.style + ' class="ti-container"></span>'); 62 this.tel = this.el.find('span'); 63 64 this.insert = function(c) { 65 this.tel.append(c); 66 }; 67 68 if (this.s.startDelete) { 69 this.tel.html(this.stringsToDelete); 70 this.queue.push([this.delete]); 71 } 72 73 this._generateQueue(); 74 this._kickoff(); 75 }, 76 77 _kickoff: function() { 78 this._cursor(); 79 80 if (this.s.autoStart) { 81 this._startQueue(); 82 } else { 83 if (this._isVisible()) { 84 this.hasStarted = true; 85 this._startQueue(); 86 } else { 87 $doc.on('scroll', function() { 88 if (this._isVisible() && !this.hasStarted) { 89 this.hasStarted = true; 90 this._startQueue(); 91 } 92 }.bind(this)); 93 } 94 } 95 }, 96 97 _generateQueue: function() { 98 for (var i = 0; i < this.s.strings.length; i++) { 99 100 this.queue.push([this.type, this.s.strings[i]]); 101 102 if (i < (this.s.strings.length - 1)) { 103 var curPos = this.queue.length; 104 this.queue.push([this.s.breakLines ? this.break : this.delete]); 105 106 if (this.s.breakLines) { 107 this.queue.splice(curPos, 0, [this.pause, this.s.breakDelay / 2]); 108 this.queue.splice(curPos + 2, 0, [this.pause, this.s.breakDelay / 2]); 109 } 110 } 111 } 112 }, 113 114 _startQueue: function() { 115 this._to(function() { 116 this._executeQueue(); 117 }.bind(this), this.s.startDelay); 118 }, 119 120 /* 121 Pass in a string, and loop over that string until empty. Then return true. 122 */ 123 type: function(string, rake) { 124 125 // set default 'rake' value 126 rake = typeof rake === 'undefined' ? true : rake; 127 128 // convert to array 129 string = this._toArray(string); 130 131 // if it's designated, rake that bad boy for HTML tags and stuff 132 if (rake) { 133 string = this._rake(string); 134 string = string[0]; 135 } 136 137 // do the work that matters 138 this.tTO = setTimeout(function() { 139 140 // randomize the timeout each time, if that's your thing 141 this._setPace(this); 142 143 // "_print" the character 144 // if an opening HTML tag is found and we're not already pringing inside a tag 145 if (this.s.html && (string[0].indexOf('<') !== -1 && str
145ing[0].indexOf('</') === -1) && (!this.inTag)) { 146 147 // loop the string to find where the tag ends 148 for (var i = string.length - 1; i >= 0; i--) { 149 if (string[i].indexOf('</') !== -1) { 150 this.tagCount = 1; 151 this.tagDuration = i; 152 } 153 } 154 155 this._makeNode(string[0]); 156 } else { 157 this._print(string[0]); 158 } 159 160 // shorten it 161 string.splice(0, 1); 162 163 // if there's more to it, run again until fully printed 164 if (string.length) { 165 this.type(string, false); 166 } else { 167 this._executeQueue(); 168 } 169 170 }.bind(this), this.typePace); 171 }, 172 173 pause: function(time) { 174 time = time === undefined ? this.s.breakDelay : time; 175 this._to(function() { 176 this._executeQueue(); 177 }.bind(this), time); 178 }, 179 180 break: function() { 181 this.insert('<br>'); 182 this._executeQueue(); 183 }, 184 185 mergeSet: function(s) { 186 this.s = $.extend({}, this.s, s); 187 this._executeQueue(); 188 }, 189 190 _print: function(chr) { 191 if (this.inTag) { 192 $(this.tag, this.el).last().append(chr); 193 if (this.tagCount < this.tagDuration) { 194 this.tagCount++; 195 } else { 196 this.inTag = false; 197 } 198 } else { 199 this.insert(chr); 200 } 201 }, 202 203 /* 204 If show cursor is enabled, move array starting point for the for loop back one, 205 so that the loop will not find the closing tag and delete the cursor. 206 */ 207 delete: function(chars) { 208 209 this.dTO = setTimeout(function() { 210 211 this._setPace(); 212 213 var a = this.tel.html().split(""); 214 215 var amount = chars === undefined || chars === null ? a.length - 1 : chars + 1; 216 217 // cut the array by a character 218 for (var n = a.length - 1; n > -1; n--) { 219 220 if ((a[n] === '>' || a[n] === ';') && this.s.html) { 221 for (var o = n; o > -1; o--) { 222 223 if (a.slice(o - 3, o + 1).join('') === '<br>') { 224 a.splice(o - 3, 4); 225 break; 226 } 227 228 if (a[o] === '&') { 229 a.splice(o, n - o + 1); 230 break; 231 } 232 233 if (a[o] === '<') { 234 if (a[o - 1] !== '>') { 235 if (a[o - 1] === ';') { 236 for (var p = o - 1; p > -1; p--) { 237 if (a[p] === '&') { 238 a.splice(p, o - p); 239 break; 240 } 241 } 242 } 243 244 a.splice(o - 1, 1); 245 break; 246 } 247 } 248 } 249 break; 250 } else { 251 a.pop(); 252 break; 253 } 254 255 } 256 257 // if we've found an empty set of HTML tags... 258 if (this.tel.html().indexOf('></') > -1) { 259 for (var i = this.tel.html().indexOf('></') - 2; i >= 0; i--) { 260 if (a[i] === '<') { 261 a.splice(i, a.length - i); 262 break; 263 } 264 } 265 } 266 267 this.tel.html(a.join('')); 268 269 // characters still in the string. 270 if (amount > (chars === undefined ? 0 : 2)) { 271 this.delete(chars === undefined ? undefined : chars - 1); 272 } else { 273 this._executeQueue(); 274 } 275 }.bind(this), this.deletePace); 276 }, 277 278 _isVisible: function() { 279 var win = $(window); 280 281 var viewport = { 282 top: win.scrollTop(), 283 left: win.scrollLeft() 284 }; 285 viewport.right = viewport.left + win.width(); 286 viewport.bottom = viewport.top + win.height(); 287 288 var height = this.el.outerHeight(); 289 var width = this.el.outerWidth(); 290 291 if (!width || !height) { 292 return false; 293 } 294 295 var bounds = this.el.offset(); 296 bounds.right = bounds.left + width; 297 bounds.bottom = bounds.top + height; 298 299 var visible = (!(viewport.right < bounds.left || viewport.left >
299 bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom)); 300 301 if (!visible) { 302 return false; 303 } 304 305 var deltas = { 306 top: Math.min(1, (bounds.bottom - viewport.top) / height), 307 bottom: Math.min(1, (viewport.bottom - bounds.top) / height), 308 left: Math.min(1, (bounds.right - viewport.left) / width), 309 right: Math.min(1, (viewport.right - bounds.left) / width) 310 }; 311 312 return (deltas.left * deltas.right) >= 1 && (deltas.top * deltas.bottom) >= 1; 313 }, 314 315 /* 316 Advance the function queue to execute the next function after the previous one has finished. 317 */ 318 _executeQueue: function() { 319 if (this.queueIndex < this.queue.length) { 320 var thisFunc = this.queue[this.queueIndex]; 321 this.queueIndex++; 322 323 // delay execution if looping back to the beginning of the queue. 324 if (this.isLooping && this.queueIndex === 1) { 325 this._to(function() { 326 thisFunc[0].bind(this)(thisFunc[1]); 327 }.bind(this), this.s.loopDelay / 2); 328 } else { 329 thisFunc[0].bind(this)(thisFunc[1]); 330 } 331 } else { 332 if (this.s.loop) { 333 this.queueIndex = 0; 334 this.isLooping = true; 335 this._to(function() { 336 this.delete(); 337 }.bind(this), this.s.loopDelay / 2); 338 } else { 339 this.s.callback(); 340 } 341 } 342 }, 343 344 _to: function(fn, time) { 345 setTimeout(function() { 346 fn(); 347 }.bind(this), time); 348 }, 349 350 _elCheck: function() { 351 if (!this.s.startDelete && this.el.html().length > 0) { 352 this.s.strings = this.el.html().trim(); 353 } else if (this.s.startDelete) { 354 this.stringsToDelete = this.el.html(); 355 } 356 }, 357 358 _toArray: function(str) { 359 return str.constructor === Array ? str.slice(0) : str.split('<br>'); 360 }, 361 362 _cursor: function() { 363 if (this.s.cursor) { 364 this.el.append('<span ' + this.style + 'class="ti-cursor">|</span>'); 365 var s = this.s.cursorSpeed; 366 var t = this; 367 (function loop() { 368 t.el.find('.ti-cursor').fadeTo(s / 2, 0).fadeTo(s / 2, 1); 369 t._to(loop, s); 370 })(); 371 } 372 }, 373 374 _setPace: function() { 375 var typeSpeed = this.s.speed; 376 var deleteSpeed = this.s.deleteSpeed !== undefined ? this.s.deleteSpeed : this.s.speed / 3; 377 var typeRange = typeSpeed / 2; 378 var deleteRange = deleteSpeed / 2; 379 380 this.typePace = this.s.lifeLike ? this._randomInRange(typeSpeed, typeRange) : typeSpeed; 381 this.deletePace = this.s.lifeLike ? this._randomInRange(deleteSpeed, deleteRange) : deleteSpeed; 382 }, 383 384 _randomInRange: function(value, range) { 385 return Math.abs(Math.random() * ((value + range) - (value - range)) + (value - range)); 386 }, 387 388 /* 389 Convert each string in the array to a sub-array. While happening, search the subarrays for HTML tags. 390 When a complete tag is found, slice the subarray to get the complete tag, insert it at the correct index, 391 and delete the range of indexes where the indexed tag used to be. 392 */ 393 _rake: function(array) { 394 for (var i = 0; i < array.length; i++) { 395 array[i] = array[i].split(''); 396 397 if (this.s.html) { 398 this.tPos = []; 399 var p = this.tPos; 400 var tag; 401 var en = false; 402 for (var j = 0; j < array[i].length; j++) { 403 404 if (array[i][j] === '<' || array[i][j] === '&') { 405 p[0] = j; 406 en = array[i][j] === '&' ? true : false; 407 } 408 409 if (array[i][j] === '>' || (array[i][j] === ';' && en)) { 410 p[1] = j; 411 j = 0; 412 tag = (array[i].slice(p[0], p[1] + 1)).join(''); 413 array[i].splice(p[0], (p[1] - p[0] + 1), tag); 414 en = false; 415 } 416 } 417 } 418 } 419 420 return array; 421 }, 422 423 /* 424 Get the start & ending positions of the string inside HTML opening & closing angle brackets, 425 and then create a DOM element of that string/tag name. 426 */
427 _makeNode: function(char) { 428 this.tag = $($.parseHTML(char)); 429 this._print(this.tag); 430 this.inTag = true; 431 } 432 }; 433 434 $.fn.tiType = function(str) { 435 var i = $(this).data('typeit'); 436 if (i === undefined) return $doc; 437 i.queue.push([i.type, str]); 438 return this; 439 }; 440 441 $.fn.tiDelete = function(num) { 442 var i = $(this).data('typeit'); 443 if (i === undefined) return $doc; 444 i.queue.push([i.delete, num]); 445 return this; 446 }; 447 448 $.fn.tiPause = function(time) { 449 var i = $(this).data('typeit'); 450 if (i === undefined) return $doc; 451 i.queue.push([i.pause, time]); 452 return this; 453 }; 454 455 $.fn.tiBreak = function() { 456 var i = $(this).data('typeit'); 457 if (i === undefined) return $doc; 458 i.queue.push([i.break]); 459 return this; 460 }; 461 462 $.fn.tiSettings = function(settings) { 463 var i = $(this).data('typeit'); 464 if (i === undefined) return $doc; 465 i.queue.push([i.mergeSet, settings]); 466 return this; 467 }; 468 469 }(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.