diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..3e3f0a2 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,19 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "newcap": true, + "noarg": true, + "quotmark": false, + "undef": true, + "unused": "vars", + "strict": false, + "trailing": true, + "smarttabs": true +} diff --git a/Gulpfile.js b/Gulpfile.js new file mode 100644 index 0000000..0d6861f --- /dev/null +++ b/Gulpfile.js @@ -0,0 +1,159 @@ +'use strict'; + +var gulp = require('gulp'), + $ = require('gulp-load-plugins')(), + browserify = require('browserify'), + source = require('vinyl-source-stream'), + buffer = require('vinyl-buffer'), + util = require('gulp-util'), + runSequence = require('run-sequence').use(gulp), + del = require('del'), + fs = require('fs'), + semver = require('semver'), + argv = require('yargs').argv; + +var config = { + source: ['./src/editableCell.js'], + target: './out', + externals: ['knockout', 'jquery'], + increment: argv.inc || 'patch', + dryRun: argv.dryrun || false +}; + +gulp.task('default', function(done) { + return runSequence('clean', 'bundle', 'lint', 'test', done); +}); + +gulp.task('clean', function(done) { + del(config.target, { + force: true + }, done); +}); + +gulp.task('lint', function() { + return gulp.src(['./src/**/*.+(js)', './Gulpfile.js']) + .pipe($.jshint('.jshintrc')) + .pipe($.jshint.reporter('jshint-stylish')) + .pipe($.jshint.reporter('fail')); +}); + +gulp.task('bundle', function() { + browserify(config.source, { + debug: false, + standalone: 'editableCell', + silent: true + }) + .external(config.externals) + .on('log', function(msg) { + util.log(util.colors.yellow('[Browserify]', msg)); + }) + .on('error', function(err) { + util.log(util.colors.red('[Browserify]', err)); + }) + .bundle() + .pipe(source('editableCell.js')) + .pipe($.replace('define(e);', 'define([\'knockout\'],e);')) + .pipe(buffer()) + .pipe(gulp.dest(config.target)) + .pipe($.sourcemaps.init({ + loadMaps: true + })) + .pipe($.uglify()) + .pipe($.rename({ + suffix: '.min' + })) + .pipe($.sourcemaps.write('.')) + .pipe(gulp.dest(config.target)); +}); + +gulp.task('_bundle_tests', function(done) { + browserify('./test/index.js', { + silent: true, + baseDir: './' + }) + .on('error', function(err) { + util.log(util.colors.red('[Browserify]', err)); + }) + .bundle() + .pipe(source('tests.js')) + .pipe(buffer()) + .pipe(gulp.dest(config.target)); + + done(); +}); + +gulp.task('_test', function(done) { + var stream = $.mochaPhantomjs(); + stream.on('phantomjsExit', function() { + done(); + }); + + return gulp.src(['./test/*.html']) + .pipe(stream) + .on('error', function(err) { + util.log(util.colors.red('[Err]', err)); + this.emit('end'); + }); +}); + +gulp.task('test', function(done) { + return runSequence('_bundle_tests', ['_test'], done); +}); + +gulp.task('bump', function() { + var pkg = getPackageJson(), + newVer = semver.inc(pkg.version, config.increment); + + return gulp.src('./package.json') + .pipe($.bump({ version: newVer })) + .pipe(gulp.dest('./')) + .pipe($.if(!config.dryRun, $.git.commit('Bumped version to v' + newVer))); +}); + +gulp.task('_add_output_folder', function() { + return gulp.src(['./out/*']) + .pipe($.git.add({ args: '-f'})); +}); + +gulp.task('_publish', function(done) { + /* + git add -f out/* + git checkout head + git commit -m "Version {version} for distribution" + git tag -a v{version} -m "Add tag v{verson}" + git checkout master + git push origin --tags + */ + var ver = getPackageJson().version, + opt = { quiet: true }; + + $.git.checkout('head', function(err){ + if (err) { throw err; } + + $.git.commit('Version ' + ver + ' for distribution', opt); + + $.git.tag('v' + ver, 'Add tag v' + ver, opt, function(err) { + if (err) { throw err; } + + $.git.reset('master', { args: '--hard', quiet: true }, function(err) { + if (err) { throw err; } + + if (!config.dryRun) { + $.git.push('origin', 'master', { args: '--tags', quiet: true }, function(err){ + if (err) { throw err; } + done(); + }); + } + }); + }); + + }); +}); + +gulp.task('release', function(done) { + runSequence('default', 'bump', '_add_output_folder', '_publish', done); +}); + +function getPackageJson() { + return JSON.parse(fs.readFileSync('./package.json', 'utf8')); +} diff --git a/make.js b/make.js deleted file mode 100644 index 8877675..0000000 --- a/make.js +++ /dev/null @@ -1,74 +0,0 @@ -require('shelljs/make'); -require('shelljs/global'); - -// Targets - -target.all = function () { - target.lint(); - target.bundle(); - target.test(); - target.minify(); -}; - -target.lint = function () { - console.log('Linting...'); - run('jshint src', {silent: true}); -}; - -target.bundle = function () { - console.log('Bundling...'); - run('browserify --external knockout -s editableCell src/editableCell.js', {silent: true}) - .output - // Add 'knockout' as a dependency for AMD scenario - .replace('define(e);', 'define([\'knockout\'],e);') - .to('out/editableCell.js'); -}; - -target.minify = function () { - console.log('Minifying...'); - run('uglifyjs out/editableCell.js', {silent: true}).output.to('out/editableCell.min.js'); -}; - -target['test-bundle'] = function () { - console.log('Bundling tests...'); - - [ - "require('should');" - ] - .concat(find('./test') - .filter(function(file) { return file.match(/\.js$/); }) - .map(function (file) { return "require('./" + file + "');" }) - ) - .join('\n') - .to('_tests.js'); - - run('browserify _tests.js', {silent: true}).output.to('out/tests.js'); - rm('_tests.js'); -}; - -target.test = function () { - target['test-bundle'](); - - console.log('Running tests...'); - run('mocha-phantomjs test/runner.html'); -}; - -// Helper functions -var path = require('path'); - -function less (file) { - return run('lessc -x ' + file, {silent: true}).output.replace(/\n/g, ''); -} - -function run (command, options) { - var result = exec(path.join('node_modules/.bin/') + command, options); - - if (result.code !== 0) { - if (!options || options.silent) { - console.error(result.output); - } - exit(1); - } - - return result; -} diff --git a/out/.gitignore b/out/.gitignore deleted file mode 100644 index a6c7c28..0000000 --- a/out/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/package.json b/package.json index 0564b14..8acdcc4 100644 --- a/package.json +++ b/package.json @@ -7,19 +7,40 @@ "url": "https://github.com/gnab/editableCell.git" }, "devDependencies": { - "browserify": "^6.3.3", + "browserify": "^9.0.3", + "del": "^1.1.1", + "events": "^1.0.2", + "gulp": "^3.8.11", + "gulp-bump": "^0.2.1", + "gulp-concat": "^2.5.2", + "gulp-derequire": "^2.0.0", + "gulp-git": "^1.0.0", + "gulp-if": "^1.2.5", + "gulp-jshint": "^1.9.2", + "gulp-load-plugins": "^0.8.0", + "gulp-mocha-phantomjs": "^0.5.3", + "gulp-rename": "^1.2.0", + "gulp-replace": "^0.5.3", + "gulp-sourcemaps": "^1.3.0", + "gulp-uglify": "^1.1.0", + "gulp-util": "^3.0.4", "inherits": "^2.0.1", "jquery": "^2.1.1", "jshint": "^2.5.10", + "jshint-stylish": "^1.0.1", "knockout": "^3.2.0", "mocha": "^2.0.1", "mocha-phantomjs": "^3.5.1", "phantomjs": "^1.9.12", - "shelljs": "^0.3.0", + "run-sequence": "^1.0.2", + "semver": "^4.3.1", "should": "^4.3.0", - "uglify-js": "^2.4.15" + "uglify-js": "^2.4.15", + "vinyl-buffer": "^1.0.0", + "vinyl-source-stream": "^1.0.0", + "yargs": "^3.3.1" }, "scripts": { - "test": "node make test" + "test": "gulp bundle test" } } diff --git a/src/editableCell.js b/src/editableCell.js index 9ff1dd5..e340987 100644 --- a/src/editableCell.js +++ b/src/editableCell.js @@ -1,5 +1,5 @@ "option strict"; -var koBindingHandlers = require('./ko'), +var koBindingHandlers = require('./ko'), //jshint ignore:line events = require('./events'); exports.selectCell = function (cell) { diff --git a/src/ko/editableCellBinding.js b/src/ko/editableCellBinding.js index 90f6273..1be7a69 100644 --- a/src/ko/editableCellBinding.js +++ b/src/ko/editableCellBinding.js @@ -1,3 +1,5 @@ +/* global $ */ + "option strict"; var utils = require('./utils'), events = require('../events'), diff --git a/src/ko/index.js b/src/ko/index.js index 1bb61e3..31d1907 100644 --- a/src/ko/index.js +++ b/src/ko/index.js @@ -1,5 +1,5 @@ "option strict"; -var polyfill = require('../polyfill'); +var polyfill = require('../polyfill'); // jshint ignore:line var ko = require('./wrapper'); // Knockout binding handlers diff --git a/src/ko/wrapper.js b/src/ko/wrapper.js index 1e3abb6..0b34f6c 100644 --- a/src/ko/wrapper.js +++ b/src/ko/wrapper.js @@ -1,3 +1,4 @@ +/* global ko */ if (typeof ko !== 'undefined') { module.exports = ko; } diff --git a/src/selection.js b/src/selection.js index 083f54b..9fd322b 100644 --- a/src/selection.js +++ b/src/selection.js @@ -2,7 +2,7 @@ var SelectionView = require('./selectionView'), SelectionRange = require('./selectionRange'), EventEmitter = require('events').EventEmitter, - polyfill = require('./polyfill'), + polyfill = require('./polyfill'), // jshint ignore: line events = require('./events'), ko = require('./ko/wrapper'), inherits = require('inherits'); @@ -160,8 +160,8 @@ Selection.prototype.endEditingCell = function(cell) { }; Selection.prototype.getRowByIndex = function(index, originTable) { - if (isNaN(index)) return null; - + if (isNaN(index)) { return null; } + var targetTable = originTable || this.table; // Check if we're moving out of table diff --git a/src/selectionRange.js b/src/selectionRange.js index c19c8e6..990a148 100644 --- a/src/selectionRange.js +++ b/src/selectionRange.js @@ -1,6 +1,6 @@ "option strict"; var EventEmitter = require('events').EventEmitter, - polyfill = require('./polyfill'), + polyfill = require('./polyfill'), // jshint ignore:line inherits = require('inherits'); module.exports = SelectionRange; @@ -126,8 +126,7 @@ SelectionRange.prototype.getCellInDirection = function (originCell, direction) { }; SelectionRange.prototype.getSelectableCellInDirection = function (originCell, direction) { - var lastCell, - cell = originCell; + var cell = originCell; while (cell) { cell = this.getCellInDirection(cell, direction); @@ -141,7 +140,9 @@ SelectionRange.prototype.getSelectableCellInDirection = function (originCell, di }; SelectionRange.prototype.getLastSelectableCellInDirection = function (originCell, direction) { - var nextCell = originCell; + var nextCell = originCell, + cell = nextCell; + do { cell = nextCell; nextCell = this.getSelectableCellInDirection(cell, direction); diff --git a/src/selectionView.js b/src/selectionView.js index ba7263d..28fbee2 100644 --- a/src/selectionView.js +++ b/src/selectionView.js @@ -1,5 +1,5 @@ "option strict"; -var polyfill = require('./polyfill'); +var polyfill = require('./polyfill'); // jshint ignore: line module.exports = SelectionView; @@ -159,14 +159,6 @@ SelectionView.prototype.show = function () { } }; -function resolve (value) { - if (typeof value === 'function') { - return value(); - } - - return value; -} - SelectionView.prototype.hide = function () { this.element.style.display = 'none'; }; diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..5af0545 --- /dev/null +++ b/test/index.js @@ -0,0 +1,9 @@ +"use strict"; + +var tests = [ + require('should'), + require('./ko/editableCellBindingTest'), + require('./ko/editableCellSelectionBindingTest') +]; + +module.exports = tests; diff --git a/test/ko/editableCellBindingTest.js b/test/ko/editableCellBindingTest.js index 5e1b52f..072b79c 100644 --- a/test/ko/editableCellBindingTest.js +++ b/test/ko/editableCellBindingTest.js @@ -1,5 +1,9 @@ +/* globals describe, it, ko */ +"use strict"; + var editableCell = require('../../src/editableCell'); var utils = require('../utils'); +require('should'); describe('editableCell binding', function () { it('should be registered with Knockout', function () { @@ -72,4 +76,4 @@ describe('editableCell binding', function () { observable().should.equal('updated value'); }); }); -}); \ No newline at end of file +}); diff --git a/test/ko/editableCellSelectionBindingTest.js b/test/ko/editableCellSelectionBindingTest.js index ded5b77..bfd1792 100644 --- a/test/ko/editableCellSelectionBindingTest.js +++ b/test/ko/editableCellSelectionBindingTest.js @@ -1,5 +1,9 @@ +/* globals describe, it, ko */ +"use strict"; + var editableCell = require('../../src/editableCell'); var utils = require('../utils'); +require('should'); describe('editableCellSelection binding', function () { it('should be registered with Knockout', function () { @@ -55,7 +59,7 @@ describe('editableCellSelection binding', function () { var aCell = utils.createCell("editableCell: 'a'"); var row = aCell.parentNode; var table = row.parentNode.parentNode; - + var bCell = utils.addCell(row, "editableCell: 'b'"); var cCell = utils.addCell(row, "editableCell: 'c'"); bCell.style.display = 'none'; @@ -79,4 +83,4 @@ describe('editableCellSelection binding', function () { }]); }); }); -}); \ No newline at end of file +});