diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faa7835 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +._* +.DS_Store +node_modules diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..4b8c932 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,40 @@ +module.exports = function(grunt) { + + // Import task configurations + var jshint = require('./grunt/jshint'), + uglify = require('./grunt/uglify'), + watch = require('./grunt/watch'); + + // Main project configuration. + grunt.initConfig({ + // Read NPM package information + pkg: grunt.file.readJSON('package.json'), + // Static code analysis of Javascript + jshint: jshint, + // Concatenate, minify and beautify/uglify Javascript + uglify: uglify.config, + // Watcher + watch: watch + }); + + // Load the grunt tasks + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-watch'); + + // Compile production files + grunt.registerTask('dist', [ + 'jshint', + 'uglify:dist' + ]); + + // Compile developer friendly environment + grunt.registerTask('dev', [ + 'jshint', + 'uglify:dev' + ]); + + // Default task(s). + grunt.registerTask('default', 'dist'); + +}; diff --git a/README.md b/README.md index f3e72a6..1900d01 100644 --- a/README.md +++ b/README.md @@ -51,4 +51,22 @@ Progressive.js is a work-in-progress. If you come across any issues not mentione - Multiple enhancements cannot be applied to individual elements - You can only use a class name to select elements -- The fallback in browsers that don't support CSS animations uses the `onload` event. It would be nicer to use `DOMContentLoaded` where available. \ No newline at end of file +- The fallback in browsers that don't support CSS animations uses the `onload` event. It would be nicer to use `DOMContentLoaded` where available. + +##Contribute + +If you would like to contribute, I would advise you to setup `grunt` locally. That way the javascript is both automatically checked for syntax errors and minified before your changes are pushed to the remote branch. + +You will need to have `grunt` installed globally. Run: + + npm install -g grunt-cli + +Once you have installed `grunt` globally, run: + + npm install + +...while at the root of this repository. The dependencies/plugins will be retrieved and you'll have `grunt` running the linting and minification tasks on the background as you develop, by running: + + grunt watch + +Run this command on a dedicated shell so that it keeps running uninterrupted. Enjoy! \ No newline at end of file diff --git a/grunt/jshint.js b/grunt/jshint.js new file mode 100644 index 0000000..87ffd17 --- /dev/null +++ b/grunt/jshint.js @@ -0,0 +1,14 @@ +var gruntfiles = ['Gruntfile.js', 'config/*.js']; +var jsfiles = [ + 'Gruntfile.js', + 'config/*.js', + 'progressive.js' +]; + +module.exports = { + options: { + evil: true + }, + grunt: gruntfiles, + all: jsfiles +}; diff --git a/grunt/uglify.js b/grunt/uglify.js new file mode 100644 index 0000000..9cc2706 --- /dev/null +++ b/grunt/uglify.js @@ -0,0 +1,23 @@ +var files = { + 'progressive.min.js': [ + 'progressive.js' + ] +}; + +// Grunt configuration settings +module.exports.config = { + options: { + mangle: false, + report: 'min' // 'false', 'min' or 'gzip' Default: false + }, + dist: { + files: files + }, + dev: { + options: { + compress: false, + beautify: true + }, + files: files + } +}; diff --git a/grunt/watch.js b/grunt/watch.js new file mode 100644 index 0000000..768648c --- /dev/null +++ b/grunt/watch.js @@ -0,0 +1,15 @@ +// This task watches files and runs the appropriate +// tasks when files are changed. +module.exports = { + + grunt: { + files: ['Gruntfile.js', 'config/*.js'], + tasks: ['jshint:grunt'] + }, + + js: { + files: ['config/*.js', 'progressive.js'], + tasks: ['jshint', 'uglify:dist'] + } + +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..d606b18 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "go", + "version": "0.0.0", + "private": true, + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-jshint": "~0.6.0", + "grunt-contrib-uglify": "~0.1.1", + "grunt-contrib-watch": "~0.4.4" + }, + "engines": { + "node": ">=0.8.0" + } +} diff --git a/progressive.js b/progressive.js index 25958e4..b4924d9 100644 --- a/progressive.js +++ b/progressive.js @@ -6,125 +6,125 @@ Progressive.js v0.1 - Brought to you by James Allardice (@james_allardice) and K var Progressive = (function () { - "use strict"; + "use strict"; - var styleElem = document.createElement("style"), - animationSupport = false, - animationString, - keyframePrefix = "", - domPrefixes = ["Webkit", "Moz", "O", "ms", "Khtml"], - numPrefixes = domPrefixes.length, - prefix, - enhance, - i; + var styleElem = document.createElement("style"), + animationSupport = false, + animationString, + keyframePrefix = "", + domPrefixes = ["Webkit", "Moz", "O", "ms", "Khtml"], + numPrefixes = domPrefixes.length, + prefix, + enhance, + i; - // Polyfill `getElementsByClassName`, which is used by the fallback method - document.getElementsByClassName = document.getElementsByClassName || function (className) { - var classElements = [], - els, - elsLen, - pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"), - i, - j; - els = document.getElementsByTagName("*"); - elsLen = els.length; - for (i = 0, j = 0; i < elsLen; i++) { - if (pattern.test(els[i].className)) { - classElements[j] = els[i]; - j++; - } - } - return classElements; - }; + // Polyfill `getElementsByClassName`, which is used by the fallback method + document.getElementsByClassName = document.getElementsByClassName || function (className) { + var classElements = [], + els, + elsLen, + pattern = new RegExp("(^|\\s)" + className + "(\\s|$)"), + i, + j; + els = document.getElementsByTagName("*"); + elsLen = els.length; + for (i = 0, j = 0; i < elsLen; i++) { + if (pattern.test(els[i].className)) { + classElements[j] = els[i]; + j++; + } + } + return classElements; + }; - // Feature/vendor prefix detection for CSS animations - if (styleElem.style.animationName) { - animationSupport = true; - } else { - for (i = 0; i < numPrefixes; i++) { - if (styleElem.style[domPrefixes[i] + "AnimationName"] !== undefined) { - prefix = domPrefixes[i]; - animationString = prefix + "Animation"; - keyframePrefix = "-" + prefix.toLowerCase() + "-"; - animationSupport = true; - break; - } - } - } + // Feature/vendor prefix detection for CSS animations + if (styleElem.style.animationName) { + animationSupport = true; + } else { + for (i = 0; i < numPrefixes; i++) { + if (styleElem.style[domPrefixes[i] + "AnimationName"] !== undefined) { + prefix = domPrefixes[i]; + animationString = prefix + "Animation"; + keyframePrefix = "-" + prefix.toLowerCase() + "-"; + animationSupport = true; + break; + } + } + } - // The main `enhance` method to register callbacks that will be executed when certain DOM elements are inserted - enhance = function (enhancements) { - var ruleText, - styleRules = {}, - enhancement, - onNodeInserted, - // The fallback function is executed on window load. It will handle any elements not handled by the animation callbacks - fallback = function () { - var enhancement, - elems, - numElems, - i; - for (enhancement in enhancements) { - if (enhancements.hasOwnProperty(enhancement)) { - elems = document.getElementsByClassName(enhancements[enhancement].className); - numElems = elems.length; - if (!enhancements[enhancement].count || enhancements[enhancement].count < numElems) { - for (i = 0; i < numElems; i++) { - enhancements[enhancement].callback.call(elems[i]); - } - } - } - } - }; - ruleText = ""; - // This is used as a callback to the CSS animation events. It's used to fire the supplied enhancements, in the context of each element - onNodeInserted = function (e) { - var enhancement = enhancements[e.animationName]; - if (enhancement) { - enhancement.count = ++enhancement.count || 1; - enhancement.callback.call(e.target); - } - }; + // The main `enhance` method to register callbacks that will be executed when certain DOM elements are inserted + enhance = function (enhancements) { + var ruleText, + styleRules = {}, + enhancement, + onNodeInserted, + // The fallback function is executed on window load. It will handle any elements not handled by the animation callbacks + fallback = function () { + var enhancement, + elems, + numElems, + i; + for (enhancement in enhancements) { + if (enhancements.hasOwnProperty(enhancement)) { + elems = document.getElementsByClassName(enhancements[enhancement].className); + numElems = elems.length; + if (!enhancements[enhancement].count || enhancements[enhancement].count < numElems) { + for (i = 0; i < numElems; i++) { + enhancements[enhancement].callback.call(elems[i]); + } + } + } + } + }; + ruleText = ""; + // This is used as a callback to the CSS animation events. It's used to fire the supplied enhancements, in the context of each element + onNodeInserted = function (e) { + var enhancement = enhancements[e.animationName]; + if (enhancement) { + enhancement.count = ++enhancement.count || 1; + enhancement.callback.call(e.target); + } + }; - if (animationSupport) { - // Build up a set of CSS rules to run animations on newly inserted elements - for (enhancement in enhancements) { - if (enhancements.hasOwnProperty(enhancement)) { - ruleText += "." + enhancements[enhancement].className + "{"; - ruleText += keyframePrefix + "animation:" + enhancement + " 0.001s;"; - ruleText += "}"; - ruleText += "@" + keyframePrefix + "keyframes " + enhancement + "{from{clip:rect(1px,auto,auto,auto);}to{clip:rect(0px,auto,auto,auto);}}"; - } - } + if (animationSupport) { + // Build up a set of CSS rules to run animations on newly inserted elements + for (enhancement in enhancements) { + if (enhancements.hasOwnProperty(enhancement)) { + ruleText += "." + enhancements[enhancement].className + "{"; + ruleText += keyframePrefix + "animation:" + enhancement + " 0.001s;"; + ruleText += "}"; + ruleText += "@" + keyframePrefix + "keyframes " + enhancement + "{from{clip:rect(1px,auto,auto,auto);}to{clip:rect(0px,auto,auto,auto);}}"; + } + } - styleRules = document.createTextNode(ruleText); - styleElem.type = "text/css"; - if (styleElem.styleSheet) { - styleElem.styleSheet.cssText = styleRules.nodeValue; - } else { - styleElem.appendChild(styleRules); - } + styleRules = document.createTextNode(ruleText); + styleElem.type = "text/css"; + if (styleElem.styleSheet) { + styleElem.styleSheet.cssText = styleRules.nodeValue; + } else { + styleElem.appendChild(styleRules); + } - document.getElementsByTagName("script")[0].parentNode.appendChild(styleElem); + document.getElementsByTagName("script")[0].parentNode.appendChild(styleElem); - // Register cross-browser CSS animation event handlers - document.addEventListener("animationstart", onNodeInserted, false); - document.addEventListener("oanimationstart", onNodeInserted, false); - document.addEventListener("MSAnimationStart", onNodeInserted, false); - document.addEventListener("webkitAnimationStart", onNodeInserted, false); - } + // Register cross-browser CSS animation event handlers + document.addEventListener("animationstart", onNodeInserted, false); + document.addEventListener("oanimationstart", onNodeInserted, false); + document.addEventListener("MSAnimationStart", onNodeInserted, false); + document.addEventListener("webkitAnimationStart", onNodeInserted, false); + } - // Register fallback event handlers - if (window.addEventListener) { - window.addEventListener("load", fallback); - } else if (window.attachEvent) { - window.attachEvent("onload", fallback); - } - }; + // Register fallback event handlers + if (window.addEventListener) { + window.addEventListener("load", fallback); + } else if (window.attachEvent) { + window.attachEvent("onload", fallback); + } + }; - // Expose public methods - return { - enhance: enhance - }; + // Expose public methods + return { + enhance: enhance + }; }()); \ No newline at end of file diff --git a/progressive.min.js b/progressive.min.js index 872cc83..3a485b7 100644 --- a/progressive.min.js +++ b/progressive.min.js @@ -1,3 +1 @@ -var Progressive=function(){var d=document.createElement("style"),h=!1,i="",b=["Webkit","Moz","O","ms","Khtml"],k=b.length,g;document.getElementsByClassName=document.getElementsByClassName||function(c){var a=[],e,d=RegExp("(^|\\s)"+c+"(\\s|$)"),f,j,c=document.getElementsByTagName("*");e=c.length;for(j=f=0;fi;i++)pattern.test(els[i].className)&&(classElements[j]=els[i],j++);return classElements},styleElem.style.animationName)animationSupport=!0;else for(i=0;numPrefixes>i;i++)if(void 0!==styleElem.style[domPrefixes[i]+"AnimationName"]){prefix=domPrefixes[i],animationString=prefix+"Animation",keyframePrefix="-"+prefix.toLowerCase()+"-",animationSupport=!0;break}return enhance=function(enhancements){var ruleText,enhancement,onNodeInserted,styleRules={},fallback=function(){var enhancement,elems,numElems,i;for(enhancement in enhancements)if(enhancements.hasOwnProperty(enhancement)&&(elems=document.getElementsByClassName(enhancements[enhancement].className),numElems=elems.length,!enhancements[enhancement].count||numElems>enhancements[enhancement].count))for(i=0;numElems>i;i++)enhancements[enhancement].callback.call(elems[i])};if(ruleText="",onNodeInserted=function(e){var enhancement=enhancements[e.animationName];enhancement&&(enhancement.count=++enhancement.count||1,enhancement.callback.call(e.target))},animationSupport){for(enhancement in enhancements)enhancements.hasOwnProperty(enhancement)&&(ruleText+="."+enhancements[enhancement].className+"{",ruleText+=keyframePrefix+"animation:"+enhancement+" 0.001s;",ruleText+="}",ruleText+="@"+keyframePrefix+"keyframes "+enhancement+"{from{clip:rect(1px,auto,auto,auto);}to{clip:rect(0px,auto,auto,auto);}}");styleRules=document.createTextNode(ruleText),styleElem.type="text/css",styleElem.styleSheet?styleElem.styleSheet.cssText=styleRules.nodeValue:styleElem.appendChild(styleRules),document.getElementsByTagName("script")[0].parentNode.appendChild(styleElem),document.addEventListener("animationstart",onNodeInserted,!1),document.addEventListener("oanimationstart",onNodeInserted,!1),document.addEventListener("MSAnimationStart",onNodeInserted,!1),document.addEventListener("webkitAnimationStart",onNodeInserted,!1)}window.addEventListener?window.addEventListener("load",fallback):window.attachEvent&&window.attachEvent("onload",fallback)},{enhance:enhance}}(); \ No newline at end of file