├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── bower.json ├── build ├── compile.coffee ├── helpers │ ├── browserify.coffee │ ├── changelogs.coffee │ ├── coffee.coffee │ ├── coffeelint.coffee │ ├── data.coffee │ ├── error.coffee │ ├── header.coffee │ ├── remove-dir.coffee │ ├── rename.coffee │ ├── sass.coffee │ ├── uglify.coffee │ └── umd.coffee ├── publish.coffee ├── templates │ ├── full-header.txt │ ├── mocha-phantomjs-hooks.js │ ├── simple-header.txt │ └── umd.js └── test.coffee ├── coffeelint.json ├── dist ├── simple-module.js └── simple-module.min.js ├── gulpfile.coffee ├── karma.coffee ├── package.json ├── src └── simple-module.coffee └── test └── simple-module.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | npm-debug.log 4 | coverage 5 | test/runner/*.js 6 | .publish 7 | .token 8 | 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | install: 5 | - npm install 6 | after_script: 7 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## V3.0.3 - 2016-06-20 3 | 4 | * CHG: use jQuery 3.0 instead of EventEmitter2 and lodash 5 | * CHG: use phantomjs for browser testing 6 | * CHG: improve bundle technique, which makes it an UMD module since now. 7 | 8 | ## V3.0.2 - 2016-03-08 9 | 10 | * FIX: remove useless return after throwing error 11 | * ADD: coffeelint build task 12 | * ADD: test coverage build task and increase test coverage 13 | 14 | ## V3.0.1 - 2016-03-08 15 | 16 | * FIX: incorrect publish of v3.0.0 on npm 17 | 18 | ## V3.0.0 - 2016-03-08 19 | 20 | * Fresh new start. Now Providing event emitter, mixins and plugins features for browser or node sub-modules. 21 | * Changed: replace jQuery with lodash as dependency. 22 | * Changed: replace grunt with gulp for build and publish automation. 23 | * Changed: use jade to generate doc site instead of jekyll. 24 | * Removed: i18n features, please use dedicated i18n module like [i18next](https://github.com/i18next/i18next) instead. 25 | 26 | ## Older Versions 27 | 28 | Change logs under V3.0.0 are not recorded here, please check [Release History](https://github.com/mycolorway/simple-module/releases). 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mycolorway Design 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Module 2 | 3 | [![Latest Version](https://img.shields.io/npm/v/simple-module.svg)](https://www.npmjs.com/package/simple-module) 4 | [![Build Status](https://img.shields.io/travis/mycolorway/simple-module.svg)](https://travis-ci.org/mycolorway/simple-module) 5 | [![Coveralls](https://img.shields.io/coveralls/mycolorway/simple-module.svg)](https://coveralls.io/github/mycolorway/simple-module) 6 | [![David](https://img.shields.io/david/mycolorway/simple-module.svg)](https://david-dm.org/mycolorway/simple-module) 7 | [![David](https://img.shields.io/david/dev/mycolorway/simple-module.svg)](https://david-dm.org/mycolorway/simple-module#info=devDependencies) 8 | [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/mycolorway/simple-module) 9 | 10 | SimpleModule is a simple base class providing some necessary features to make its subclasses extendable. 11 | 12 | ## Features 13 | 14 | #### Events 15 | 16 | SimpleModule delegate events mothods to jQuery object: 17 | 18 | ```js 19 | let module = new SimpleModule(); 20 | 21 | // bind namespace event 22 | module.on('customEvent.test', function(data) { 23 | console.log(data); 24 | }); 25 | // equivalent to 26 | $(module).on('customEvent.test', function(data) { 27 | console.log(data); 28 | }); 29 | 30 | // trigger a namespace event 31 | module.trigger('customEvent.test', 'test'); 32 | // equivalent to 33 | $(module).trigger('customEvent.test', 'test'); 34 | ``` 35 | 36 | #### Mixins 37 | 38 | Add class properties and methods to SimpleModule: 39 | 40 | ```js 41 | var testMixins = { 42 | classProperty: true, 43 | classMethod: function() {} 44 | }; 45 | 46 | SimpleModule.extend(testMixins); 47 | ``` 48 | 49 | Add instance properties and methods to SimpleModule: 50 | 51 | ```js 52 | var testMixins = { 53 | instanceProperty: true, 54 | instanceMethod: function() {} 55 | }; 56 | 57 | SimpleModule.include(testMixins); 58 | ``` 59 | 60 | #### Plugins 61 | 62 | Register a plugin on SimpleModule: 63 | 64 | ```js 65 | class TestPlugin extends SimpleModule { 66 | constructor(module) { 67 | super() 68 | this.module = module; 69 | this.test = true; 70 | } 71 | } 72 | 73 | SimpleModule.plugin('testPlugin', TestPlugin); 74 | ``` 75 | 76 | Then pass the plugin name to options while creating instance: 77 | 78 | ```js 79 | let module = new SimpleModule({ 80 | plugins: ['testPlugin'] 81 | }); 82 | console.log(module.plugins.testPlugin.test); // true 83 | ``` 84 | 85 | ## Installation 86 | 87 | Install via npm: 88 | 89 | ```bash 90 | npm install --save simple-module 91 | ``` 92 | 93 | Install via bower: 94 | 95 | ```bash 96 | bower install --save simple-module 97 | ``` 98 | 99 | ## Development 100 | 101 | Clone repository from github: 102 | 103 | ```bash 104 | git clone https://github.com/mycolorway/simple-module.git 105 | ``` 106 | 107 | Install npm dependencies: 108 | 109 | ```bash 110 | npm install 111 | ``` 112 | 113 | Run default gulp task to build project, which will compile source files, run test and watch file changes for you: 114 | 115 | ```bash 116 | gulp 117 | ``` 118 | 119 | Now, you are ready to go. 120 | 121 | ## Publish 122 | 123 | If you want to publish new version to npm and bower, please make sure all tests have passed before you publish new version, and you need do these preparations: 124 | 125 | * Add new release information in `CHANGELOG.md`. The format of markdown contents will matter, because build scripts will get version and release content from this file by regular expression. You can follow the format of the older release information. 126 | 127 | * Put your [personal API tokens](https://github.com/blog/1509-personal-api-tokens) in `/.token.json`, which is required by the build scripts to request [Github API](https://developer.github.com/v3/) for creating new release: 128 | 129 | ```json 130 | { 131 | "github": "[your github personal access token]" 132 | } 133 | ``` 134 | 135 | Now you can run `gulp publish` task, which will do these work for you: 136 | 137 | * Get version number from `CHANGELOG.md` and bump it into `package.json` and `bower.json`. 138 | * Get release information from `CHANGELOG.md` and request Github API to create new release. 139 | 140 | If everything goes fine, you can see your release at [https://github.com/mycolorway/simple-module/releases](https://github.com/mycolorway/simple-module/releases). At the End you can publish new version to npm with the command: 141 | 142 | ```bash 143 | npm publish 144 | ``` 145 | 146 | Please be careful with the last step, because you cannot delete or republish a release on npm. 147 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-module", 3 | "version": "3.0.3", 4 | "homepage": "https://github.com/mycolorway/simple-module", 5 | "authors": [ 6 | "farthinker " 7 | ], 8 | "description": "A simple base class for js modules.", 9 | "main": "dist/simple-module.js", 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "test", 14 | "build", 15 | "gulpfile.coffee", 16 | "package.json" 17 | ], 18 | "dependencies": { 19 | "jquery": "^3.0.0" 20 | } 21 | } -------------------------------------------------------------------------------- /build/compile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | _ = require 'lodash' 3 | coffeelint = require './helpers/coffeelint' 4 | browserify = require './helpers/browserify' 5 | sass = require './helpers/sass' 6 | header = require './helpers/header' 7 | rename = require './helpers/rename' 8 | uglify = require './helpers/uglify' 9 | umd = require './helpers/umd' 10 | 11 | compileSass = -> 12 | gulp.src 'src/**/*.scss' 13 | .pipe sass() 14 | .pipe header() 15 | .pipe gulp.dest('dist/') 16 | compileSass.displayName = 'compile-sass' 17 | 18 | compileCoffee = -> 19 | gulp.src 'src/simple-module.coffee' 20 | .pipe coffeelint() 21 | .pipe browserify() 22 | .pipe umd() 23 | .pipe header() 24 | .pipe gulp.dest('dist/') 25 | compileCoffee.displayName = 'compile-coffee' 26 | 27 | compileUglify = -> 28 | gulp.src ['dist/**/*.js', '!dist/**/*.min.js'] 29 | .pipe uglify() 30 | .pipe header('simple') 31 | .pipe rename 32 | suffix: '.min' 33 | .pipe gulp.dest('dist/') 34 | compileUglify.displayName = 'compile-uglify' 35 | 36 | compileAssets = gulp.parallel compileCoffee, compileSass, (done) -> 37 | done() 38 | 39 | compile = gulp.series compileAssets, compileUglify, (done) -> 40 | done() 41 | 42 | gulp.task 'compile', compile 43 | 44 | module.exports = _.extend compile, 45 | sass: compileSass 46 | coffee: compileCoffee 47 | uglify: compileUglify 48 | -------------------------------------------------------------------------------- /build/helpers/browserify.coffee: -------------------------------------------------------------------------------- 1 | gutil = require 'gulp-util' 2 | _ = require 'lodash' 3 | through = require 'through2' 4 | coffee = require 'coffee-script' 5 | browserify = require 'browserify' 6 | handleError = require './error' 7 | 8 | module.exports = (opts) -> 9 | b = browserify _.extend 10 | transform: [coffeeify] 11 | bundleExternal: false 12 | , opts 13 | 14 | through.obj (file, encoding, done) -> 15 | 16 | try 17 | b.require file.path, 18 | expose: file.stem 19 | b.bundle (error, buffer) => 20 | handleError(error, @) if error 21 | file.contents = buffer 22 | file.path = gutil.replaceExtension file.path, '.js' 23 | @push file 24 | done() 25 | catch e 26 | handleError e, @ 27 | done() 28 | 29 | coffeeify = (filename, opts = {}) -> 30 | return through() unless /\.coffee$/.test(filename) 31 | 32 | opts = _.extend 33 | inline: true 34 | bare: true 35 | header: false 36 | , opts 37 | 38 | chunks = [] 39 | through (chunk, encoding, done) -> 40 | chunks.push chunk 41 | done() 42 | , (done) -> 43 | str = Buffer.concat(chunks).toString() 44 | 45 | try 46 | result = coffee.compile str, opts 47 | catch e 48 | handleError e, @ 49 | 50 | @push result 51 | done() 52 | -------------------------------------------------------------------------------- /build/helpers/changelogs.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | 3 | changelogs = fs.readFileSync('CHANGELOG.md').toString() 4 | 5 | lastestVersion = do -> 6 | result = changelogs.match /## V(\d+\.\d+\.\d+)/ 7 | 8 | if result and result.length > 1 9 | result[1] 10 | else 11 | null 12 | 13 | latestContent = do -> 14 | re = new RegExp "## V#{lastestVersion.replace('.', '\\.')}\ 15 | .+\\n\\n((?:\\* .*\\n)+)" 16 | result = changelogs.match re 17 | 18 | if result and result.length > 1 19 | result[1] 20 | else 21 | null 22 | 23 | module.exports = 24 | lastestVersion: lastestVersion 25 | latestContent: latestContent 26 | -------------------------------------------------------------------------------- /build/helpers/coffee.coffee: -------------------------------------------------------------------------------- 1 | gutil = require 'gulp-util' 2 | through = require 'through2' 3 | coffee = require 'coffee-script' 4 | handleError = require './error' 5 | 6 | module.exports = (opts) -> 7 | through.obj (file, encoding, done) -> 8 | str = file.contents.toString() 9 | 10 | try 11 | result = coffee.compile str, opts 12 | catch e 13 | handleError e, @ 14 | 15 | file.contents = new Buffer result 16 | file.path = gutil.replaceExtension file.path, '.js' 17 | @push file 18 | done() 19 | -------------------------------------------------------------------------------- /build/helpers/coffeelint.coffee: -------------------------------------------------------------------------------- 1 | through = require 'through2' 2 | coffeelint = require 'coffeelint' 3 | Reporter = require 'coffeelint/lib/reporters/default' 4 | configFinder = require 'coffeelint/lib/configfinder' 5 | handleError = require './error' 6 | 7 | module.exports = -> 8 | through.obj (file, encoding, done) -> 9 | opts = configFinder.getConfig() 10 | errorReport = coffeelint.getErrorReport() 11 | errorReport.lint file.relative, file.contents.toString(), opts 12 | 13 | summary = errorReport.getSummary() 14 | if summary.errorCount > 0 || summary.warningCount > 0 15 | reporter = new Reporter errorReport 16 | reporter.publish() 17 | handleError 'coffeelint failed with errors or warnings', @ 18 | 19 | @push file 20 | done() 21 | -------------------------------------------------------------------------------- /build/helpers/data.coffee: -------------------------------------------------------------------------------- 1 | through = require 'through2' 2 | _ = require 'lodash' 3 | 4 | module.exports = (data) -> 5 | through.obj (file, encoding, done) -> 6 | file.data = if _.isFunction(data) then data(file) else data 7 | @push file 8 | done() 9 | -------------------------------------------------------------------------------- /build/helpers/error.coffee: -------------------------------------------------------------------------------- 1 | gutil = require 'gulp-util' 2 | 3 | module.exports = (error, stream) -> 4 | if stream 5 | opts = if typeof error == 'string' 6 | {} 7 | else 8 | stack: error.stack 9 | showStack: !!error.stack 10 | 11 | stream.emit 'error', new gutil.PluginError 'gulp-build', error, opts 12 | else 13 | gutil.log gutil.colors.red("gulp-build error: #{error.message || error}") 14 | -------------------------------------------------------------------------------- /build/helpers/header.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | through = require 'through2' 3 | _ = require 'lodash' 4 | pkg = require '../../package' 5 | 6 | module.exports = (type = 'full') -> 7 | now = new Date() 8 | year = now.getFullYear() 9 | month = _.padStart(now.getMonth() + 1, 2, '0') 10 | date = now.getDate() 11 | tpl = fs.readFileSync("build/templates/#{type}-header.txt").toString() 12 | header = _.template(tpl) 13 | name: pkg.name 14 | version: pkg.version 15 | homepage: pkg.homepage 16 | date: "#{year}-#{month}-#{date}" 17 | 18 | through.obj (file, encoding, done) -> 19 | headerBuffer = new Buffer header 20 | file.contents = Buffer.concat [headerBuffer, file.contents] 21 | @push file 22 | done() 23 | -------------------------------------------------------------------------------- /build/helpers/remove-dir.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | 3 | module.exports = removeDir = (dirPath) -> 4 | return unless fs.existsSync dirPath 5 | 6 | fs.readdirSync(dirPath).forEach (file, index) -> 7 | filePath = "#{dirPath}/#{file}" 8 | if fs.lstatSync(filePath).isDirectory() 9 | removeDir filePath 10 | else 11 | fs.unlinkSync filePath 12 | 13 | fs.rmdirSync dirPath 14 | -------------------------------------------------------------------------------- /build/helpers/rename.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'lodash' 2 | through = require 'through2' 3 | path = require 'path' 4 | 5 | module.exports = (opts) -> 6 | opts = _.extend 7 | prefix: '' 8 | suffix: '' 9 | dirname: null 10 | basename: null 11 | extname: null 12 | , opts 13 | 14 | through.obj (file, encoding, done) -> 15 | dirname = path.dirname file.relative 16 | extname = path.extname file.relative 17 | basename = path.basename file.relative, extname 18 | newDirName = if _.isNull(opts.dirname) then dirname else opts.dirname 19 | newExtName = if _.isNull(opts.extname) then extname else opts.extname 20 | newBaseName = if _.isNull(opts.basename) then basename else opts.basename 21 | 22 | filename = "#{opts.prefix}#{newBaseName}#{opts.suffix}#{newExtName}" 23 | file.path = path.join file.base, newDirName, filename 24 | @push file 25 | done() 26 | -------------------------------------------------------------------------------- /build/helpers/sass.coffee: -------------------------------------------------------------------------------- 1 | gutil = require 'gulp-util' 2 | through = require 'through2' 3 | path = require 'path' 4 | sass = require 'node-sass' 5 | _ = require 'lodash' 6 | handleError = require './error' 7 | 8 | module.exports = (opts) -> 9 | through.obj (file, encoding, done) -> 10 | opts = _.extend 11 | data: file.contents.toString() 12 | includePath: [path.dirname(file.path)] 13 | , opts 14 | 15 | try 16 | result = sass.renderSync opts 17 | catch e 18 | handleError e, @ 19 | 20 | file.contents = new Buffer result.css 21 | file.path = gutil.replaceExtension file.path, '.css' 22 | @push file 23 | done() 24 | -------------------------------------------------------------------------------- /build/helpers/uglify.coffee: -------------------------------------------------------------------------------- 1 | through = require 'through2' 2 | uglify = require 'uglify-js' 3 | _ = require 'lodash' 4 | handleError = require './error' 5 | 6 | module.exports = (opts) -> 7 | through.obj (file, encoding, done) -> 8 | opts = _.extend {fromString: true}, opts 9 | 10 | try 11 | result = uglify.minify file.contents.toString(), opts 12 | catch e 13 | handleError e, @ 14 | 15 | file.contents = new Buffer result.code 16 | @push file 17 | done() 18 | -------------------------------------------------------------------------------- /build/helpers/umd.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | through = require 'through2' 3 | _ = require 'lodash' 4 | pkg = require '../../package' 5 | 6 | module.exports = (opts) -> 7 | umdConfig = _.cloneDeep pkg.umd 8 | opts = _.extend umdConfig, opts 9 | 10 | opts.dependencies.cjs = opts.dependencies.cjs.map (name) -> 11 | "require('#{name}')" 12 | .join ',' 13 | 14 | opts.dependencies.global = opts.dependencies.global.map (name) -> 15 | "root.#{name}" 16 | .join ',' 17 | 18 | opts.dependencies.params = opts.dependencies.params.join ',' 19 | 20 | tpl = _.template fs.readFileSync('build/templates/umd.js').toString() 21 | 22 | through.obj (file, encoding, done) -> 23 | opts.contents = file.contents.toString() 24 | opts.filename = file.stem 25 | file.contents = new Buffer tpl opts 26 | @push file 27 | done() 28 | -------------------------------------------------------------------------------- /build/publish.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | gutil = require 'gulp-util' 3 | fs = require 'fs' 4 | request = require 'request' 5 | changelogs = require './helpers/changelogs' 6 | handleError = require './helpers/error' 7 | compile = require './compile' 8 | test = require './test' 9 | _ = require 'lodash' 10 | 11 | createRelease = (done) -> 12 | try 13 | token = _.trim fs.readFileSync('.token').toString() 14 | catch e 15 | throw new Error 'Publish: Need github access token for creating release.' 16 | return 17 | 18 | pkg = require '../package' 19 | content = changelogs.latestContent 20 | unless content 21 | throw new Error('Publish: Invalid release content in CHANGELOG.md') 22 | return 23 | 24 | request 25 | uri: "https://api.github.com/repos/#{pkg.githubOwner}/#{pkg.name}/releases" 26 | method: 'POST' 27 | json: true 28 | body: 29 | tag_name: "v#{pkg.version}", 30 | name: "v#{pkg.version}", 31 | body: content, 32 | draft: false, 33 | prerelease: false 34 | headers: 35 | Authorization: "token #{token}", 36 | 'User-Agent': 'Mycolorway Release' 37 | , (error, response, body) -> 38 | if error 39 | handleError error 40 | else if response.statusCode.toString().search(/2\d\d/) > -1 41 | message = "#{pkg.name} v#{pkg.version} released on github!" 42 | gutil.log gutil.colors.green message 43 | else 44 | message = "#{response.statusCode} #{JSON.stringify response.body}" 45 | handleError gutil.colors.red message 46 | done() 47 | createRelease.displayName = 'create-release' 48 | 49 | publish = gulp.series [ 50 | compile, 51 | test, 52 | createRelease 53 | ]..., (done) -> 54 | done() 55 | 56 | gulp.task 'publish', publish 57 | 58 | module.exports = publish 59 | -------------------------------------------------------------------------------- /build/templates/full-header.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * <%= name %> v<%= version %> 3 | * <%= homepage %> 4 | * 5 | * Copyright Mycolorway Design 6 | * Released under the MIT license 7 | * <%= homepage %>/license.html 8 | * 9 | * Date: <%= date %> 10 | */ 11 | -------------------------------------------------------------------------------- /build/templates/mocha-phantomjs-hooks.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function collectCoverage(page) { 4 | var coverage = page.evaluate(function() { 5 | return window.__coverage__; 6 | }); 7 | 8 | if (!coverage) { 9 | return; 10 | } 11 | 12 | var json = JSON.stringify(coverage); 13 | fs.write('coverage/coverage.json', json); 14 | } 15 | 16 | // beforeStart and afterEnd hooks for mocha-phantomjs 17 | module.exports = { 18 | afterEnd: function(data) { 19 | collectCoverage(data.page); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /build/templates/simple-header.txt: -------------------------------------------------------------------------------- 1 | /* <%= name %> v<%= version %> | (c) Mycolorway Design | MIT License */ 2 | -------------------------------------------------------------------------------- /build/templates/umd.js: -------------------------------------------------------------------------------- 1 | ;(function(root, factory) { 2 | if (typeof module === 'object' && module.exports) { 3 | module.exports = factory(<%= dependencies.cjs %>); 4 | } else { 5 | root.<%= name %> = factory(<%= dependencies.global %>); 6 | } 7 | }(this, function (<%= dependencies.params %>) { 8 | var define, module, exports; 9 | var b = <%= contents %> 10 | return b('<%= filename %>'); 11 | })); 12 | -------------------------------------------------------------------------------- /build/test.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | karma = require 'karma' 3 | fs = require 'fs' 4 | handleError = require './helpers/error' 5 | 6 | test = (done) -> 7 | server = new karma.Server 8 | configFile: "#{process.cwd()}/karma.coffee" 9 | , (code) -> 10 | fs.unlinkSync 'test/coverage-init.js' 11 | if code != 0 12 | handleError "karma exit with code: #{code}" 13 | done() 14 | 15 | server.start() 16 | 17 | gulp.task 'test', test 18 | module.exports = test 19 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrow_spacing": { 3 | "level": "ignore" 4 | }, 5 | "braces_spacing": { 6 | "level": "ignore", 7 | "spaces": 0, 8 | "empty_object_spaces": 0 9 | }, 10 | "camel_case_classes": { 11 | "level": "error" 12 | }, 13 | "coffeescript_error": { 14 | "level": "error" 15 | }, 16 | "colon_assignment_spacing": { 17 | "level": "ignore", 18 | "spacing": { 19 | "left": 0, 20 | "right": 0 21 | } 22 | }, 23 | "cyclomatic_complexity": { 24 | "value": 10, 25 | "level": "ignore" 26 | }, 27 | "duplicate_key": { 28 | "level": "error" 29 | }, 30 | "empty_constructor_needs_parens": { 31 | "level": "ignore" 32 | }, 33 | "ensure_comprehensions": { 34 | "level": "warn" 35 | }, 36 | "eol_last": { 37 | "level": "ignore" 38 | }, 39 | "indentation": { 40 | "value": 2, 41 | "level": "error" 42 | }, 43 | "line_endings": { 44 | "level": "ignore", 45 | "value": "unix" 46 | }, 47 | "max_line_length": { 48 | "value": 80, 49 | "level": "error", 50 | "limitComments": true 51 | }, 52 | "missing_fat_arrows": { 53 | "level": "ignore", 54 | "is_strict": false 55 | }, 56 | "newlines_after_classes": { 57 | "value": 3, 58 | "level": "ignore" 59 | }, 60 | "no_backticks": { 61 | "level": "error" 62 | }, 63 | "no_debugger": { 64 | "level": "warn", 65 | "console": false 66 | }, 67 | "no_empty_functions": { 68 | "level": "ignore" 69 | }, 70 | "no_empty_param_list": { 71 | "level": "ignore" 72 | }, 73 | "no_implicit_braces": { 74 | "level": "ignore", 75 | "strict": true 76 | }, 77 | "no_implicit_parens": { 78 | "strict": true, 79 | "level": "ignore" 80 | }, 81 | "no_interpolation_in_single_quotes": { 82 | "level": "ignore" 83 | }, 84 | "no_plusplus": { 85 | "level": "ignore" 86 | }, 87 | "no_stand_alone_at": { 88 | "level": "ignore" 89 | }, 90 | "no_tabs": { 91 | "level": "error" 92 | }, 93 | "no_this": { 94 | "level": "ignore" 95 | }, 96 | "no_throwing_strings": { 97 | "level": "error" 98 | }, 99 | "no_trailing_semicolons": { 100 | "level": "error" 101 | }, 102 | "no_trailing_whitespace": { 103 | "level": "error", 104 | "allowed_in_comments": false, 105 | "allowed_in_empty_lines": true 106 | }, 107 | "no_unnecessary_double_quotes": { 108 | "level": "ignore" 109 | }, 110 | "no_unnecessary_fat_arrows": { 111 | "level": "warn" 112 | }, 113 | "non_empty_constructor_needs_parens": { 114 | "level": "ignore" 115 | }, 116 | "prefer_english_operator": { 117 | "level": "ignore", 118 | "doubleNotLevel": "ignore" 119 | }, 120 | "space_operators": { 121 | "level": "ignore" 122 | }, 123 | "spacing_after_comma": { 124 | "level": "ignore" 125 | }, 126 | "transform_messes_up_line_numbers": { 127 | "level": "warn" 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /dist/simple-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * simple-module v3.0.3 3 | * http://mycolorway.github.io/simple-module 4 | * 5 | * Copyright Mycolorway Design 6 | * Released under the MIT license 7 | * http://mycolorway.github.io/simple-module/license.html 8 | * 9 | * Date: 2016-07-29 10 | */ 11 | ;(function(root, factory) { 12 | if (typeof module === 'object' && module.exports) { 13 | module.exports = factory(require('jquery')); 14 | } else { 15 | root.SimpleModule = factory(root.jQuery); 16 | } 17 | }(this, function ($) { 18 | var define, module, exports; 19 | var b = require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 8 | gulp.src 'build/**/*.coffee' 9 | .pipe coffeelint() 10 | 11 | gulp.task 'default', gulp.series lint, compile, test, (done) -> 12 | gulp.watch 'build/**/*.coffee', lint 13 | 14 | gulp.watch 'src/**/*.coffee', gulp.series compile.coffee, test 15 | gulp.watch 'src/**/*.scss', compile.sass 16 | gulp.watch 'test/**/*.coffee', test 17 | 18 | done() 19 | -------------------------------------------------------------------------------- /karma.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (config) -> 2 | config.set 3 | 4 | # base path that will be used to resolve all patterns (eg. files, exclude) 5 | basePath: '' 6 | 7 | 8 | # frameworks to use 9 | # available frameworks: https://npmjs.org/browse/keyword/karma-adapter 10 | frameworks: ['coffee-coverage', 'browserify', 'mocha', 'chai'] 11 | 12 | 13 | # list of files / patterns to load in the browser 14 | files: [ 15 | 'node_modules/jquery/dist/jquery.js' 16 | 'test/coverage-init.js' 17 | 'src/simple-module.coffee' 18 | 'test/simple-module.coffee' 19 | ] 20 | 21 | 22 | # list of files to exclude 23 | exclude: [ 24 | ] 25 | 26 | 27 | # preprocess matching files before serving them to the browser 28 | # available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 29 | preprocessors: 30 | 'src/simple-module.coffee': ['browserify'] 31 | 'test/simple-module.coffee': ['browserify'] 32 | 33 | 34 | browserify: 35 | transform: [['browserify-coffee-coverage', {noInit: true, instrumentor: 'istanbul'}]] 36 | extensions: ['.js', '.coffee'] 37 | 38 | 39 | coffeeCoverage: 40 | framework: 41 | initAllSources: true 42 | sourcesBasePath: 'src' 43 | dest: 'test/coverage-init.js' 44 | instrumentor: 'istanbul' 45 | 46 | 47 | coverageReporter: 48 | dir: 'coverage' 49 | subdir: '.' 50 | reporters: [ 51 | { type: 'lcovonly' } 52 | { type: 'text-summary' } 53 | ] 54 | 55 | 56 | # test results reporter to use 57 | # possible values: 'dots', 'progress' 58 | # available reporters: https://npmjs.org/browse/keyword/karma-reporter 59 | reporters: ['coverage', 'mocha'] 60 | 61 | 62 | # web server port 63 | port: 9876 64 | 65 | 66 | # enable / disable colors in the output (reporters and logs) 67 | colors: true 68 | 69 | 70 | # level of logging 71 | # possible values: 72 | # - config.LOG_DISABLE 73 | # - config.LOG_ERROR 74 | # - config.LOG_WARN 75 | # - config.LOG_INFO 76 | # - config.LOG_DEBUG 77 | logLevel: config.LOG_INFO 78 | 79 | 80 | # enable / disable watching file and executing tests whenever any file changes 81 | autoWatch: false 82 | 83 | 84 | # start these browsers 85 | # available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 86 | browsers: ['PhantomJS'] 87 | 88 | 89 | # Continuous Integration mode 90 | # if true, Karma captures browsers, runs the tests and exits 91 | singleRun: true 92 | 93 | # Concurrency level 94 | # how many browser should be started simultaneous 95 | concurrency: Infinity 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-module", 3 | "githubOwner": "mycolorway", 4 | "version": "3.0.3", 5 | "description": "A base class for javascript modules.", 6 | "homepage": "http://mycolorway.github.io/simple-module", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/mycolorway/simple-module.git" 10 | }, 11 | "author": "farthinker", 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/mycolorway/simple-module/issues" 15 | }, 16 | "main": "dist/simple-module.js", 17 | "scripts": { 18 | "start": "gulp", 19 | "test": "gulp test" 20 | }, 21 | "umd": { 22 | "name": "SimpleModule", 23 | "dependencies": { 24 | "cjs": [ 25 | "jquery" 26 | ], 27 | "global": [ 28 | "jQuery" 29 | ], 30 | "params": [ 31 | "$" 32 | ] 33 | } 34 | }, 35 | "dependencies": { 36 | "jquery": "^3.0.0" 37 | }, 38 | "devDependencies": { 39 | "browserify": "^13.0.0", 40 | "browserify-coffee-coverage": "^1.1.1", 41 | "chai": "^3.5.0", 42 | "coffee-script": "^1.10.0", 43 | "coffeelint": "^1.15.0", 44 | "coveralls": "^2.11.8", 45 | "gulp": "github:gulpjs/gulp#4.0", 46 | "gulp-util": "^3.0.7", 47 | "istanbul": "^0.4.2", 48 | "karma": "^1.1.1", 49 | "karma-browserify": "^5.0.5", 50 | "karma-chai": "^0.1.0", 51 | "karma-coffee-coverage": "^1.1.2", 52 | "karma-coverage": "^1.0.0", 53 | "karma-mocha": "^1.0.1", 54 | "karma-mocha-reporter": "^2.0.4", 55 | "karma-phantomjs-launcher": "^1.0.0", 56 | "lodash": "^4.13.1", 57 | "mocha": "^2.5.3", 58 | "node-sass": "^3.8.0", 59 | "phantomjs-prebuilt": "^2.1.7", 60 | "through2": "^2.0.1", 61 | "uglify-js": "^2.6.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/simple-module.coffee: -------------------------------------------------------------------------------- 1 | class SimpleModule 2 | 3 | # Add properties to {SimpleModule} class. 4 | # 5 | # @param [Object] obj The properties of {obj} will be copied to {SimpleModule} 6 | # , except property named `extended`, which is a function 7 | # that will be called after copy operation. 8 | @extend: (obj) -> 9 | unless obj and typeof obj == 'object' 10 | throw new Error('SimpleModule.extend: param should be an object') 11 | 12 | for key, val of obj when key not in ['included', 'extended'] 13 | @[key] = val 14 | 15 | obj.extended?.call(@) 16 | @ 17 | 18 | # Add properties to instance of {SimpleModule} class. 19 | # 20 | # @param [Hash] obj The properties of {obj} will be copied to prototype of 21 | # {SimpleModule}, except property named `included`, which is 22 | # a function that will be called after copy operation. 23 | @include: (obj) -> 24 | unless obj and typeof obj == 'object' 25 | throw new Error('SimpleModule.include: param should be an object') 26 | 27 | for key, val of obj when key not in ['included', 'extended'] 28 | @::[key] = val 29 | 30 | obj.included?.call(@) 31 | @ 32 | 33 | # @property [Hash] The registered plugins. 34 | @plugins: {} 35 | 36 | # Register plugin for {SimpleModule} 37 | # 38 | # @param [String] name The name of plugin. 39 | # @param [Function] cls The class of plugin. 40 | # 41 | @plugin: (name, cls) -> 42 | unless name and typeof name == 'string' 43 | throw new Error 'SimpleModule.plugin: first param should be a string' 44 | 45 | unless typeof cls == 'function' 46 | throw new Error 'SimpleModule.plugin: second param should be a class' 47 | 48 | @plugins[name] = cls 49 | @ 50 | 51 | @opts: 52 | plugins: [] 53 | 54 | plugins: {} 55 | 56 | # Create a new instance of {SimpleModule} 57 | # 58 | # @param [Hash] opts The options for initialization. 59 | # 60 | # @return The new instance. 61 | constructor: (opts) -> 62 | @opts = $.extend {}, SimpleModule.opts, opts 63 | 64 | @opts.plugins.forEach (name) => 65 | @plugins[name] = new SimpleModule.plugins[name](@) 66 | 67 | on: (args...) -> 68 | $(@).on args... 69 | 70 | off: (args...) -> 71 | $(@).off args... 72 | 73 | trigger: (args...) -> 74 | $(@).triggerHandler(args...) 75 | 76 | one: (args...) -> 77 | $(@).one args... 78 | 79 | module.exports = SimpleModule 80 | -------------------------------------------------------------------------------- /test/simple-module.coffee: -------------------------------------------------------------------------------- 1 | SimpleModule = require '../src/simple-module' 2 | expect = chai.expect 3 | 4 | describe 'SimpleModule', -> 5 | 6 | class TestPlugin extends SimpleModule 7 | constructor: (@module) -> 8 | super() 9 | this.test = true 10 | start: -> 11 | this.started = true 12 | 13 | it 'should support custom events', -> 14 | module = new SimpleModule() 15 | callCount = 0 16 | listener = -> 17 | callCount += 1 18 | 19 | module.on 'customEvent', listener 20 | module.trigger 'customEvent' 21 | expect(callCount).to.be.equal(1) 22 | module.off 'customEvent' 23 | module.trigger 'customEvent' 24 | expect(callCount).to.be.equal(1) 25 | 26 | module.on 'customEvent', listener 27 | module.trigger 'customEvent' 28 | expect(callCount).to.be.equal(2) 29 | module.off 'customEvent', listener 30 | module.trigger 'customEvent' 31 | expect(callCount).to.be.equal(2) 32 | 33 | module.one 'customEvent', listener 34 | module.trigger 'customEvent' 35 | module.trigger 'customEvent' 36 | expect(callCount).to.be.equal(3) 37 | 38 | it 'can extend properties', -> 39 | extendWithWrongArgs = -> 40 | SimpleModule.extend 'test' 41 | expect(extendWithWrongArgs).to.throw(/param should be an object/) 42 | 43 | SimpleModule.extend 44 | classProperty: true 45 | expect(SimpleModule.classProperty).to.be.equal(true) 46 | 47 | it 'can include prototype properties', -> 48 | includeWithWrongArgs = -> 49 | SimpleModule.include 'test' 50 | expect(includeWithWrongArgs).to.throw(/param should be an object/) 51 | 52 | SimpleModule.include 53 | prototypeProperty: true 54 | module = new SimpleModule() 55 | expect(SimpleModule.prototype.prototypeProperty).to.be.equal(true) 56 | expect(module.prototypeProperty).to.be.equal(true) 57 | 58 | it 'can register plugin', -> 59 | registerWithoutName = -> 60 | SimpleModule.plugin() 61 | expect(registerWithoutName).to.throw(/first param should be a string/) 62 | expect(SimpleModule.plugins.testPlugin).to.be.undefined 63 | 64 | registerWithoutClass = -> 65 | SimpleModule.plugin('testPlugin') 66 | expect(registerWithoutClass).to.throw(/second param should be a class/) 67 | expect(SimpleModule.plugins.testPlugin).to.be.undefined 68 | 69 | SimpleModule.plugin 'testPlugin', TestPlugin 70 | expect(SimpleModule.plugins.testPlugin).to.be.equal(TestPlugin) 71 | 72 | it 'can create instance with plugin', -> 73 | SimpleModule.plugin 'testPlugin', TestPlugin 74 | module = new SimpleModule 75 | plugins: ['testPlugin'] 76 | 77 | expect(module.plugins.testPlugin instanceof TestPlugin).to.be.equal(true) 78 | expect(module.plugins.testPlugin.test).to.be.equal(true) 79 | 80 | module.plugins.testPlugin.start() 81 | expect(module.plugins.testPlugin.started).to.be.equal(true) 82 | 83 | it 'should let subclasses override default options', -> 84 | class ModuleA extends SimpleModule 85 | @opts: 86 | name: 'A' 87 | constructor: (opts) -> 88 | super() 89 | $.extend @opts, ModuleA.opts, opts 90 | 91 | class ModuleB extends ModuleA 92 | @opts: 93 | name: 'B' 94 | constructor: (opts) -> 95 | super() 96 | $.extend @opts, ModuleB.opts, opts 97 | 98 | moduleB = new ModuleB() 99 | moduleA = new ModuleA() 100 | module = new SimpleModule() 101 | moduleX = new ModuleB 102 | name: 'X' 103 | 104 | expect(moduleB.opts.name).to.be.equal('B') 105 | expect(moduleA.opts.name).to.be.equal('A') 106 | expect(module.opts.name).to.be.undefined 107 | expect(moduleX.opts.name).to.be.equal('X') 108 | --------------------------------------------------------------------------------