From fa7c5201e9472f5a0d0971cdfdec2d36657963cc Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Mon, 16 Sep 2013 21:12:35 -0400 Subject: [PATCH 1/9] Add .jshintrc.comments, remove comments from master .jshintrc as they break some grunt tasks currently --- .jshintrc | 154 +++++++++++++++++++-------------------------- .jshintrc.comments | 106 +++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 88 deletions(-) create mode 100644 .jshintrc.comments diff --git a/.jshintrc b/.jshintrc index 2a024e2..3548450 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,93 +1,69 @@ -/* - * - * JSHint Configuration File for IntentionJS project. - * See http://jshint.com/docs/ for more details - * - */ - { - "maxerr" : 100, // {int} Maximum error before stopping - - // Enforcing - "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) - "camelcase" : false, // true: Identifiers must be in camelCase - // TODO change to true with rule for __setters__ - "curly" : true, // true: Require {} for every new block or scope - "eqeqeq" : true, // true: Require triple equals (===) for comparison - "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() - "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` - "indent" : 2, // {int} Number of spaces to use for indentation - "latedef" : true, // true: Require variables/functions to be defined before being used - "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` - "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` - "noempty" : true, // true: Prohibit use of empty blocks - "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) - "plusplus" : false, // true: Prohibit use of `++` & `--` - "quotmark" : "single", // Quotation mark consistency: - // false : do nothing (default) - // true : ensure whatever is used is consistent - // "single" : require single quotes - // "double" : require double quotes - "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) - "unused" : true, // true: Require all defined variables be used - "strict" : true, // true: Requires all functions run in ES5 Strict Mode - "trailing" : true, // true: Prohibit trailing whitespaces - "maxparams" : false, // {int} Max number of formal params allowed per function - "maxdepth" : false, // {int} Max depth of nested blocks (within functions) - "maxstatements" : false, // {int} Max number statements per function - "maxcomplexity" : false, // {int} Max cyclomatic complexity per function - "maxlen" : false, // {int} Max number of characters per line - - // Relaxing - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) - "boss" : false, // true: Tolerate assignments where comparisons would be expected - "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. - "eqnull" : true, // true: Tolerate use of `== null` - "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) - "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) - // (ex: `for each`, multiple try/catch, function expression…) - "evil" : false, // true: Tolerate use of `eval` and `new Function()` - "expr" : false, // true: Tolerate `ExpressionStatement` as Programs - "funcscope" : false, // true: Tolerate defining variables inside control statements" - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') - "iterator" : false, // true: Tolerate using the `__iterator__` property - "lastsemic" : true, // true: Tolerate omitting a semicolon for the last statement of a 1-line block - "laxbreak" : false, // true: Tolerate possibly unsafe line breakings - "laxcomma" : false, // true: Tolerate comma-first style coding - "loopfunc" : false, // true: Tolerate functions being defined in loops - "multistr" : false, // true: Tolerate multi-line strings - "proto" : false, // true: Tolerate using the `__proto__` property - "scripturl" : false, // true: Tolerate script-targeted URLs - "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment - "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` - "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation - "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` - "validthis" : false, // true: Tolerate using this in a non-constructor function - // "regexp" : false, // true if . and [^...] should be allowed in RegExp literals. They match more material than might be expected, allowing attackers to confuse applications. These forms should not be used when validating in secure applications. - - // Environments - "browser" : true, // Web Browser (window, document, etc) - "couch" : false, // CouchDB - "devel" : true, // Development/debugging (alert, confirm, etc) - "dojo" : false, // Dojo Toolkit - "jquery" : true, // jQuery - "mootools" : false, // MooTools - "node" : true, // Node.js - "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "prototypejs" : false, // Prototype and Scriptaculous - "rhino" : false, // Rhino - "worker" : false, // Web Workers - "wsh" : false, // Windows Scripting Host - "yui" : false, // Yahoo User Interface - - // Legacy - "nomen" : false, // true: Prohibit dangling `_` in variables - "onevar" : false, // true: Allow only one `var` statement per function - "passfail" : false, // true: Stop on first error - "white" : true, // true: Check against strict whitespace and indentation rules - - // Custom Globals + "maxerr" : 100, + "bitwise" : false, + "camelcase" : false, + "curly" : true, + "eqeqeq" : true, + "forin" : true, + "immed" : true, + "indent" : 2, + "latedef" : true, + "newcap" : true, + "noarg" : true, + "noempty" : true, + "nonew" : false, + "plusplus" : false, + "quotmark" : "single", + "undef" : true, + "unused" : true, + "strict" : true, + "trailing" : false, + "maxparams" : false, + "maxdepth" : false, + "maxstatements" : false, + "maxcomplexity" : false, + "maxlen" : false, + "asi" : false, + "boss" : false, + "debug" : true, + "eqnull" : true, + "esnext" : true, + "moz" : false, + "evil" : false, + "expr" : false, + "funcscope" : false, + "globalstrict" : false, + "iterator" : false, + "lastsemic" : true, + "laxbreak" : true, + "laxcomma" : false, + "loopfunc" : false, + "multistr" : false, + "proto" : false, + "scripturl" : false, + "smarttabs" : true, + "shadow" : false, + "sub" : false, + "supernew" : false, + "validthis" : false, + "browser" : true, + "couch" : false, + "devel" : true, + "dojo" : false, + "jquery" : true, + "mootools" : false, + "node" : true, + "nonstandard" : false, + "prototypejs" : false, + "rhino" : false, + "worker" : false, + "wsh" : false, + "yui" : false, + "nomen" : false, + "onevar" : false, + "passfail" : false, + "white" : true, "globals" : { "_" : false, "it" : false, @@ -96,7 +72,9 @@ "describe" : false, "expect" : false, "module" : true, + "window" : false, "Intention" : true } } + diff --git a/.jshintrc.comments b/.jshintrc.comments new file mode 100644 index 0000000..bcead1b --- /dev/null +++ b/.jshintrc.comments @@ -0,0 +1,106 @@ + + + +/* + * + * JSHint Configuration File for IntentionJS project. + * See http://jshint.com/docs/ for more details + * + */ + +{ + + "maxerr" : 100, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + // TODO change to true with rule for __setters__ + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent" : 2, // {int} Number of spaces to use for indentation + "latedef" : true, // true: Require variables/functions to be defined before being used + "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : "single", // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used + "strict" : true, // true: Requires all functions run in ES5 Strict Mode + "trailing" : false, // true: Prohibit trailing whitespaces + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : true, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : true, // true: Tolerate use of `== null` + "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements" + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : true, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : true, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "smarttabs" : true, // true: Tolerate mixed tabs/spaces when used for alignment + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + // "regexp" : false, // true if . and [^...] should be allowed in RegExp literals. They match more material than might be expected, allowing attackers to confuse applications. These forms should not be used when validating in secure applications. + + // Environments + "browser" : true, // Web Browser (window, document, etc) + "couch" : false, // CouchDB + "devel" : true, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jquery" : true, // jQuery + "mootools" : false, // MooTools + "node" : true, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "rhino" : false, // Rhino + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Legacy + "nomen" : false, // true: Prohibit dangling `_` in variables + "onevar" : false, // true: Allow only one `var` statement per function + "passfail" : false, // true: Stop on first error + "white" : false, // true: Check against strict whitespace and indentation rules + + // Custom Globals + "globals" : { + "_" : false, + "it" : false, + "underscore": false, + "define" : false, + "describe" : false, + "expect" : false, + "module" : true, + "window" : false, + "Intention" : true + } + +} From 360f3d1e9c9580901e2dd3f3237fcea0d590f8a0 Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Mon, 16 Sep 2013 21:27:36 -0400 Subject: [PATCH 2/9] Add grunt fixmyjs tasks to the project --- Gruntfile.js | 24 +++++++++++++++++++++++- package.json | 5 ++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0a8f736..132f6f9 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,14 +1,17 @@ module.exports = function (grunt) { 'use strict'; grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-fixmyjs'); //grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-release'); + grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), release: { options: { npm: false } }, jshint: { - files: [ + all: [ '**.js', 'test/*.js' ], @@ -20,6 +23,19 @@ module.exports = function (grunt) { jshintrc: '.jshintrc' } }, + clean: { + intention: ['code/**/*'] + }, + fixmyjs: { + options: { + jshintrc: '.jshintrc' + }, + intention: { + files: [ + {expand: true, cwd: './', src: ['*.js'], dest: 'code/fixed/', ext: '.js'} + ] + } + }, uglify: { intention: { options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, @@ -35,4 +51,10 @@ module.exports = function (grunt) { 'jshint', 'uglify' ]); + grunt.registerTask('build', [ + 'jshint', + 'clean', + 'fixmyjs', + 'uglify' + ]); }; \ No newline at end of file diff --git a/package.json b/package.json index e5b53cb..bd63a72 100755 --- a/package.json +++ b/package.json @@ -26,7 +26,10 @@ "grunt-contrib-uglify": "~0.2.2", "chai": "*", "mocha": "*", - "grunt-release": "~0.5.1" + "grunt-release": "~0.5.1", + "grunt-fixmyjs": "~0.1.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-jshint": "~0.6.4" }, "bugs": { "url": "https://github.com/wsjdesign/intentionjs/issues" From e52c63c9240cded826595a6236818b8e575cec02 Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 00:29:54 -0400 Subject: [PATCH 3/9] Add mocha tests and watch tasks to Gruntfile.js --- Gruntfile.js | 36 +++++++++++++++++++++++++++--------- package.json | 4 +++- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 132f6f9..2692fd5 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,26 +1,30 @@ module.exports = function (grunt) { 'use strict'; + grunt.loadNpmTasks('grunt-mocha'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-fixmyjs'); //grunt.loadNpmTasks('grunt-contrib-requirejs'); - grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-release'); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), release: { options: { npm: false } }, jshint: { - all: [ - '**.js', - 'test/*.js' + files: [ + 'Gruntfile.js', + 'intention.js', + 'context.js', + 'test/**/*.js' ], options: { ignores: [ - 'code/*', 'test/vendor/**/*' ], - jshintrc: '.jshintrc' + jshintrc: '.jshintrc', } }, clean: { @@ -32,10 +36,17 @@ module.exports = function (grunt) { }, intention: { files: [ - {expand: true, cwd: './', src: ['*.js'], dest: 'code/fixed/', ext: '.js'} + {expand: true, src: ['*.js'], dest: 'code/', ext: '.js'} ] } }, + mocha: { + index: ['test/index.html'] + }, + watch: { + files: ['<%= jshint.files %>'], + tasks: ['jshint', 'mocha'] + }, uglify: { intention: { options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, @@ -47,14 +58,21 @@ module.exports = function (grunt) { } } }); + grunt.registerTask('test', [ + 'mocha' + ]); grunt.registerTask('default', [ 'jshint', - 'uglify' + 'mocha', + 'fixmyjs', + 'uglify', + 'watch' ]); grunt.registerTask('build', [ 'jshint', 'clean', 'fixmyjs', + 'mocha', 'uglify' ]); -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index bd63a72..0797859 100755 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "grunt-release": "~0.5.1", "grunt-fixmyjs": "~0.1.0", "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-jshint": "~0.6.4" + "grunt-contrib-jshint": "~0.6.4", + "grunt-mocha": "~0.4.1", + "grunt-contrib-watch": "~0.5.3" }, "bugs": { "url": "https://github.com/wsjdesign/intentionjs/issues" From f34840de89b624922efa405df23279fdcea6497e Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 00:29:54 -0400 Subject: [PATCH 4/9] Add mocha tests and watch tasks to Gruntfile.js --- Gruntfile.js | 35 ++++++++++++++++++++++++++--------- package.json | 4 +++- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 132f6f9..00a0ce6 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,26 +1,29 @@ module.exports = function (grunt) { 'use strict'; + grunt.loadNpmTasks('grunt-mocha'); + grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-fixmyjs'); //grunt.loadNpmTasks('grunt-contrib-requirejs'); - grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-release'); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), release: { options: { npm: false } }, jshint: { - all: [ - '**.js', - 'test/*.js' + files: [ + 'Gruntfile.js', + 'intention.js', + 'context.js', + 'test/**/*.js' ], options: { ignores: [ - 'code/*', 'test/vendor/**/*' ], - jshintrc: '.jshintrc' + jshintrc: '.jshintrc', } }, clean: { @@ -32,10 +35,17 @@ module.exports = function (grunt) { }, intention: { files: [ - {expand: true, cwd: './', src: ['*.js'], dest: 'code/fixed/', ext: '.js'} + {expand: true, src: ['*.js'], dest: 'code/', ext: '.js'} ] } }, + mocha: { + index: ['test/index.html'] + }, + watch: { + files: ['<%= jshint.files %>'], + tasks: ['jshint', 'mocha'] + }, uglify: { intention: { options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, @@ -47,14 +57,21 @@ module.exports = function (grunt) { } } }); + grunt.registerTask('test', [ + 'mocha' + ]); grunt.registerTask('default', [ 'jshint', - 'uglify' + 'mocha', + 'fixmyjs', + 'uglify', + 'watch' ]); grunt.registerTask('build', [ 'jshint', 'clean', 'fixmyjs', + 'mocha', 'uglify' ]); -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index bd63a72..0797859 100755 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "grunt-release": "~0.5.1", "grunt-fixmyjs": "~0.1.0", "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-jshint": "~0.6.4" + "grunt-contrib-jshint": "~0.6.4", + "grunt-mocha": "~0.4.1", + "grunt-contrib-watch": "~0.5.3" }, "bugs": { "url": "https://github.com/wsjdesign/intentionjs/issues" From 56bfc02f9b579b766b3dd1189ddac30fa017616d Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 06:01:44 -0400 Subject: [PATCH 5/9] Setup tasks for cleaning and keeping a code folder with pristine source/uglify files set to project styles --- Gruntfile.js | 72 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 00a0ce6..f6c40af 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,69 +8,89 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-fixmyjs'); //grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-release'); - grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), release: { options: { npm: false } }, jshint: { - files: [ - 'Gruntfile.js', + source: [ 'intention.js', - 'context.js', - 'test/**/*.js' + 'context.js' + ], + code: [ + 'code/intention.js', + 'code/context.js', + 'Gruntfile.js' ], + test: ['test/**/*'], options: { ignores: [ - 'test/vendor/**/*' + 'test/vendor/**/*', + 'code/*min.js' ], - jshintrc: '.jshintrc', + jshintrc: '.jshintrc' } }, clean: { - intention: ['code/**/*'] + intention: ['code/**/*'], + grunt: ['code/Gruntfile.js'] }, fixmyjs: { options: { - jshintrc: '.jshintrc' - }, - intention: { - files: [ - {expand: true, src: ['*.js'], dest: 'code/', ext: '.js'} - ] + jshintrc: '.jshintrc', + intention: { + files: [{ + expand: true, + src: ['*.js'], + dest: 'code/', + ext: '.js' + }] + } } }, mocha: { - index: ['test/index.html'] + intention: [ + 'test/index.html', + 'test/context-tests.html' + ], + options: { + run: true, + ui: 'bdd' + } }, watch: { - files: ['<%= jshint.files %>'], - tasks: ['jshint', 'mocha'] + files: [ + '<%= jshint.source %>', + 'test/**/*' + ], + tasks: [ + 'jshint:source', + 'mocha' + ] }, uglify: { intention: { options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, - files: { 'code/intention.min.js': ['intention.js'] } + files: { 'code/intention.min.js': ['code/intention.js'] } }, context: { options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* context.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, - files: { 'code/context.min.js': ['context.js'] } + files: { 'code/context.min.js': ['code/context.js'] } } } }); - grunt.registerTask('test', [ - 'mocha' - ]); grunt.registerTask('default', [ - 'jshint', - 'mocha', + 'jshint:source', 'fixmyjs', + 'jshint:code', + 'mocha', 'uglify', - 'watch' + 'clean:grunt' ]); + grunt.registerTask('test', ['mocha']); grunt.registerTask('build', [ - 'jshint', 'clean', 'fixmyjs', + 'clean:grunt', 'mocha', 'uglify' ]); From 4d1522d9dcee8a7db621e5ad0fad41f40aef5d01 Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 10:00:38 -0400 Subject: [PATCH 6/9] Recursive creation fails under phantomJS automated test --- Gruntfile.js | 16 ++++++++++++---- code/context.min.js | 9 --------- code/intention.min.js | 9 --------- context.js | 10 ++++++---- intention.js | 2 +- test/index.html | 4 ++-- test/intention.tests.js | 6 ++++-- 7 files changed, 25 insertions(+), 31 deletions(-) delete mode 100755 code/context.min.js delete mode 100755 code/intention.min.js mode change 100644 => 100755 intention.js diff --git a/Gruntfile.js b/Gruntfile.js index f6c40af..3d9256b 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -21,7 +21,9 @@ module.exports = function (grunt) { 'code/context.js', 'Gruntfile.js' ], - test: ['test/**/*'], + test: [ + 'test/**/*' + ], options: { ignores: [ 'test/vendor/**/*', @@ -40,7 +42,7 @@ module.exports = function (grunt) { intention: { files: [{ expand: true, - src: ['*.js'], + src: '<%= jshint.source %>', dest: 'code/', ext: '.js' }] @@ -80,13 +82,19 @@ module.exports = function (grunt) { }); grunt.registerTask('default', [ 'jshint:source', + 'clean:intention', 'fixmyjs', 'jshint:code', + 'clean:grunt', 'mocha', 'uglify', - 'clean:grunt' ]); - grunt.registerTask('test', ['mocha']); + grunt.registerTask('test', [ + 'fixmyjs', + 'jshint:code', + 'jshint:test', + 'mocha' + ]); grunt.registerTask('build', [ 'clean', 'fixmyjs', diff --git a/code/context.min.js b/code/context.min.js deleted file mode 100755 index 567a067..0000000 --- a/code/context.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! intention.js v0.9.9 -* http://intentionjs.com/ -* -* context.js -* -* Copyright 2008, 2013 -* Dowjones and other contributors. -* Released under the MIT license. -**/ (function(){"use strict";var a=function(a,b){function c(a,b){var c=new Date,d=null;return function(e){var f=new Date;if(b>f-c){d&&window.clearTimeout(d);var g=function(b){return function(){a(b)}};return d=window.setTimeout(g(e),b),!1}a(e),c=f}}var d,e,f=new b;return f.responsive([{name:"base"}]).respond("base"),d=f.responsive({ID:"width",contexts:[{name:"standard",min:840},{name:"tablet",min:510},{name:"mobile",min:0}],matcher:function(a,b){return"string"==typeof a?a===b.name:a>=b.min},measure:function(b){return"string"==typeof b?b:a(window).width()}}),e=f.responsive({ID:"orientation",contexts:[{name:"portrait",rotation:0},{name:"landscape",rotation:90}],matcher:function(a,b){return a===b.rotation},measure:function(){var a=Math.abs(window.orientation);return a>0&&(a=180-a),a}}),f.responsive({ID:"touch",contexts:[{name:"touch"}],matcher:function(){return"ontouchstart"in window}}).respond(),f.responsive({ID:"highres",contexts:[{name:"highres"}],matcher:function(){return window.devicePixelRatio>1}}).respond(),a(window).on("resize",c(d.respond,100)).on("orientationchange",d.respond).on("orientationchange",e.respond),d.respond(),e.respond(),a(function(){f.elements(document)}),f};!function(a,b){"function"==typeof define&&define.amd?define("context",["jquery","intention"],b):a.intent=b(a.jQuery,a.Intention)}(this,function(b,c){return a(b,c)})}).call(this); \ No newline at end of file diff --git a/code/intention.min.js b/code/intention.min.js deleted file mode 100755 index 1755e5d..0000000 --- a/code/intention.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! intention.js v0.9.9 -* http://intentionjs.com/ -* -* intention.js -* -* Copyright 2008, 2013 -* Dowjones and other contributors. -* Released under the MIT license. -**/ !function(a,b){"use strict";"function"==typeof define&&define.amd?define("intention",["jquery","underscore"],b):a.Intention=b(a.jQuery,a._)}(this,function(a,b){"use strict";var c=function(c){var d=b.extend(this,c,{_listeners:{},contexts:[],elms:a(),axes:{},priority:[]});return d};return c.prototype={responsive:function d(a,c){var e,f="abcdefghijklmnopqrstuvwxyz0123456789",g="";for(e=0;5>e;e++)g+=f[Math.floor(Math.random()*f.length)];var h={matcher:function(a,b){return a===b.name},measure:b.identity,ID:g};if(b.isObject(c)===!1&&(c={}),b.isArray(a)&&b.isArray(a[0].contexts))return b.each(a,function(a){d.apply(this,a)},this),void 0;b.isArray(a)===!1&&b.isObject(a)?c=a:c.contexts=a,c=b.extend({},h,c),this.on("_"+c.ID+":",b.bind(function(a){this.axes=this._contextualize(c.ID,a.context,this.axes),this._respond(this.axes,this.elms)},this));var i={ID:c.ID,current:null,contexts:c.contexts,respond:b.bind(this._responder(c.ID,c.contexts,c.matcher,c.measure),this)};return this.axes[c.ID]=i,this.axes.__keys__=this.priority,this.priority.unshift(c.ID),i},elements:function(c){return c||(c=document),a("[data-intent],[intent],[data-in],[in]",c).each(b.bind(function(b,c){this.add(a(c))},this)),this},add:function(c,d){var e;return d||(d={}),c.each(b.bind(function(c,f){var g=!1;this.elms.each(function(a,b){return f===b?(g=!0,!1):!0}),g===!1&&(e=this._fillSpec(b.extend(d,this._attrsToSpec(f.attributes,this.axes))),this._makeChanges(a(f),e,this.axes),this.elms.push({elm:f,spec:e}))},this)),this},remove:function(a){var b=this.elms;return a.each(function(a,c){b.each(function(a,d){return c===d.elm?(b.splice(a,1),!1):!0})}),this},is:function(a){var c=this.axes;return b.some(c.__keys__,function(b){return a===c[b].current})},current:function(a){return this.axes.hasOwnProperty(a)?this.axes[a].current:!1},on:function(a,b){var c=a.split(" "),d=0;for(d;d
+ @@ -16,8 +17,7 @@ - - + diff --git a/test/intention.tests.js b/test/intention.tests.js index 9ee9c59..c1fa819 100755 --- a/test/intention.tests.js +++ b/test/intention.tests.js @@ -13,7 +13,7 @@ describe('Intention', function () { // basic in-* attrs responsiveElm1.attr('in-mobile-class', 'mobile').attr('in-tablet-class', 'tablet').attr('in-touch-class', 'touch').attr('in-standard-class', 'standard'); container.append(nonResponsiveElm, responsiveElm1, $('
')); - describe('add and remove: add and remove responsive elements', function () { + describe('add and remove: add and remove /esponsive elements', function () { var intent = new Intention(); it('Should add three items to intent.elms', function () { expect(intent.elms.length).to.equal(0); @@ -128,16 +128,18 @@ describe('Intention', function () { }); width.respond('tab'); }); + // TODO: Only works in browser not via PhantomJS/Grunt Task describe('recursive responsive axis creation', function () { it('should create three axis by calling intent.responsive once', function () { var intent = new Intention(); intent.responsive([ { contexts: [{ name: 'foo' }] }, { contexts: [{ name: 'bar' }] }, - { contexts: [{ name: 'baz' }] } + { contexts: [{ name: 'baz' }] }, ]); // TODO: make this que off the contexts length expect(intent.axes.__keys__.length).to.equal(3); + expect(intent.primary.length).to.equal(3); }); }); }); From d4b39fe3a5f6d4d88759d1159e738c9767d84041 Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 16:20:33 -0400 Subject: [PATCH 7/9] Add karma.conf.js config file --- karma.conf.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 karma.conf.js diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..20b3850 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,16 @@ +// karma.conf.js +module.exports = function(config) { + config.set({ + frameworks: ['mocha'], + + files: [ + 'code/*.js', + 'test/*.js' + ], + + browsers: process.env.TRAVIS ? ['Firefox'] : ['Chrome'], + + autoWatch: true + }); +}; +/ From 5f6b1db030b865c5d3d5e2dd425866f60380640b Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 18:58:14 -0400 Subject: [PATCH 8/9] Add .npmignore to project --- .npmignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .npmignore diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..3920e51 --- /dev/null +++ b/.npmignore @@ -0,0 +1,7 @@ +// .npmignore +.git* +docs/ +examples/ +support/ +test/ +.DS_Store From 37dcf53e15c821c15a6104267831a939ca6e2232 Mon Sep 17 00:00:00 2001 From: Adam Bedell Date: Tue, 17 Sep 2013 22:31:57 -0400 Subject: [PATCH 9/9] Add automated teOHsting tasks to project. --- .jshintrc | 44 ++--- Gruntfile.js | 91 +++++---- bower.json | 4 +- code/context.js | 144 ++++++++++++++ code/context.min.js | 9 + code/intention.js | 426 ++++++++++++++++++++++++++++++++++++++++ code/intention.min.js | 9 + intention.js | 7 +- karma.conf.js | 14 +- package.json | 21 +- test/index.html | 2 +- test/intention.tests.js | 2 +- 12 files changed, 690 insertions(+), 83 deletions(-) mode change 100755 => 100644 Gruntfile.js create mode 100644 code/context.js create mode 100644 code/context.min.js create mode 100644 code/intention.js create mode 100644 code/intention.min.js mode change 100755 => 100644 intention.js diff --git a/.jshintrc b/.jshintrc index 3548450..aa89d9c 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,13 +1,13 @@ { - "maxerr" : 100, - "bitwise" : false, - "camelcase" : false, - "curly" : true, + "maxerr" : 100, + "bitwise" : false, + "camelcase" : false, + "curly" : true, "eqeqeq" : true, "forin" : true, "immed" : true, - "indent" : 2, + "indent" : 2, "latedef" : true, "newcap" : true, "noarg" : true, @@ -16,9 +16,9 @@ "plusplus" : false, "quotmark" : "single", "undef" : true, - "unused" : true, + "unused" : false, "strict" : true, - "trailing" : false, + "trailing" : true, "maxparams" : false, "maxdepth" : false, "maxstatements" : false, @@ -26,44 +26,44 @@ "maxlen" : false, "asi" : false, "boss" : false, - "debug" : true, - "eqnull" : true, - "esnext" : true, + "debug" : true, + "eqnull" : true, + "esnext" : true, "moz" : false, "evil" : false, "expr" : false, "funcscope" : false, "globalstrict" : false, "iterator" : false, - "lastsemic" : true, - "laxbreak" : true, + "lastsemic" : true, + "laxbreak" : true, "laxcomma" : false, "loopfunc" : false, "multistr" : false, "proto" : false, "scripturl" : false, - "smarttabs" : true, + "smarttabs" : true, "shadow" : false, "sub" : false, "supernew" : false, - "validthis" : false, - "browser" : true, + "validthis" : false, + "browser" : true, "couch" : false, - "devel" : true, + "devel" : true, "dojo" : false, - "jquery" : true, + "jquery" : true, "mootools" : false, - "node" : true, + "node" : true, "nonstandard" : false, "prototypejs" : false, "rhino" : false, "worker" : false, "wsh" : false, "yui" : false, - "nomen" : false, - "onevar" : false, - "passfail" : false, - "white" : true, + "nomen" : false, + "onevar" : false, + "passfail" : false, + "white" : true, "globals" : { "_" : false, "it" : false, diff --git a/Gruntfile.js b/Gruntfile.js old mode 100755 new mode 100644 index 3d9256b..f6644eb --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,28 +1,20 @@ module.exports = function (grunt) { 'use strict'; - grunt.loadNpmTasks('grunt-mocha'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-fixmyjs'); - //grunt.loadNpmTasks('grunt-contrib-requirejs'); - grunt.loadNpmTasks('grunt-release'); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), release: { options: { npm: false } }, jshint: { source: [ 'intention.js', - 'context.js' + 'context.js', + 'Gruntfile.js' ], code: [ 'code/intention.js', 'code/context.js', - 'Gruntfile.js' ], test: [ - 'test/**/*' + 'test/**/*.js' ], options: { ignores: [ @@ -33,22 +25,24 @@ module.exports = function (grunt) { } }, clean: { - intention: ['code/**/*'], - grunt: ['code/Gruntfile.js'] + code: ['<%= jshint.code %>'], + config: ['code/Gruntfile.js', 'code/karma.js'] }, fixmyjs: { - options: { - jshintrc: '.jshintrc', - intention: { - files: [{ - expand: true, - src: '<%= jshint.source %>', - dest: 'code/', - ext: '.js' - }] + build: { + files: [ + {expand: true, cwd: '.', src: ['*.js'], dest: 'code/', ext: '.js'} + ], + options: { + jshintrc: '.jshintrc' } } }, + karma: { + unit: { + configFile: 'karma.conf.js' + } + }, mocha: { intention: [ 'test/index.html', @@ -60,45 +54,66 @@ module.exports = function (grunt) { } }, watch: { - files: [ - '<%= jshint.source %>', - 'test/**/*' - ], - tasks: [ - 'jshint:source', - 'mocha' - ] + karma: { + files: [ + '<%= jshint.source %>', + 'test/**/*' + ], + tasks: [ + 'jshint:source', + 'karma:unit:run' + ] + } }, uglify: { intention: { - options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, + options: { + banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* intention.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' + }, files: { 'code/intention.min.js': ['code/intention.js'] } }, context: { - options: { banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* context.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' }, + options: { + banner: '/*! <%= pkg.name %> v<%= pkg.version %> \n* <%= pkg.homepage %> \n* \n* context.js \n* \n* <%=pkg.copyright %>, <%= grunt.template.today("yyyy") %>\n* <%=pkg.banner %>*/ ' + }, files: { 'code/context.min.js': ['code/context.js'] } } } }); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-mocha'); + grunt.loadNpmTasks('grunt-fixmyjs'); + //grunt.loadNpmTasks('fixmyjs'); + //grunt.loadNpmTasks('karma'); + //grunt.loadNpmTasks('mocha'); + grunt.loadNpmTasks('grunt-karma'); + grunt.loadNpmTasks('grunt-release'); + grunt.registerTask('default', [ 'jshint:source', - 'clean:intention', - 'fixmyjs', + 'clean:code', + 'fixmyjs:build', 'jshint:code', - 'clean:grunt', + 'clean:config', + 'jshint:test', 'mocha', 'uglify', + 'watch' ]); grunt.registerTask('test', [ - 'fixmyjs', + 'jshint:source', + 'fixmyjs:build', 'jshint:code', 'jshint:test', 'mocha' ]); grunt.registerTask('build', [ 'clean', - 'fixmyjs', - 'clean:grunt', + 'fixmyjs:build', + 'clean:config', 'mocha', 'uglify' ]); diff --git a/bower.json b/bower.json index 5452aef..bbf15a4 100644 --- a/bower.json +++ b/bower.json @@ -1,10 +1,10 @@ { "name": "intentionjs", - "version": "0.9.9", + "versin": "0.9.9", "main": "intention.js", "scripts": "dependencies": { - "jquery": "1.9.0", + "jquery": "latest", "underscore.js": "latest" }, "license": "MIT", diff --git a/code/context.js b/code/context.js new file mode 100644 index 0000000..c40c9fd --- /dev/null +++ b/code/context.js @@ -0,0 +1,144 @@ +/*! + * context.js Library associated with > v0.9.6.2 of intention.js + * http://intentionjs.com/ + * + * Copyright 2011, 2013 Dowjones and other contributors + * Released under the MIT license + * + */ +(function () { + 'use strict'; + var context = function ($, Intention) { + // create a brand spankin new intention object. + var intent = new Intention(), + // placeholder for the horizontal axis and orientation axis. + horizontal_axis, orientation_axis; + // throttle function used for keeping calls to the resize responsive + // callback to a minimum. + function throttle(callback, interval) { + var lastExec = new Date(), timer = null; + return function (e) { + var d = new Date(); + if (d - lastExec < interval) { + if (timer) { + window.clearTimeout(timer); + } + var callbackWrapper = function (event) { + return function () { + callback(event); + }; + }; + timer = window.setTimeout(callbackWrapper(e), interval); + return false; + } + callback(e); + lastExec = d; + }; + } + // catchall + // ======================================================================= + intent.responsive([{ name: 'base' }]).respond('base'); + // + // width context? + // ======================================================================= + horizontal_axis = intent.responsive({ + ID: 'width', + contexts: [ + { + name: 'standard', + min: 840 + }, + { + name: 'tablet', + min: 510 + }, + { + name: 'mobile', + min: 0 + } + ], + matcher: function (test, context) { + if (typeof test === 'string') { + return test === context.name; + } + return test >= context.min; + }, + measure: function (arg) { + if (typeof arg === 'string') { + return arg; + } + return $(window).width(); + } + }); + // orientation context? + // ======================================================================= + orientation_axis = intent.responsive({ + ID: 'orientation', + contexts: [ + { + name: 'portrait', + rotation: 0 + }, + { + name: 'landscape', + rotation: 90 + } + ], + matcher: function (measure, ctx) { + return measure === ctx.rotation; + }, + measure: function () { + var test = Math.abs(window.orientation); + if (test > 0) { + test = 180 - test; + } + return test; + } + }); + // ONE TIME CHECK AXES: + // touch device? + // ======================================================================= + intent.responsive({ + ID: 'touch', + contexts: [{ name: 'touch' }], + matcher: function () { + return 'ontouchstart' in window; + } + }).respond(); + // retina display? + // ======================================================================= + intent.responsive({ + ID: 'highres', + contexts: [{ name: 'highres' }], + matcher: function () { + return window.devicePixelRatio > 1; + } + }).respond(); + // bind events to the window + $(window).on('resize', throttle(horizontal_axis.respond, 100)).on('orientationchange', horizontal_axis.respond).on('orientationchange', orientation_axis.respond); + // register the current width and orientation without waiting for a window + // resize + horizontal_axis.respond(); + orientation_axis.respond(); + $(function () { + // at doc ready grab all of the elements in the doc + intent.elements(document); + }); + // return the intention object so that it can be extended by other plugins + return intent; + }; + (function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define('context', [ + 'jquery', + 'intention' + ], factory); + } else { + // Browser globals + root.intent = factory(root.jQuery, root.Intention); + } + }(this, function ($, Intention) { + return context($, Intention); + })); +}.call(this)); \ No newline at end of file diff --git a/code/context.min.js b/code/context.min.js new file mode 100644 index 0000000..567a067 --- /dev/null +++ b/code/context.min.js @@ -0,0 +1,9 @@ +/*! intention.js v0.9.9 +* http://intentionjs.com/ +* +* context.js +* +* Copyright 2008, 2013 +* Dowjones and other contributors. +* Released under the MIT license. +**/ (function(){"use strict";var a=function(a,b){function c(a,b){var c=new Date,d=null;return function(e){var f=new Date;if(b>f-c){d&&window.clearTimeout(d);var g=function(b){return function(){a(b)}};return d=window.setTimeout(g(e),b),!1}a(e),c=f}}var d,e,f=new b;return f.responsive([{name:"base"}]).respond("base"),d=f.responsive({ID:"width",contexts:[{name:"standard",min:840},{name:"tablet",min:510},{name:"mobile",min:0}],matcher:function(a,b){return"string"==typeof a?a===b.name:a>=b.min},measure:function(b){return"string"==typeof b?b:a(window).width()}}),e=f.responsive({ID:"orientation",contexts:[{name:"portrait",rotation:0},{name:"landscape",rotation:90}],matcher:function(a,b){return a===b.rotation},measure:function(){var a=Math.abs(window.orientation);return a>0&&(a=180-a),a}}),f.responsive({ID:"touch",contexts:[{name:"touch"}],matcher:function(){return"ontouchstart"in window}}).respond(),f.responsive({ID:"highres",contexts:[{name:"highres"}],matcher:function(){return window.devicePixelRatio>1}}).respond(),a(window).on("resize",c(d.respond,100)).on("orientationchange",d.respond).on("orientationchange",e.respond),d.respond(),e.respond(),a(function(){f.elements(document)}),f};!function(a,b){"function"==typeof define&&define.amd?define("context",["jquery","intention"],b):a.intent=b(a.jQuery,a.Intention)}(this,function(b,c){return a(b,c)})}).call(this); \ No newline at end of file diff --git a/code/intention.js b/code/intention.js new file mode 100644 index 0000000..4eb7ab8 --- /dev/null +++ b/code/intention.js @@ -0,0 +1,426 @@ +/*! + * intention.js Library v0.9.9 + * http://intentionjs.com/ + * + * Copyright 2011, 2013 Dowjones and other contributors + * Released under the MIT license + * + */ +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define('intention', [ + 'jquery', + 'underscore' + ], factory); + } else { + root.Intention = factory(root.jQuery, root._); + } +}(this, function ($, _) { + 'use strict'; + var Intention = function (params) { + var intent = _.extend(this, params, { + _listeners: {}, + contexts: [], + elms: $(), + axes: {}, + priority: [] + }); + return intent; + }; + Intention.prototype = { + responsive: function responsive(contexts, options) { + /************************************************************** + * + * @public methods + * + **************************************************************/ + // for generating random ids for axis when not specified + var idChars = 'abcdefghijklmnopqrstuvwxyz0123456789', len = idChars.length, id = '', i; + // create a random id for the axis + for (i = 0; i < 5; i++) { + id += idChars[Math.floor(Math.random() * len)]; + } + var defaults = { + matcher: function (measure, ctx) { + return measure === ctx.name; + }, + measure: _.identity, + ID: id + }; + if (_.isObject(options) === false) { + options = {}; + } + if (_.isArray(contexts) && _.isArray(contexts[0].contexts)) { + _.each(contexts, function (axis) { + responsive.apply(this, axis); + }, this); + return; + } + if (_.isArray(contexts) === false && _.isObject(contexts)) { + options = contexts; + } else { + options.contexts = contexts; + } + // fill in the options + options = _.extend({}, defaults, options); + // bind the respond function to the axis ID and prefix it + // with an underscore so that it does not get whomped accidentally + this.on('_' + options.ID + ':', _.bind(function (e) { + this.axes = this._contextualize(options.ID, e.context, this.axes); + this._respond(this.axes, this.elms); + }, this)); + var axis = { + ID: options.ID, + current: null, + contexts: options.contexts, + respond: _.bind(this._responder(options.ID, options.contexts, options.matcher, options.measure), this) + }; + this.axes[options.ID] = axis; + this.axes.__keys__ = this.priority; + this.priority.unshift(options.ID); + return axis; + }, + elements: function (scope) { + // find all responsive elms in a specific dom scope + if (!scope) { + scope = document; + } + $('[data-intent],[intent],[data-in],[in]', scope).each(_.bind(function (i, elm) { + this.add($(elm)); + }, this)); + return this; + }, + add: function (elms, options) { + var spec; + if (!options) { + options = {}; + } + // is expecting a jquery object + elms.each(_.bind(function (i, elm) { + var exists = false; + this.elms.each(function (i, respElm) { + if (elm === respElm) { + exists = true; + return false; + } + return true; + }); + if (exists === false) { + // create the elements responsive data + spec = this._fillSpec(_.extend(options, this._attrsToSpec(elm.attributes, this.axes))); + // make any appropriate changes based on the current contexts + this._makeChanges($(elm), spec, this.axes); + this.elms.push({ + elm: elm, + spec: spec + }); + } + }, this)); + return this; + }, + remove: function (elms) { + // is expecting a jquery object + var respElms = this.elms; + // elms to remove + elms.each(function (i, elm) { + // elms to check against + respElms.each(function (i, candidate) { + if (elm === candidate.elm) { + respElms.splice(i, 1); + // found the match, break the loop + return false; + } + return true; + }); + }); + return this; + }, + is: function (ctxName) { + var axes = this.axes; + return _.some(axes.__keys__, function (key) { + return ctxName === axes[key].current; + }); + }, + current: function (axisName) { + if (this.axes.hasOwnProperty(axisName)) { + return this.axes[axisName].current; + } else { + return false; + } + }, + on: function (type, listener) { + var events = type.split(' '), i = 0; + for (i; i < events.length; i++) { + if (this._listeners[events[i]] === undefined) { + this._listeners[events[i]] = []; + } + this._listeners[events[i]].push(listener); + } + return this; + }, + off: function (type, listener) { + if (_.isArray(this._listeners[type])) { + var listeners = this._listeners[type], i; + for (i = 0; listeners.length; i++) { + if (listeners[i] === listener) { + listeners.splice(i, 1); + break; + } + } + } + return this; + }, + _responder: function (axisID, contexts, matcher, measure) { + var currentContext; + // called to perform a check + return function () { + var measurement = measure.apply(this, arguments); + _.every(contexts, function (ctx) { + if (matcher(measurement, ctx)) { + // first time, or different than last context + if (currentContext === undefined || ctx.name !== currentContext.name) { + currentContext = ctx; + // event emitting! + // emit the private axis event + this._emitter({ + _type: '_' + axisID + ':', + context: currentContext.name + }, currentContext, this)._emitter({ + _type: axisID + ':', + context: currentContext.name + }, currentContext, this)._emitter(_.extend({}, { _type: axisID + ':' + currentContext.name }, currentContext), currentContext, this)._emitter(_.extend({}, { _type: currentContext.name }, currentContext), currentContext, this); + // done, break the loop + return false; + } + // same context, break the loop + return false; + } + return true; + }, this); + // return the intention object for chaining + return this; + }; + }, + _emitter: function (event) { + if (typeof event === 'string') { + event = { _type: event }; + } + if (!event.target) { + event.target = this; + } + if (!event._type) { + throw new Error(event._type + ' is not a supported event.'); + } + if (_.isArray(this._listeners[event._type])) { + var listeners = this._listeners[event._type], i; + for (i = 0; i < listeners.length; i++) { + listeners[i].apply(this, arguments); + } + } + return this; + }, + _fillSpec: function (spec) { + var applySpec = function (fn) { + _.each(spec, function (axisOptions, axis) { + _.each(axisOptions, function (ctxOptions, ctx) { + fn(ctxOptions, ctx, axis); + }); + }); + }, filler = {}; + applySpec(function (options) { + // check to see if the ctx val is an object, could be a string + if (_.isObject(options)) { + _.each(options, function (val, func) { + filler[func] = ''; + }); + } + }); + applySpec(function (options, ctx, axis) { + if (_.isObject(options)) { + spec[axis][ctx] = _.extend({}, filler, options); + } + }); + return spec; + }, + _assocAxis: function (ctx, axes) { + var match = false; + _.every(axes.__keys__, function (axis) { + if (match === false) { + _.every(axes[axis].contexts, function (ctxCandidate) { + if (ctxCandidate.name === ctx) { + match = axis; + return false; + } + return true; + }); + return true; + } else { + return false; + } + }); + return match; + }, + _makeSpec: function (axis, ctx, sAttr, value, spec) { + var axisObj, ctxObj; + if (spec[axis] !== undefined) { + axisObj = spec[axis]; + if (axisObj[ctx] === undefined) { + axisObj[ctx] = {}; + } + } else { + axisObj = {}; + axisObj[ctx] = {}; + spec[axis] = axisObj; + } + axisObj[ctx][sAttr] = value; + return spec; + }, + _attrsToSpec: function (attrs, axes) { + var spec = {}, fullPattern = new RegExp('^(data-)?(in|intent)-(([a-zA-Z0-9][a-zA-Z0-9]*:)?([a-zA-Z0-9]*))-([A-Za-z:-]+)'), axisPattern = new RegExp('^(data-)?(in|intent)-([a-zA-Z0-9][_a-zA-Z0-9]*):$'); + _.each(attrs, function (attr) { + var specMatch = attr.name.match(fullPattern), axisName; + if (specMatch !== null) { + specMatch = specMatch.slice(-3); + axisName = specMatch[0]; + if (specMatch[0] === undefined || specMatch[0] === '') { + // if there is no axis find one: + specMatch[0] = this._assocAxis(specMatch[1], axes); + if (specMatch[0] === false) { + // there is no context, so get outa here + return; // skipt the attr + } + } else { + specMatch[0] = specMatch[0].replace(/:$/, ''); + } + specMatch.push(attr.value); + specMatch.push(spec); + spec = this._makeSpec.apply(this, specMatch); + } else if (axisPattern.test(attr.name)) { + axisName = attr.name.match(axisPattern)[3]; + _.each(axes[axisName].contexts, function (context) { + this._makeSpec(axisName, context.name, 'class', context.name + ' ' + attr.value, spec); + }, this); + } + }, this); + return spec; + }, + _contextSpec: function (ctxObj, specs) { + if (specs.hasOwnProperty(ctxObj.axis) && specs[ctxObj.axis].hasOwnProperty(ctxObj.ctx)) { + return specs[ctxObj.axis][ctxObj.ctx]; + } + return {}; + }, + _resolveSpecs: function (currentContexts, specs) { + var changes = {}, moveFuncs = [ + 'append', + 'prepend', + 'before', + 'after' + ]; + _.each(currentContexts, function (ctxObj) { + // if the axis or the context to not exist in the specs object + // skip to the next one + _.each(this._contextSpec(ctxObj, specs), function (val, func) { + if (func === 'class') { + if (!changes[func]) { + changes[func] = []; + } + changes[func] = _.union(changes[func], val.split(' ')); + } else if ((changes.move === undefined || changes.move.value === '') && $.inArray(func, moveFuncs) !== -1) { + changes.move = { + value: val, + placement: func + }; + } else { + if (changes[func] === undefined || changes[func] === '') { + changes[func] = val; + } + } + }, this); + }, this); + return changes; + }, + _currentContexts: function (axes) { + var contexts = []; + _.each(axes.__keys__, function (ID) { + if (axes[ID].current !== null) { + contexts.push({ + ctx: axes[ID].current, + axis: ID + }); + return; + } + }); + return contexts; + }, + _removeClasses: function (specs, axes) { + var toRemove = []; + _.each(axes.__keys__, function (key) { + var axis = axes[key]; + _.each(axis.contexts, function (ctx) { + // ignore the current context, those classes SHOULD be applied + if (ctx.name === axis.current) { + return; + } + var contextSpec = this._contextSpec({ + axis: axis.ID, + ctx: ctx.name + }, specs), classes; + if (contextSpec !== undefined) { + if (contextSpec['class'] !== undefined) { + classes = contextSpec['class'].split(' '); + if (classes !== undefined) { + toRemove = _.union(toRemove, classes); + } + } + } + }, this); + }, this); + return toRemove; + }, + _contextConfig: function (specs, axes) { + return this._resolveSpecs(this._currentContexts(axes), specs, axes); + }, + _makeChanges: function (elm, specs, axes) { + if (_.isEmpty(axes) === false) { + var ctxConfig = this._contextConfig(specs, axes); + _.each(ctxConfig, function (change, func) { + if (func === 'move') { + if (specs.__placement__ !== change.placement || specs.__move__ !== change.value) { + $(change.value)[change.placement](elm); + // save the last placement of the element so + // we're not moving it around for no good reason + specs.__placement__ = change.placement; + specs.__move__ = change.value; + } + } else if (func === 'class') { + var classes = elm.attr('class') || ''; + // the class add/remove formula + classes = _.union(change, _.difference(classes.split(' '), this._removeClasses(specs, axes))); + elm.attr('class', classes.join(' ')); + } else { + elm.attr(func, change); + } + }, this); + } + return elm; + }, + _respond: function (axes, elms) { + // go through all of the responsive elms + elms.each(_.bind(function (i, elm) { + var $elm = $(elm.elm); + this._makeChanges($elm, elm.spec, axes); + $elm.trigger('intent', this); + }, this)); + }, + _contextualize: function (axisID, context, axes) { + axes[axisID].current = context; + return axes; + }, + _axis_test_pattern: new RegExp('^_[a-zA-Z0-9]'), + _axis_match_pattern: new RegExp('^_([a-zA-Z0-9][_a-zA-Z0-9]*)'), + _trim_pattern: new RegExp('^s+|s+$', 'g') + }; + return Intention; +})); \ No newline at end of file diff --git a/code/intention.min.js b/code/intention.min.js new file mode 100644 index 0000000..efdb774 --- /dev/null +++ b/code/intention.min.js @@ -0,0 +1,9 @@ +/*! intention.js v0.9.9 +* http://intentionjs.com/ +* +* intention.js +* +* Copyright 2008, 2013 +* Dowjones and other contributors. +* Released under the MIT license. +**/ !function(a,b){"use strict";"function"==typeof define&&define.amd?define("intention",["jquery","underscore"],b):a.Intention=b(a.jQuery,a._)}(this,function(a,b){"use strict";var c=function(c){var d=b.extend(this,c,{_listeners:{},contexts:[],elms:a(),axes:{},priority:[]});return d};return c.prototype={responsive:function d(a,c){var e,f="abcdefghijklmnopqrstuvwxyz0123456789",g=f.length,h="";for(e=0;5>e;e++)h+=f[Math.floor(Math.random()*g)];var i={matcher:function(a,b){return a===b.name},measure:b.identity,ID:h};if(b.isObject(c)===!1&&(c={}),b.isArray(a)&&b.isArray(a[0].contexts))return b.each(a,function(a){d.apply(this,a)},this),void 0;b.isArray(a)===!1&&b.isObject(a)?c=a:c.contexts=a,c=b.extend({},i,c),this.on("_"+c.ID+":",b.bind(function(a){this.axes=this._contextualize(c.ID,a.context,this.axes),this._respond(this.axes,this.elms)},this));var j={ID:c.ID,current:null,contexts:c.contexts,respond:b.bind(this._responder(c.ID,c.contexts,c.matcher,c.measure),this)};return this.axes[c.ID]=j,this.axes.__keys__=this.priority,this.priority.unshift(c.ID),j},elements:function(c){return c||(c=document),a("[data-intent],[intent],[data-in],[in]",c).each(b.bind(function(b,c){this.add(a(c))},this)),this},add:function(c,d){var e;return d||(d={}),c.each(b.bind(function(c,f){var g=!1;this.elms.each(function(a,b){return f===b?(g=!0,!1):!0}),g===!1&&(e=this._fillSpec(b.extend(d,this._attrsToSpec(f.attributes,this.axes))),this._makeChanges(a(f),e,this.axes),this.elms.push({elm:f,spec:e}))},this)),this},remove:function(a){var b=this.elms;return a.each(function(a,c){b.each(function(a,d){return c===d.elm?(b.splice(a,1),!1):!0})}),this},is:function(a){var c=this.axes;return b.some(c.__keys__,function(b){return a===c[b].current})},current:function(a){return this.axes.hasOwnProperty(a)?this.axes[a].current:!1},on:function(a,b){var c=a.split(" "),d=0;for(d;d intention tests - + diff --git a/test/intention.tests.js b/test/intention.tests.js index 0716450..8767775 100755 --- a/test/intention.tests.js +++ b/test/intention.tests.js @@ -135,7 +135,7 @@ describe('Intention', function () { intent.responsive([ { contexts: [{ name: 'foo' }] }, { contexts: [{ name: 'bar' }] }, - { contexts: [{ name: 'baz' }] }, + { contexts: [{ name: 'baz' }] } ]); // TODO: make this que off the contexts length expect(intent.axes.__keys__.length).to.equal(3);