diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c5f108 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.project +node_modules +bower_components \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..59834cf --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,96 @@ +/*global module:false*/ +module.exports = function (grunt) { + + // Project configuration. + grunt.initConfig({ + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*! jquery.typer.js <%= pkg.version %>- <%= grunt.template.today("yyyy-mm-dd") %> */\n', + // Task configuration. + concat: { + options: { + banner: '<%= banner %>', + stripBanners: true + }, + dist: { + src: ['src/jquery.typer.js'], + dest: 'dist/jquery.typer.js' + } + }, + uglify: { + options: { + banner: '<%= banner %>' + }, + dist: { + src: '<%= concat.dist.dest %>', + dest: 'dist/jquery.typer.min.js' + } + }, + copy: { + dist: { + src:'src/jquery.typer.js', + dest: 'dist/jquery.typer.js' + } + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + unused: true, + boss: true, + eqnull: true, + browser: true, + globals: { + jQuery: true + } + }, + gruntfile: { + src: 'Gruntfile.js' + }, + dist: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + unused: true, + boss: true, + eqnull: true, + browser: true, + globals: { + jQuery: true, + console: true + } + }, + src: 'src/jquery.typer.js' + } + }, + watch: { + gruntfile: { + files: '<%= jshint.gruntfile.src %>', + tasks: ['jshint:gruntfile'] + } + } + }); + + // These plugins provide necessary tasks. + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-copy'); + + // Default task. + grunt.registerTask('dist', ['jshint:dist', 'concat:dist', 'uglify:dist', 'copy:dist']); + +}; diff --git a/README.md b/README.md index 3be0145..111501a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ There are some options that are available to you as well: typeDelay : 200, clearOnHighlight : true, typerDataAttr : 'data-typer-targets', + tapeColor : 'auto' // 'auto' or a css color value + typerOrder : 'random', // or 'sequential' typerInterval : 2000 } ``` @@ -45,6 +47,11 @@ Set the options individually: ```javascript $.typer.options.highlightSpeed = 500; ``` + +## Contributing + +Make your changes on `src/jquery.typer.js`. To generate updated minified distribution versions run `$ grunt dist`. + ## About jquery.typer.js was originally developed for [LayerVault](http://layervault.com) by [Kelly Sutton](http://kellysutton.com). diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..53d44af --- /dev/null +++ b/bower.json @@ -0,0 +1,29 @@ +{ + "name": "jquery-typer", + "version": "1.0.0", + "homepage": "https://github.com/kontur/jquery.typer.js", + "authors": [ + "Various", + "check github repo" + ], + "description": "JQuery plugin for typing out text", + "main": "./src/jquery.typer.js", + "keywords": [ + "JQuery", + "plugin", + "text", + "typewriter" + ], + "license": "MIT", + "dependencies": { + "jquery": ">=1.11.0" + }, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "app/bower_components", + "test", + "tests" + ] +} diff --git a/dist/jquery.typer.js b/dist/jquery.typer.js new file mode 100644 index 0000000..6ce6513 --- /dev/null +++ b/dist/jquery.typer.js @@ -0,0 +1,285 @@ +String.prototype.rightChars = function(n){ + if (n <= 0) { + return ""; + } + else if (n > this.length) { + return this; + } + else { + return this.substring(this.length, this.length - n); + } +}; + +(function($) { + var opts, + highlight, + clearText, + type, + spanWithColor, + clearDelay, + typeDelay, + clearData, + isNumber, + typeWithAttribute, + getHighlightInterval, + getTypeInterval, + intervalHandle, + typerInterval; + + + spanWithColor = function(color, backgroundColor) { + return $('') + .css('color', color) + .css('background-color', backgroundColor); + }; + + isNumber = function (n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + + clearData = function ($e) { + $e.removeData([ + 'typePosition', + 'highlightPosition', + 'leftStop', + 'rightStop', + 'primaryColor', + 'backgroundColor', + 'text', + 'typing' + ]); + }; + + type = function ($e) { + var + // position = $e.data('typePosition'), + text = $e.data('text'), + oldLeft = $e.data('oldLeft'), + oldRight = $e.data('oldRight'); + + // if (!isNumber(position)) { + // position = $e.data('leftStop'); + // } + + if (!text || text.length === 0) { + clearData($e); + return; + } + + $e.text( + oldLeft + + text.charAt(0) + + oldRight + ).data({ + oldLeft: oldLeft + text.charAt(0), + text: text.substring(1) + }); + + // $e.text($e.text() + text.substring(position, position + 1)); + + // $e.data('typePosition', position + 1); + + setTimeout(function () { + type($e); + }, getTypeInterval()); + }; + + clearText = function ($e) { + $e.find('span').remove(); + + setTimeout(function () { + type($e); + }, typeDelay()); + }; + + highlight = function ($e) { + var + position = $e.data('highlightPosition'), + leftText, + highlightedText, + rightText, + options = $e.data('options'); + + if (!isNumber(position)) { + position = $e.data('rightStop') + 1; + } + + if (position <= $e.data('leftStop')) { + setTimeout(function () { + clearText($e); + }, clearDelay()); + return; + } + + leftText = $e.text().substring(0, position - 1); + highlightedText = $e.text().substring(position - 1, $e.data('rightStop') + 1); + rightText = $e.text().substring($e.data('rightStop') + 1); + + $e.html(leftText) + .append( + spanWithColor( + options.highlightColor === 'auto' ? $e.css('background-color') : options.highlightColor, + options.backgroundColor === 'auto' ? $e.css('color') : options.backgroundColor + ) + .append(highlightedText) + ) + .append(rightText); + + $e.data('highlightPosition', position - 1); + + setTimeout(function () { + return highlight($e); + }, getHighlightInterval()); + }; + + typeWithAttribute = (function () { + var last = 0; + + return function($e) { + var targets; + var options = $e.data('options'); + + if ($e.data('typing')) { + return; + } + + try { + targets = JSON.parse($e.attr(options.typerDataAttr)).targets; + } catch (e) {} + + if (typeof targets === "undefined") { + targets = $.map($e.attr(options.typerDataAttr).split(','), function (e) { + return $.trim(e); + }); + } + + if (options.typerOrder === 'random') { + $e.typeTo(targets[Math.floor(Math.random()*targets.length)]); + } + else if (options.typerOrder === 'sequential') { + $e.typeTo(targets[last]); + last = (last < targets.length - 1) ? last + 1 : 0; + } + else { + console.error("Type order of '" + options.typerOrder + "' not supported"); + clearInterval(intervalHandle); + } + }; + })(); + + //-- Methods to attach to jQuery sets + + $.fn.typer = function(options) { + var $elements = $(this); + + if ($elements.length < 1) { + return; + } + + opts = jQuery.extend({}, $.fn.typer.defaults, options); + + return $elements.each(function () { + var $e = $(this); + $e.data('options', opts); + + if (typeof $e.attr(opts.typerDataAttr) === "undefined") { + return; + } + + typeWithAttribute($e); + intervalHandle = setInterval(function () { + typeWithAttribute($e); + }, typerInterval()); + }); + }; + + $.fn.typeTo = function (newString, options) { + var + $e = $(this), + currentText = $e.text(), + i = 0, + j = 0, + opts = jQuery.extend({}, $.fn.typer.defaults, options, $e.data('options')); + + if (currentText === newString) { + if (opts.debug === true) { + console.log("Our strings our equal, nothing to type"); + } + return $e; + } + + if (currentText !== $e.html()) { + if (opts.debug === true) { + console.error("Typer does not work on elements with child elements."); + } + return $e; + } + + $e.data('typing', true); + + if (opts.highlightEverything !== true) { + while (currentText.charAt(i) === newString.charAt(i)) { + i++; + } + + while (currentText.rightChars(j) === newString.rightChars(j)) { + j++; + } + } + + newString = newString.substring(i, newString.length - j + 1); + + $e.data({ + options: opts, + oldLeft: currentText.substring(0, i), + oldRight: currentText.rightChars(j - 1), + leftStop: i, + rightStop: currentText.length - j, + //primaryColor: opts.backgroundColor === 'auto' ? $e.data('primaryColor') : opts.backgroundColor, + //backgroundColor: $e.css('background-color'), + text: newString + }); + + highlight($e); + + return $e; + }; + + //-- Helper methods. These can one day be customized further to include things like ranges of delays. + + getHighlightInterval = function () { + return opts.highlightSpeed; + }; + + getTypeInterval = function () { + return opts.typeSpeed; + }; + + clearDelay = function () { + return opts.clearDelay; + }; + + typeDelay = function () { + return opts.typeDelay; + }; + + typerInterval = function () { + return opts.typerInterval; + }; + + $.fn.typer.defaults = { + highlightSpeed : 20, + typeSpeed : 100, + clearDelay : 500, + typeDelay : 200, + clearOnHighlight : true, + highlightEverything : true, + typerDataAttr : 'data-typer-targets', + typerInterval : 2000, + debug : false, + backgroundColor : 'auto', + highlightColor : 'auto', + typerOrder : 'random' + }; + +})(jQuery); diff --git a/dist/jquery.typer.min.js b/dist/jquery.typer.min.js new file mode 100644 index 0000000..9780bf8 --- /dev/null +++ b/dist/jquery.typer.min.js @@ -0,0 +1,2 @@ +/*! jquery.typer.js 1.0.0- 2015-03-29 */ +String.prototype.rightChars=function(a){return 0>=a?"":a>this.length?this:this.substring(this.length,this.length-a)},function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o;f=function(b,c){return a("").css("color",b).css("background-color",c)},j=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},i=function(a){a.removeData(["typePosition","highlightPosition","leftStop","rightStop","primaryColor","backgroundColor","text","typing"])},e=function(a){var b=a.data("text"),c=a.data("oldLeft"),d=a.data("oldRight");return b&&0!==b.length?(a.text(c+b.charAt(0)+d).data({oldLeft:c+b.charAt(0),text:b.substring(1)}),void setTimeout(function(){e(a)},m())):void i(a)},d=function(a){a.find("span").remove(),setTimeout(function(){e(a)},h())},c=function(a){var b,e,h,i=a.data("highlightPosition"),k=a.data("options");return j(i)||(i=a.data("rightStop")+1),i<=a.data("leftStop")?void setTimeout(function(){d(a)},g()):(b=a.text().substring(0,i-1),e=a.text().substring(i-1,a.data("rightStop")+1),h=a.text().substring(a.data("rightStop")+1),a.html(b).append(f("auto"===k.highlightColor?a.css("background-color"):k.highlightColor,"auto"===k.backgroundColor?a.css("color"):k.backgroundColor).append(e)).append(h),a.data("highlightPosition",i-1),void setTimeout(function(){return c(a)},l()))},k=function(){var b=0;return function(c){var d,e=c.data("options");if(!c.data("typing")){try{d=JSON.parse(c.attr(e.typerDataAttr)).targets}catch(f){}"undefined"==typeof d&&(d=a.map(c.attr(e.typerDataAttr).split(","),function(b){return a.trim(b)})),"random"===e.typerOrder?c.typeTo(d[Math.floor(Math.random()*d.length)]):"sequential"===e.typerOrder?(c.typeTo(d[b]),b=b", + "bugs": { + "url": "https://github.com/kontur/jquery.typer.js/issues" + }, + "homepage": "https://github.com/kontur/jquery.typer.js" +} diff --git a/src/jquery.typer.js b/src/jquery.typer.js index d1520cc..6ce6513 100644 --- a/src/jquery.typer.js +++ b/src/jquery.typer.js @@ -11,35 +11,23 @@ String.prototype.rightChars = function(n){ }; (function($) { - var - options = { - highlightSpeed : 20, - typeSpeed : 100, - clearDelay : 500, - typeDelay : 200, - clearOnHighlight : true, - typerDataAttr : 'data-typer-targets', - typerInterval : 2000 - }, - highlight, - clearText, - backspace, - type, - spanWithColor, - clearDelay, - typeDelay, - clearData, - isNumber, - typeWithAttribute, - getHighlightInterval, - getTypeInterval, - typerInterval; + var opts, + highlight, + clearText, + type, + spanWithColor, + clearDelay, + typeDelay, + clearData, + isNumber, + typeWithAttribute, + getHighlightInterval, + getTypeInterval, + intervalHandle, + typerInterval; - spanWithColor = function(color, backgroundColor) { - if (color === 'rgba(0, 0, 0, 0)') { - color = 'rgb(255, 255, 255)'; - } + spanWithColor = function(color, backgroundColor) { return $('') .css('color', color) .css('background-color', backgroundColor); @@ -78,7 +66,6 @@ String.prototype.rightChars = function(n){ return; } - $e.text( oldLeft + text.charAt(0) + @@ -110,7 +97,8 @@ String.prototype.rightChars = function(n){ position = $e.data('highlightPosition'), leftText, highlightedText, - rightText; + rightText, + options = $e.data('options'); if (!isNumber(position)) { position = $e.data('rightStop') + 1; @@ -130,8 +118,8 @@ String.prototype.rightChars = function(n){ $e.html(leftText) .append( spanWithColor( - $e.data('backgroundColor'), - $e.data('primaryColor') + options.highlightColor === 'auto' ? $e.css('background-color') : options.highlightColor, + options.backgroundColor === 'auto' ? $e.css('color') : options.backgroundColor ) .append(highlightedText) ) @@ -144,90 +132,111 @@ String.prototype.rightChars = function(n){ }, getHighlightInterval()); }; - typeWithAttribute = function ($e) { - var targets; + typeWithAttribute = (function () { + var last = 0; - if ($e.data('typing')) { - return; - } + return function($e) { + var targets; + var options = $e.data('options'); - try { - targets = JSON.parse($e.attr($.typer.options.typerDataAttr)).targets; - } catch (e) {} + if ($e.data('typing')) { + return; + } - if (typeof targets === "undefined") { - targets = $.map($e.attr($.typer.options.typerDataAttr).split(','), function (e) { - return $.trim(e); - }); - } + try { + targets = JSON.parse($e.attr(options.typerDataAttr)).targets; + } catch (e) {} - $e.typeTo(targets[Math.floor(Math.random()*targets.length)]); - }; + if (typeof targets === "undefined") { + targets = $.map($e.attr(options.typerDataAttr).split(','), function (e) { + return $.trim(e); + }); + } - // Expose our options to the world. - $.typer = (function () { - return { options: options }; + if (options.typerOrder === 'random') { + $e.typeTo(targets[Math.floor(Math.random()*targets.length)]); + } + else if (options.typerOrder === 'sequential') { + $e.typeTo(targets[last]); + last = (last < targets.length - 1) ? last + 1 : 0; + } + else { + console.error("Type order of '" + options.typerOrder + "' not supported"); + clearInterval(intervalHandle); + } + }; })(); - $.extend($.typer, { - options: options - }); - //-- Methods to attach to jQuery sets - $.fn.typer = function() { + $.fn.typer = function(options) { var $elements = $(this); + if ($elements.length < 1) { + return; + } + + opts = jQuery.extend({}, $.fn.typer.defaults, options); + return $elements.each(function () { var $e = $(this); + $e.data('options', opts); - if (typeof $e.attr($.typer.options.typerDataAttr) === "undefined") { + if (typeof $e.attr(opts.typerDataAttr) === "undefined") { return; } typeWithAttribute($e); - setInterval(function () { + intervalHandle = setInterval(function () { typeWithAttribute($e); }, typerInterval()); }); }; - $.fn.typeTo = function (newString) { + $.fn.typeTo = function (newString, options) { var $e = $(this), currentText = $e.text(), i = 0, - j = 0; + j = 0, + opts = jQuery.extend({}, $.fn.typer.defaults, options, $e.data('options')); if (currentText === newString) { - console.log("Our strings our equal, nothing to type"); + if (opts.debug === true) { + console.log("Our strings our equal, nothing to type"); + } return $e; } if (currentText !== $e.html()) { - console.error("Typer does not work on elements with child elements."); + if (opts.debug === true) { + console.error("Typer does not work on elements with child elements."); + } return $e; } $e.data('typing', true); - while (currentText.charAt(i) === newString.charAt(i)) { - i++; - } + if (opts.highlightEverything !== true) { + while (currentText.charAt(i) === newString.charAt(i)) { + i++; + } - while (currentText.rightChars(j) === newString.rightChars(j)) { - j++; + while (currentText.rightChars(j) === newString.rightChars(j)) { + j++; + } } newString = newString.substring(i, newString.length - j + 1); $e.data({ + options: opts, oldLeft: currentText.substring(0, i), oldRight: currentText.rightChars(j - 1), leftStop: i, rightStop: currentText.length - j, - primaryColor: $e.css('color'), - backgroundColor: $e.css('background-color'), + //primaryColor: opts.backgroundColor === 'auto' ? $e.data('primaryColor') : opts.backgroundColor, + //backgroundColor: $e.css('background-color'), text: newString }); @@ -239,22 +248,38 @@ String.prototype.rightChars = function(n){ //-- Helper methods. These can one day be customized further to include things like ranges of delays. getHighlightInterval = function () { - return $.typer.options.highlightSpeed; + return opts.highlightSpeed; }; getTypeInterval = function () { - return $.typer.options.typeSpeed; - }, + return opts.typeSpeed; + }; clearDelay = function () { - return $.typer.options.clearDelay; - }, + return opts.clearDelay; + }; typeDelay = function () { - return $.typer.options.typeDelay; + return opts.typeDelay; }; typerInterval = function () { - return $.typer.options.typerInterval; + return opts.typerInterval; }; + + $.fn.typer.defaults = { + highlightSpeed : 20, + typeSpeed : 100, + clearDelay : 500, + typeDelay : 200, + clearOnHighlight : true, + highlightEverything : true, + typerDataAttr : 'data-typer-targets', + typerInterval : 2000, + debug : false, + backgroundColor : 'auto', + highlightColor : 'auto', + typerOrder : 'random' + }; + })(jQuery); diff --git a/test.html b/test.html index a9311f0..37c0a8f 100644 --- a/test.html +++ b/test.html @@ -1,20 +1,204 @@ + jquery.typer.js test page - - + + + -

Hello, World!

-

Welcome

+

Typer testing and samples!

+ + +
+

Default with data-typer-targets

+ +

Welcome

+ +
+ + +
+

Default, automatically using the element's text color as highlightColor

+ $("#element").typer(); + +

Hello World!

+ +
+ + +
+

Default, automatically using the element's text color as highlightColor

+ $("#element").typer(); + +

Hello World!

+ +
+ + +
+

With option backgroundColor

+$("#element").typer({ + backgroundColor: 'orange' +}); + + +

Hello World!

+ +
+ + +
+

With option highlightColor

+$("#element").typer({ + highlightColor: 'orange' +}); + + +

Hello World!

+ +
+ + +
+

With option highlightColor and backgroundColor combined

+$("#element").typer({ + highlightColor: 'orange', + backgroundColor: 'purple' +}); + +

Hello World!

+ +
+ + +
+

With option random

+ Randomize the strings in data-typer-targets="lorem,ipsum,dol,solor" + $("#element").typer({ + typerOrder: 'random' +}); + +

Hello World!

+ +
+ + +
+

With option sequential

+ Type the strings in data-typer-targets="lorem,ipsum,dol,solor" in sequence + $("#element").typer({ + typerOrder: 'sequential' +}); + +

Hello World!

+ +
+ + +
+

Using .typeTo()

+ $("#element").typeTo("Hello Typer!"); +

+ + +
-

Welcome

-