diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..69fad35 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e717f5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..5a33f9f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,13 @@ +{ + "extends": "eslint:recommended", + "plugins": ["angular"], + "env": { + "browser": true, + "jasmine": true + }, + "globals": { + "angular": true, + "module": true, + "inject": true + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01b919d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +bower_components/ +coverage/ +.sass-cache/ +.idea/ +.tmp/ +dist/ diff --git a/.yo-rc.json b/.yo-rc.json new file mode 100644 index 0000000..7da5de1 --- /dev/null +++ b/.yo-rc.json @@ -0,0 +1,78 @@ +{ + "generator-gulp-angular": { + "version": "1.0.2", + "props": { + "angularVersion": "~1.4.2", + "angularModules": [ + { + "key": "animate", + "module": "ngAnimate" + }, + { + "key": "cookies", + "module": "ngCookies" + }, + { + "key": "touch", + "module": "ngTouch" + }, + { + "key": "sanitize", + "module": "ngSanitize" + }, + { + "key": "messages", + "module": "ngMessages" + }, + { + "key": "aria", + "module": "ngAria" + } + ], + "jQuery": { + "key": "jqLite" + }, + "resource": { + "key": "angular-resource", + "module": "ngResource" + }, + "router": { + "key": "angular-route", + "module": "ngRoute" + }, + "ui": { + "key": "bootstrap", + "module": null + }, + "bootstrapComponents": { + "key": "ui-bootstrap", + "module": "ui.bootstrap" + }, + "cssPreprocessor": { + "key": "node-sass", + "extension": "scss" + }, + "jsPreprocessor": { + "key": "noJsPrepro", + "extension": "js", + "srcExtension": "js" + }, + "htmlPreprocessor": { + "key": "noHtmlPrepro", + "extension": "html" + }, + "foundationComponents": { + "name": null, + "version": null, + "key": null, + "module": null + }, + "paths": { + "src": "src", + "dist": "dist", + "e2e": "e2e", + "tmp": ".tmp" + } + } + } +} \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..e8ad744 --- /dev/null +++ b/bower.json @@ -0,0 +1,39 @@ +{ + "name": "angular", + "version": "0.0.0", + "dependencies": { + "angular-animate": "~1.4.2", + "angular-cookies": "~1.4.2", + "angular-touch": "~1.4.2", + "angular-sanitize": "~1.4.2", + "angular-messages": "~1.4.2", + "angular-aria": "~1.4.2", + "angular-resource": "~1.4.2", + "angular-route": "~1.4.2", + "bootstrap-sass": "~3.3.5", + "angular-bootstrap": "~0.13.4", + "malarkey": "yuanqing/malarkey#~1.3.1", + "angular-toastr": "~1.5.0", + "moment": "~2.10.6", + "animate.css": "~3.4.0", + "angular": "~1.4.2" + }, + "devDependencies": { + "angular-mocks": "~1.4.2" + }, + "overrides": { + "bootstrap-sass": { + "main": [ + "assets/stylesheets/_bootstrap.scss", + "assets/fonts/bootstrap/glyphicons-halflings-regular.eot", + "assets/fonts/bootstrap/glyphicons-halflings-regular.svg", + "assets/fonts/bootstrap/glyphicons-halflings-regular.ttf", + "assets/fonts/bootstrap/glyphicons-halflings-regular.woff", + "assets/fonts/bootstrap/glyphicons-halflings-regular.woff2" + ] + } + }, + "resolutions": { + "angular": "~1.4.2" + } +} diff --git a/e2e/.eslintrc b/e2e/.eslintrc new file mode 100644 index 0000000..d2c6f97 --- /dev/null +++ b/e2e/.eslintrc @@ -0,0 +1,9 @@ +{ + "globals": { + "browser": false, + "element": false, + "by": false, + "$": false, + "$$": false + } +} diff --git a/e2e/main.po.js b/e2e/main.po.js new file mode 100644 index 0000000..0f0428c --- /dev/null +++ b/e2e/main.po.js @@ -0,0 +1,15 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var MainPage = function() { + this.jumbEl = element(by.css('.jumbotron')); + this.h1El = this.jumbEl.element(by.css('h1')); + this.imgEl = this.jumbEl.element(by.css('img')); + this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in main.awesomeThings')); +}; + +module.exports = new MainPage(); diff --git a/e2e/main.spec.js b/e2e/main.spec.js new file mode 100644 index 0000000..ef2e5c1 --- /dev/null +++ b/e2e/main.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('The main view', function () { + var page; + + beforeEach(function () { + browser.get('/index.html'); + page = require('./main.po'); + }); + + it('should include jumbotron with correct data', function() { + expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); + expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); + expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); + }); + + it('should list more than 5 awesome things', function () { + expect(page.thumbnailEls.count()).toBeGreaterThan(5); + }); + +}); diff --git a/gulp/.eslintrc b/gulp/.eslintrc new file mode 100644 index 0000000..10d2238 --- /dev/null +++ b/gulp/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "node": true + } +} diff --git a/gulp/build.js b/gulp/build.js new file mode 100644 index 0000000..2769817 --- /dev/null +++ b/gulp/build.js @@ -0,0 +1,98 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')({ + pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] +}); + +gulp.task('partials', function () { + return gulp.src([ + path.join(conf.paths.src, '/app/**/*.html'), + path.join(conf.paths.tmp, '/serve/app/**/*.html') + ]) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true + })) + .pipe($.angularTemplatecache('templateCacheHtml.js', { + module: 'angular', + root: 'app' + })) + .pipe(gulp.dest(conf.paths.tmp + '/partials/')); +}); + +gulp.task('html', ['inject', 'partials'], function () { + var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); + var partialsInjectOptions = { + starttag: '', + ignorePath: path.join(conf.paths.tmp, '/partials'), + addRootSlash: false + }; + + var htmlFilter = $.filter('*.html', { restore: true }); + var jsFilter = $.filter('**/*.js', { restore: true }); + var cssFilter = $.filter('**/*.css', { restore: true }); + var assets; + + return gulp.src(path.join(conf.paths.tmp, '/serve/*.html')) + .pipe($.inject(partialsInjectFile, partialsInjectOptions)) + .pipe(assets = $.useref.assets()) + .pipe($.rev()) + .pipe(jsFilter) + .pipe($.sourcemaps.init()) + .pipe($.ngAnnotate()) + .pipe($.uglify({ preserveComments: $.uglifySaveLicense })).on('error', conf.errorHandler('Uglify')) + .pipe($.sourcemaps.write('maps')) + .pipe(jsFilter.restore) + .pipe(cssFilter) + .pipe($.sourcemaps.init()) + .pipe($.replace('../../bower_components/bootstrap-sass/assets/fonts/bootstrap/', '../fonts/')) + .pipe($.minifyCss({ processImport: false })) + .pipe($.sourcemaps.write('maps')) + .pipe(cssFilter.restore) + .pipe(assets.restore()) + .pipe($.useref()) + .pipe($.revReplace()) + .pipe(htmlFilter) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true, + conditionals: true + })) + .pipe(htmlFilter.restore) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))) + .pipe($.size({ title: path.join(conf.paths.dist, '/'), showFiles: true })); + }); + +// Only applies for fonts from bower dependencies +// Custom fonts are handled by the "other" task +gulp.task('fonts', function () { + return gulp.src($.mainBowerFiles()) + .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) + .pipe($.flatten()) + .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); +}); + +gulp.task('other', function () { + var fileFilter = $.filter(function (file) { + return file.stat.isFile(); + }); + + return gulp.src([ + path.join(conf.paths.src, '/**/*'), + path.join('!' + conf.paths.src, '/**/*.{html,css,js,scss}') + ]) + .pipe(fileFilter) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))); +}); + +gulp.task('clean', function () { + return $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')]); +}); + +gulp.task('build', ['html', 'fonts', 'other']); diff --git a/gulp/conf.js b/gulp/conf.js new file mode 100644 index 0000000..fcec9c2 --- /dev/null +++ b/gulp/conf.js @@ -0,0 +1,41 @@ +/** + * This file contains the variables used in other gulp files + * which defines tasks + * By design, we only put there very generic config values + * which are used in several places to keep good readability + * of the tasks + */ + +var gutil = require('gulp-util'); + +/** + * The main paths of your project handle these with care + */ +exports.paths = { + src: 'src', + dist: 'dist', + tmp: '.tmp', + e2e: 'e2e' +}; + +/** + * Wiredep is the lib which inject bower dependencies in your project + * Mainly used to inject script tags in the index.html but also used + * to inject css preprocessor deps and js files in karma + */ +exports.wiredep = { + exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/], + directory: 'bower_components' +}; + +/** + * Common implementation for an error handler of a Gulp plugin + */ +exports.errorHandler = function(title) { + 'use strict'; + + return function(err) { + gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); + this.emit('end'); + }; +}; diff --git a/gulp/e2e-tests.js b/gulp/e2e-tests.js new file mode 100644 index 0000000..3a66702 --- /dev/null +++ b/gulp/e2e-tests.js @@ -0,0 +1,38 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +// Downloads the selenium webdriver +gulp.task('webdriver-update', $.protractor.webdriver_update); + +gulp.task('webdriver-standalone', $.protractor.webdriver_standalone); + +function runProtractor (done) { + var params = process.argv; + var args = params.length > 3 ? [params[3], params[4]] : []; + + gulp.src(path.join(conf.paths.e2e, '/**/*.js')) + .pipe($.protractor.protractor({ + configFile: 'protractor.conf.js', + args: args + })) + .on('error', function (err) { + // Make sure failed tests cause gulp to exit non-zero + throw err; + }) + .on('end', function () { + // Close browser sync server + browserSync.exit(); + done(); + }); +} + +gulp.task('protractor', ['protractor:src']); +gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor); +gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor); diff --git a/gulp/inject.js b/gulp/inject.js new file mode 100644 index 0000000..2a5804b --- /dev/null +++ b/gulp/inject.js @@ -0,0 +1,42 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +var browserSync = require('browser-sync'); + +gulp.task('inject-reload', ['inject'], function() { + browserSync.reload(); +}); + +gulp.task('inject', ['scripts', 'styles'], function () { + var injectStyles = gulp.src([ + path.join(conf.paths.tmp, '/serve/app/**/*.css'), + path.join('!' + conf.paths.tmp, '/serve/app/vendor.css') + ], { read: false }); + + var injectScripts = gulp.src([ + path.join(conf.paths.src, '/app/**/*.module.js'), + path.join(conf.paths.src, '/app/**/*.js'), + path.join('!' + conf.paths.src, '/app/**/*.spec.js'), + path.join('!' + conf.paths.src, '/app/**/*.mock.js'), + ]) + .pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort')); + + var injectOptions = { + ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')], + addRootSlash: false + }; + + return gulp.src(path.join(conf.paths.src, '/*.html')) + .pipe($.inject(injectStyles, injectOptions)) + .pipe($.inject(injectScripts, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve'))); +}); diff --git a/gulp/scripts.js b/gulp/scripts.js new file mode 100644 index 0000000..585e4fc --- /dev/null +++ b/gulp/scripts.js @@ -0,0 +1,26 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + + +gulp.task('scripts-reload', function() { + return buildScripts() + .pipe(browserSync.stream()); +}); + +gulp.task('scripts', function() { + return buildScripts(); +}); + +function buildScripts() { + return gulp.src(path.join(conf.paths.src, '/app/**/*.js')) + .pipe($.eslint()) + .pipe($.eslint.format()) + .pipe($.size()) +}; diff --git a/gulp/server.js b/gulp/server.js new file mode 100644 index 0000000..500b0c0 --- /dev/null +++ b/gulp/server.js @@ -0,0 +1,63 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); +var browserSyncSpa = require('browser-sync-spa'); + +var util = require('util'); + +var proxyMiddleware = require('http-proxy-middleware'); + +function browserSyncInit(baseDir, browser) { + browser = browser === undefined ? 'default' : browser; + + var routes = null; + if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) { + routes = { + '/bower_components': 'bower_components' + }; + } + + var server = { + baseDir: baseDir, + routes: routes + }; + + /* + * You can add a proxy to your backend by uncommenting the line below. + * You just have to configure a context which will we redirected and the target url. + * Example: $http.get('/users') requests will be automatically proxified. + * + * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.9.0/README.md + */ + // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', changeOrigin: true}); + + browserSync.instance = browserSync.init({ + startPath: '/', + server: server, + browser: browser + }); +} + +browserSync.use(browserSyncSpa({ + selector: '[ng-app]'// Only needed for angular apps +})); + +gulp.task('serve', ['watch'], function () { + browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]); +}); + +gulp.task('serve:dist', ['build'], function () { + browserSyncInit(conf.paths.dist); +}); + +gulp.task('serve:e2e', ['inject'], function () { + browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []); +}); + +gulp.task('serve:e2e-dist', ['build'], function () { + browserSyncInit(conf.paths.dist, []); +}); diff --git a/gulp/styles.js b/gulp/styles.js new file mode 100644 index 0000000..9eada3f --- /dev/null +++ b/gulp/styles.js @@ -0,0 +1,54 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +gulp.task('styles-reload', ['styles'], function() { + return buildStyles() + .pipe(browserSync.stream()); +}); + +gulp.task('styles', function() { + return buildStyles(); +}); + +var buildStyles = function() { + var sassOptions = { + style: 'expanded' + }; + + var injectFiles = gulp.src([ + path.join(conf.paths.src, '/app/**/*.scss'), + path.join('!' + conf.paths.src, '/app/index.scss') + ], { read: false }); + + var injectOptions = { + transform: function(filePath) { + filePath = filePath.replace(conf.paths.src + '/app/', ''); + return '@import "' + filePath + '";'; + }, + starttag: '// injector', + endtag: '// endinjector', + addRootSlash: false + }; + + + return gulp.src([ + path.join(conf.paths.src, '/app/index.scss') + ]) + .pipe($.inject(injectFiles, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe($.sourcemaps.init()) + .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) + .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) + .pipe($.sourcemaps.write()) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))); +}; diff --git a/gulp/unit-tests.js b/gulp/unit-tests.js new file mode 100644 index 0000000..576887a --- /dev/null +++ b/gulp/unit-tests.js @@ -0,0 +1,52 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var karma = require('karma'); + +var pathSrcHtml = [ + path.join(conf.paths.src, '/**/*.html') +]; + +var pathSrcJs = [ + path.join(conf.paths.src, '/**/!(*.spec).js') +]; + +function runTests (singleRun, done) { + var reporters = ['progress']; + var preprocessors = {}; + + pathSrcHtml.forEach(function(path) { + preprocessors[path] = ['ng-html2js']; + }); + + if (singleRun) { + pathSrcJs.forEach(function(path) { + preprocessors[path] = ['coverage']; + }); + reporters.push('coverage') + } + + var localConfig = { + configFile: path.join(__dirname, '/../karma.conf.js'), + singleRun: singleRun, + autoWatch: !singleRun, + reporters: reporters, + preprocessors: preprocessors + }; + + var server = new karma.Server(localConfig, function(failCount) { + done(failCount ? new Error("Failed " + failCount + " tests.") : null); + }) + server.start(); +} + +gulp.task('test', ['scripts'], function(done) { + runTests(true, done); +}); + +gulp.task('test:auto', ['watch'], function(done) { + runTests(false, done); +}); diff --git a/gulp/watch.js b/gulp/watch.js new file mode 100644 index 0000000..f748ccb --- /dev/null +++ b/gulp/watch.js @@ -0,0 +1,39 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +function isOnlyChange(event) { + return event.type === 'changed'; +} + +gulp.task('watch', ['inject'], function () { + + gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject-reload']); + + gulp.watch([ + path.join(conf.paths.src, '/app/**/*.css'), + path.join(conf.paths.src, '/app/**/*.scss') + ], function(event) { + if(isOnlyChange(event)) { + gulp.start('styles-reload'); + } else { + gulp.start('inject-reload'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) { + if(isOnlyChange(event)) { + gulp.start('scripts-reload'); + } else { + gulp.start('inject-reload'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) { + browserSync.reload(event.path); + }); +}); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..5669b5d --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,29 @@ +/** + * Welcome to your gulpfile! + * The gulp tasks are splitted in several files in the gulp directory + * because putting all here was really too long + */ + +'use strict'; + +var gulp = require('gulp'); +var wrench = require('wrench'); + +/** + * This will load all js or coffee files in the gulp directory + * in order to load all gulp tasks + */ +wrench.readdirSyncRecursive('./gulp').filter(function(file) { + return (/\.(js|coffee)$/i).test(file); +}).map(function(file) { + require('./gulp/' + file); +}); + + +/** + * Default task clean temporaries directories and launch the + * main optimization build task + */ +gulp.task('default', ['clean'], function () { + gulp.start('build'); +}); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..8e472cd --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,110 @@ +'use strict'; + +var path = require('path'); +var conf = require('./gulp/conf'); + +var _ = require('lodash'); +var wiredep = require('wiredep'); + +var pathSrcHtml = [ + path.join(conf.paths.src, '/**/*.html') +]; + +function listFiles() { + var wiredepOptions = _.extend({}, conf.wiredep, { + dependencies: true, + devDependencies: true + }); + + var patterns = wiredep(wiredepOptions).js + .concat([ + path.join(conf.paths.src, '/app/**/*.module.js'), + path.join(conf.paths.src, '/app/**/*.js'), + path.join(conf.paths.src, '/**/*.spec.js'), + path.join(conf.paths.src, '/**/*.mock.js'), + ]) + .concat(pathSrcHtml); + + var files = patterns.map(function(pattern) { + return { + pattern: pattern + }; + }); + files.push({ + pattern: path.join(conf.paths.src, '/assets/**/*'), + included: false, + served: true, + watched: false + }); + return files; +} + +module.exports = function(config) { + + var configuration = { + files: listFiles(), + + singleRun: true, + + autoWatch: false, + + ngHtml2JsPreprocessor: { + stripPrefix: conf.paths.src + '/', + moduleName: 'angular' + }, + + logLevel: 'WARN', + + frameworks: ['jasmine', 'angular-filesort'], + + angularFilesort: { + whitelist: [path.join(conf.paths.src, '/**/!(*.html|*.spec|*.mock).js')] + }, + + browsers : ['PhantomJS'], + + plugins : [ + 'karma-phantomjs-launcher', + 'karma-angular-filesort', + 'karma-coverage', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor' + ], + + coverageReporter: { + type : 'html', + dir : 'coverage/' + }, + + reporters: ['progress'], + + proxies: { + '/assets/': path.join('/base/', conf.paths.src, '/assets/') + } + }; + + // This is the default preprocessors configuration for a usage with Karma cli + // The coverage preprocessor is added in gulp/unit-test.js only for single tests + // It was not possible to do it there because karma doesn't let us now if we are + // running a single test or not + configuration.preprocessors = {}; + pathSrcHtml.forEach(function(path) { + configuration.preprocessors[path] = ['ng-html2js']; + }); + + // This block is needed to execute Chrome on Travis + // If you ever plan to use Chrome and Travis, you can keep it + // If not, you can safely remove it + // https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076 + if(configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) { + configuration.customLaunchers = { + 'chrome-travis-ci': { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }; + configuration.browsers = ['chrome-travis-ci']; + } + + config.set(configuration); +}; diff --git a/layouts/angular-layout.html.erb b/layouts/angular-layout.html.erb new file mode 120000 index 0000000..ea3bc70 --- /dev/null +++ b/layouts/angular-layout.html.erb @@ -0,0 +1 @@ +../dist/index.html \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..714a5e1 --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "angular", + "version": "0.0.0", + "dependencies": {}, + "scripts": { + "test": "gulp test" + }, + "devDependencies": { + "estraverse": "~4.1.0", + "gulp": "~3.9.0", + "gulp-autoprefixer": "~3.0.2", + "gulp-angular-templatecache": "~1.8.0", + "del": "~2.0.2", + "lodash": "~3.10.1", + "gulp-minify-css": "~1.2.1", + "gulp-filter": "~3.0.1", + "gulp-flatten": "~0.2.0", + "gulp-eslint": "~1.0.0", + "eslint-plugin-angular": "~0.12.0", + "gulp-load-plugins": "~0.10.0", + "gulp-size": "~2.0.0", + "gulp-uglify": "~1.4.1", + "gulp-useref": "~1.3.0", + "gulp-util": "~3.0.6", + "gulp-ng-annotate": "~1.1.0", + "gulp-replace": "~0.5.4", + "gulp-rename": "~1.2.2", + "gulp-rev": "~6.0.1", + "gulp-rev-replace": "~0.4.2", + "gulp-minify-html": "~1.0.4", + "gulp-inject": "~3.0.0", + "gulp-protractor": "~1.0.0", + "gulp-sourcemaps": "~1.6.0", + "gulp-sass": "~2.0.4", + "gulp-angular-filesort": "~1.1.1", + "main-bower-files": "~2.9.0", + "wiredep": "~2.2.2", + "karma": "~0.13.10", + "karma-jasmine": "~0.3.6", + "karma-phantomjs-launcher": "~0.2.1", + "phantomjs": "~1.9.18", + "karma-angular-filesort": "~1.0.0", + "karma-coverage": "~0.5.2", + "karma-ng-html2js-preprocessor": "~0.2.0", + "browser-sync": "~2.9.11", + "browser-sync-spa": "~1.0.3", + "http-proxy-middleware": "~0.9.0", + "chalk": "~1.1.1", + "uglify-save-license": "~0.4.1", + "wrench": "~1.5.8" + }, + "engines": { + "node": ">=0.10.0" + } +} diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100644 index 0000000..862d068 --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,27 @@ +'use strict'; + +var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; + +// An example configuration file. +exports.config = { + // The address of a running selenium server. + //seleniumAddress: 'http://localhost:4444/wd/hub', + //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json + + // Capabilities to be passed to the webdriver instance. + capabilities: { + 'browserName': 'chrome' + }, + + baseUrl: 'http://localhost:3000', + + // Spec patterns are relative to the current working directory when + // protractor is called. + specs: [paths.e2e + '/**/*.js'], + + // Options to be passed to Jasmine-node. + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000 + } +}; diff --git a/src/app/components/githubContributor/githubContributor.service.js b/src/app/components/githubContributor/githubContributor.service.js new file mode 100644 index 0000000..9e10661 --- /dev/null +++ b/src/app/components/githubContributor/githubContributor.service.js @@ -0,0 +1,37 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .factory('githubContributor', githubContributor); + + /** @ngInject */ + function githubContributor($log, $http) { + var apiHost = 'https://api.github.com/repos/Swiip/generator-gulp-angular'; + + var service = { + apiHost: apiHost, + getContributors: getContributors + }; + + return service; + + function getContributors(limit) { + if (!limit) { + limit = 30; + } + + return $http.get(apiHost + '/contributors?per_page=' + limit) + .then(getContributorsComplete) + .catch(getContributorsFailed); + + function getContributorsComplete(response) { + return response.data; + } + + function getContributorsFailed(error) { + $log.error('XHR Failed for getContributors.\n' + angular.toJson(error.data, true)); + } + } + } +})(); diff --git a/src/app/components/githubContributor/githubContributor.service.spec.js b/src/app/components/githubContributor/githubContributor.service.spec.js new file mode 100644 index 0000000..6f874d0 --- /dev/null +++ b/src/app/components/githubContributor/githubContributor.service.spec.js @@ -0,0 +1,62 @@ +(function() { + 'use strict'; + + describe('service githubContributor', function() { + var githubContributor; + var $httpBackend; + var $log; + + beforeEach(module('angular')); + beforeEach(inject(function(_githubContributor_, _$httpBackend_, _$log_) { + githubContributor = _githubContributor_; + $httpBackend = _$httpBackend_; + $log = _$log_; + })); + + it('should be registered', function() { + expect(githubContributor).not.toEqual(null); + }); + + describe('apiHost variable', function() { + it('should exist', function() { + expect(githubContributor.apiHost).not.toEqual(null); + }); + }); + + describe('getContributors function', function() { + it('should exist', function() { + expect(githubContributor.getContributors).not.toEqual(null); + }); + + it('should return data', function() { + $httpBackend.when('GET', githubContributor.apiHost + '/contributors?per_page=1').respond(200, [{pprt: 'value'}]); + var data; + githubContributor.getContributors(1).then(function(fetchedData) { + data = fetchedData; + }); + $httpBackend.flush(); + expect(data).toEqual(jasmine.any(Array)); + expect(data.length === 1).toBeTruthy(); + expect(data[0]).toEqual(jasmine.any(Object)); + }); + + it('should define a limit per page as default value', function() { + $httpBackend.when('GET', githubContributor.apiHost + '/contributors?per_page=30').respond(200, new Array(30)); + var data; + githubContributor.getContributors().then(function(fetchedData) { + data = fetchedData; + }); + $httpBackend.flush(); + expect(data).toEqual(jasmine.any(Array)); + expect(data.length === 30).toBeTruthy(); + }); + + it('should log a error', function() { + $httpBackend.when('GET', githubContributor.apiHost + '/contributors?per_page=1').respond(500); + githubContributor.getContributors(1); + $httpBackend.flush(); + expect($log.error.logs).toEqual(jasmine.stringMatching('XHR Failed for')); + }); + }); + }); +})(); diff --git a/src/app/components/malarkey/malarkey.directive.js b/src/app/components/malarkey/malarkey.directive.js new file mode 100644 index 0000000..977262f --- /dev/null +++ b/src/app/components/malarkey/malarkey.directive.js @@ -0,0 +1,75 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .directive('acmeMalarkey', acmeMalarkey); + + /** @ngInject */ + function acmeMalarkey(malarkey) { + var directive = { + restrict: 'E', + scope: { + extraValues: '=' + }, + template: ' ', + link: linkFunc, + controller: MalarkeyController, + controllerAs: 'vm' + }; + + return directive; + + function linkFunc(scope, el, attr, vm) { + var watcher; + var typist = malarkey(el[0], { + typeSpeed: 40, + deleteSpeed: 40, + pauseDelay: 800, + loop: true, + postfix: ' ' + }); + + el.addClass('acme-malarkey'); + + angular.forEach(scope.extraValues, function(value) { + typist.type(value).pause().delete(); + }); + + watcher = scope.$watch('vm.contributors', function() { + angular.forEach(vm.contributors, function(contributor) { + typist.type(contributor.login).pause().delete(); + }); + }); + + scope.$on('$destroy', function () { + watcher(); + }); + } + + /** @ngInject */ + function MalarkeyController($log, githubContributor) { + var vm = this; + + vm.contributors = []; + + activate(); + + function activate() { + return getContributors().then(function() { + $log.info('Activated Contributors View'); + }); + } + + function getContributors() { + return githubContributor.getContributors(10).then(function(data) { + vm.contributors = data; + + return vm.contributors; + }); + } + } + + } + +})(); diff --git a/src/app/components/malarkey/malarkey.directive.spec.js b/src/app/components/malarkey/malarkey.directive.spec.js new file mode 100644 index 0000000..3be3309 --- /dev/null +++ b/src/app/components/malarkey/malarkey.directive.spec.js @@ -0,0 +1,45 @@ +(function() { + 'use strict'; + + /** + * @todo Complete the test + * This example is not perfect. + * The `link` function is not tested. + * (malarkey usage, addClass, $watch, $destroy) + */ + describe('directive malarkey', function() { + var $log; + var vm; + var el; + + beforeEach(module('angular')); + beforeEach(inject(function($compile, $rootScope, githubContributor, $q, _$log_) { + $log = _$log_; + + spyOn(githubContributor, 'getContributors').and.callFake(function() { + return $q.when([{}, {}, {}, {}, {}, {}]); + }); + + el = angular.element(''); + + $compile(el)($rootScope.$new()); + $rootScope.$digest(); + vm = el.isolateScope().vm; + })); + + it('should be compiled', function() { + expect(el.html()).not.toEqual(null); + }); + + it('should have isolate scope object with instanciate members', function() { + expect(vm).toEqual(jasmine.any(Object)); + + expect(vm.contributors).toEqual(jasmine.any(Array)); + expect(vm.contributors.length).toEqual(6); + }); + + it('should log a info', function() { + expect($log.info.logs).toEqual(jasmine.stringMatching('Activated Contributors View')); + }); + }); +})(); diff --git a/src/app/components/malarkey/malarkey.scss b/src/app/components/malarkey/malarkey.scss new file mode 100644 index 0000000..62ccac1 --- /dev/null +++ b/src/app/components/malarkey/malarkey.scss @@ -0,0 +1,25 @@ +.acme-malarkey { + text-transform: capitalize; + color: #cb3837; + + &:after { + animation: cursor-blink 0.4s linear infinite; + content: "|"; + color: #cb3837; + } +} + +@keyframes cursor-blink { + 1% { + opacity: 0; + } + 40% { + opacity: 0; + } + 60% { + opacity: 1; + } + 100% { + opacity: 1; + } +} diff --git a/src/app/components/navbar/navbar.directive.js b/src/app/components/navbar/navbar.directive.js new file mode 100644 index 0000000..b5eb267 --- /dev/null +++ b/src/app/components/navbar/navbar.directive.js @@ -0,0 +1,32 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .directive('acmeNavbar', acmeNavbar); + + /** @ngInject */ + function acmeNavbar() { + var directive = { + restrict: 'E', + templateUrl: 'app/components/navbar/navbar.html', + scope: { + creationDate: '=' + }, + controller: NavbarController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + + /** @ngInject */ + function NavbarController(moment) { + var vm = this; + + // "vm.creation" is avaible by directive option "bindToController: true" + vm.relativeDate = moment(vm.creationDate).fromNow(); + } + } + +})(); diff --git a/src/app/components/navbar/navbar.directive.spec.js b/src/app/components/navbar/navbar.directive.spec.js new file mode 100644 index 0000000..5df48d1 --- /dev/null +++ b/src/app/components/navbar/navbar.directive.spec.js @@ -0,0 +1,50 @@ +(function() { + 'use strict'; + + /** + * @todo Complete the test + * This example is not perfect. + * Test should check if MomentJS have been called + */ + describe('directive navbar', function() { + // var $window; + var vm; + var el; + var timeInMs; + + beforeEach(module('angular')); + beforeEach(inject(function($compile, $rootScope) { + // spyOn(_$window_, 'moment').and.callThrough(); + // $window = _$window_; + + timeInMs = new Date(); + timeInMs = timeInMs.setHours(timeInMs.getHours() - 24); + + el = angular.element(''); + + $compile(el)($rootScope.$new()); + $rootScope.$digest(); + vm = el.isolateScope().vm; + // ctrl = el.controller('acmeNavbar'); + })); + + it('should be compiled', function() { + expect(el.html()).not.toEqual(null); + }); + + it('should have isolate scope object with instanciate members', function() { + expect(vm).toEqual(jasmine.any(Object)); + + expect(vm.creationDate).toEqual(jasmine.any(Number)); + expect(vm.creationDate).toEqual(timeInMs); + + expect(vm.relativeDate).toEqual(jasmine.any(String)); + expect(vm.relativeDate).toEqual('a day ago'); + }); + + // it('should call Moment', function() { + // console.log($window.moment) + // expect($window.moment).toHaveBeenCalled(); + // }); + }); +})(); diff --git a/src/app/components/navbar/navbar.html b/src/app/components/navbar/navbar.html new file mode 100644 index 0000000..058a17e --- /dev/null +++ b/src/app/components/navbar/navbar.html @@ -0,0 +1,21 @@ + diff --git a/src/app/components/navbar/navbar.scss b/src/app/components/navbar/navbar.scss new file mode 100644 index 0000000..98c1517 --- /dev/null +++ b/src/app/components/navbar/navbar.scss @@ -0,0 +1,3 @@ +.acme-navbar-text{ + color: white; +} diff --git a/src/app/components/webDevTec/webDevTec.service.js b/src/app/components/webDevTec/webDevTec.service.js new file mode 100644 index 0000000..531cf9e --- /dev/null +++ b/src/app/components/webDevTec/webDevTec.service.js @@ -0,0 +1,74 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .service('webDevTec', webDevTec); + + /** @ngInject */ + function webDevTec() { + var data = [ + { + 'title': 'AngularJS', + 'url': 'https://angularjs.org/', + 'description': 'HTML enhanced for web apps!', + 'logo': 'angular.png' + }, + { + 'title': 'BrowserSync', + 'url': 'http://browsersync.io/', + 'description': 'Time-saving synchronised browser testing.', + 'logo': 'browsersync.png' + }, + { + 'title': 'GulpJS', + 'url': 'http://gulpjs.com/', + 'description': 'The streaming build system.', + 'logo': 'gulp.png' + }, + { + 'title': 'Jasmine', + 'url': 'http://jasmine.github.io/', + 'description': 'Behavior-Driven JavaScript.', + 'logo': 'jasmine.png' + }, + { + 'title': 'Karma', + 'url': 'http://karma-runner.github.io/', + 'description': 'Spectacular Test Runner for JavaScript.', + 'logo': 'karma.png' + }, + { + 'title': 'Protractor', + 'url': 'https://github.com/angular/protractor', + 'description': 'End to end test framework for AngularJS applications built on top of WebDriverJS.', + 'logo': 'protractor.png' + }, + { + 'title': 'Bootstrap', + 'url': 'http://getbootstrap.com/', + 'description': 'Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web.', + 'logo': 'bootstrap.png' + }, + { + 'title': 'Angular UI Bootstrap', + 'url': 'http://angular-ui.github.io/bootstrap/', + 'description': 'Bootstrap components written in pure AngularJS by the AngularUI Team.', + 'logo': 'ui-bootstrap.png' + }, + { + 'title': 'Sass (Node)', + 'url': 'https://github.com/sass/node-sass', + 'description': 'Node.js binding to libsass, the C version of the popular stylesheet preprocessor, Sass.', + 'logo': 'node-sass.png' + } + ]; + + this.getTec = getTec; + + function getTec() { + return data; + } + } + +})(); diff --git a/src/app/components/webDevTec/webDevTec.service.spec.js b/src/app/components/webDevTec/webDevTec.service.spec.js new file mode 100644 index 0000000..5401eef --- /dev/null +++ b/src/app/components/webDevTec/webDevTec.service.spec.js @@ -0,0 +1,29 @@ +(function() { + 'use strict'; + + describe('service webDevTec', function() { + var webDevTec; + + beforeEach(module('angular')); + beforeEach(inject(function(_webDevTec_) { + webDevTec = _webDevTec_; + })); + + it('should be registered', function() { + expect(webDevTec).not.toEqual(null); + }); + + describe('getTec function', function() { + it('should exist', function() { + expect(webDevTec.getTec).not.toEqual(null); + }); + + it('should return array of object', function() { + var data = webDevTec.getTec(); + expect(data).toEqual(jasmine.any(Array)); + expect(data[0]).toEqual(jasmine.any(Object)); + expect(data.length > 5).toBeTruthy(); + }); + }); + }); +})(); diff --git a/src/app/index.config.js b/src/app/index.config.js new file mode 100644 index 0000000..82d7cc3 --- /dev/null +++ b/src/app/index.config.js @@ -0,0 +1,21 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .config(config); + + /** @ngInject */ + function config($logProvider, toastrConfig) { + // Enable log + $logProvider.debugEnabled(true); + + // Set options third-party lib + toastrConfig.allowHtml = true; + toastrConfig.timeOut = 3000; + toastrConfig.positionClass = 'toast-top-right'; + toastrConfig.preventDuplicates = true; + toastrConfig.progressBar = true; + } + +})(); diff --git a/src/app/index.constants.js b/src/app/index.constants.js new file mode 100644 index 0000000..b299a68 --- /dev/null +++ b/src/app/index.constants.js @@ -0,0 +1,10 @@ +/* global malarkey:false, moment:false */ +(function() { + 'use strict'; + + angular + .module('angular') + .constant('malarkey', malarkey) + .constant('moment', moment); + +})(); diff --git a/src/app/index.module.js b/src/app/index.module.js new file mode 100644 index 0000000..39ed460 --- /dev/null +++ b/src/app/index.module.js @@ -0,0 +1,7 @@ +(function() { + 'use strict'; + + angular + .module('angular', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngMessages', 'ngAria', 'ngResource', 'ngRoute', 'ui.bootstrap', 'toastr']); + +})(); diff --git a/src/app/index.route.js b/src/app/index.route.js new file mode 100644 index 0000000..3b550cd --- /dev/null +++ b/src/app/index.route.js @@ -0,0 +1,20 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .config(routeConfig); + + function routeConfig($routeProvider) { + $routeProvider + .when('/', { + templateUrl: 'app/main/main.html', + controller: 'MainController', + controllerAs: 'main' + }) + .otherwise({ + redirectTo: '/' + }); + } + +})(); diff --git a/src/app/index.run.js b/src/app/index.run.js new file mode 100644 index 0000000..69bfc48 --- /dev/null +++ b/src/app/index.run.js @@ -0,0 +1,14 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .run(runBlock); + + /** @ngInject */ + function runBlock($log) { + + $log.debug('runBlock end'); + } + +})(); diff --git a/src/app/index.scss b/src/app/index.scss new file mode 100644 index 0000000..e7f0dc4 --- /dev/null +++ b/src/app/index.scss @@ -0,0 +1,35 @@ +/** + * If you want to override some bootstrap variables, you have to change values here. + * The list of variables are listed here bower_components/bootstrap-sass/assets/stylesheets/bootstrap/_variables.scss + */ +$navbar-inverse-link-color: #5AADBB; +$icon-font-path: "../../bower_components/bootstrap-sass/assets/fonts/bootstrap/"; + +/** + * Do not remove the comments below. It's the markers used by wiredep to inject + * sass dependencies when defined in the bower.json of your dependencies + */ +// bower:scss +// endbower + +.browsehappy { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +.thumbnail { + height: 200px; + + img.pull-right { + width: 50px; + } +} + +/** + * Do not remove the comments below. It's the markers used by gulp-inject to inject + * all your sass files automatically + */ +// injector +// endinjector diff --git a/src/app/main/main.controller.js b/src/app/main/main.controller.js new file mode 100644 index 0000000..b73da83 --- /dev/null +++ b/src/app/main/main.controller.js @@ -0,0 +1,39 @@ +(function() { + 'use strict'; + + angular + .module('angular') + .controller('MainController', MainController); + + /** @ngInject */ + function MainController($timeout, webDevTec, toastr) { + var vm = this; + + vm.awesomeThings = []; + vm.classAnimation = ''; + vm.creationDate = 1452020281605; + vm.showToastr = showToastr; + + activate(); + + function activate() { + getWebDevTec(); + $timeout(function() { + vm.classAnimation = 'rubberBand'; + }, 4000); + } + + function showToastr() { + toastr.info('Fork generator-gulp-angular'); + vm.classAnimation = ''; + } + + function getWebDevTec() { + vm.awesomeThings = webDevTec.getTec(); + + angular.forEach(vm.awesomeThings, function(awesomeThing) { + awesomeThing.rank = Math.random(); + }); + } + } +})(); diff --git a/src/app/main/main.controller.spec.js b/src/app/main/main.controller.spec.js new file mode 100644 index 0000000..4931234 --- /dev/null +++ b/src/app/main/main.controller.spec.js @@ -0,0 +1,39 @@ +(function() { + 'use strict'; + + describe('controllers', function(){ + var vm; + var $timeout; + var toastr; + + beforeEach(module('angular')); + beforeEach(inject(function(_$controller_, _$timeout_, _webDevTec_, _toastr_) { + spyOn(_webDevTec_, 'getTec').and.returnValue([{}, {}, {}, {}, {}]); + spyOn(_toastr_, 'info').and.callThrough(); + + vm = _$controller_('MainController'); + $timeout = _$timeout_; + toastr = _toastr_; + })); + + it('should have a timestamp creation date', function() { + expect(vm.creationDate).toEqual(jasmine.any(Number)); + }); + + it('should define animate class after delaying timeout ', function() { + $timeout.flush(); + expect(vm.classAnimation).toEqual('rubberBand'); + }); + + it('should show a Toastr info and stop animation when invoke showToastr()', function() { + vm.showToastr(); + expect(toastr.info).toHaveBeenCalled(); + expect(vm.classAnimation).toEqual(''); + }); + + it('should define more than 5 awesome things', function() { + expect(angular.isArray(vm.awesomeThings)).toBeTruthy(); + expect(vm.awesomeThings.length === 5).toBeTruthy(); + }); + }); +})(); diff --git a/src/app/main/main.html b/src/app/main/main.html new file mode 100644 index 0000000..b586245 --- /dev/null +++ b/src/app/main/main.html @@ -0,0 +1,34 @@ +
+ +
+ +
+ +
+

'Allo, 'Allo!

+

+ I'm Yeoman
+ Always a pleasure scaffolding your apps. +

+

+ +

+

+ With ♥ thanks to the contributions of +

+
+ +
+
+
+ {{ awesomeThing.title }} +
+

{{ awesomeThing.title }}

+

{{ awesomeThing.description }}

+

{{ awesomeThing.url }}

+
+
+
+
+ +
diff --git a/src/assets/images/angular.png b/src/assets/images/angular.png new file mode 100644 index 0000000..59f36fa Binary files /dev/null and b/src/assets/images/angular.png differ diff --git a/src/assets/images/bootstrap.png b/src/assets/images/bootstrap.png new file mode 100644 index 0000000..5004b1e Binary files /dev/null and b/src/assets/images/bootstrap.png differ diff --git a/src/assets/images/browsersync.png b/src/assets/images/browsersync.png new file mode 100644 index 0000000..201c641 Binary files /dev/null and b/src/assets/images/browsersync.png differ diff --git a/src/assets/images/gulp.png b/src/assets/images/gulp.png new file mode 100644 index 0000000..5da1891 Binary files /dev/null and b/src/assets/images/gulp.png differ diff --git a/src/assets/images/jasmine.png b/src/assets/images/jasmine.png new file mode 100644 index 0000000..5be8ec8 Binary files /dev/null and b/src/assets/images/jasmine.png differ diff --git a/src/assets/images/karma.png b/src/assets/images/karma.png new file mode 100644 index 0000000..48b9601 Binary files /dev/null and b/src/assets/images/karma.png differ diff --git a/src/assets/images/node-sass.png b/src/assets/images/node-sass.png new file mode 100644 index 0000000..11d69d8 Binary files /dev/null and b/src/assets/images/node-sass.png differ diff --git a/src/assets/images/protractor.png b/src/assets/images/protractor.png new file mode 100644 index 0000000..98e0162 Binary files /dev/null and b/src/assets/images/protractor.png differ diff --git a/src/assets/images/ui-bootstrap.png b/src/assets/images/ui-bootstrap.png new file mode 100644 index 0000000..046a9e5 Binary files /dev/null and b/src/assets/images/ui-bootstrap.png differ diff --git a/src/assets/images/yeoman.png b/src/assets/images/yeoman.png new file mode 100644 index 0000000..92497ad Binary files /dev/null and b/src/assets/images/yeoman.png differ diff --git a/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..6527905 Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..75d28ec --- /dev/null +++ b/src/index.html @@ -0,0 +1,46 @@ + + + + + angular + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + diff --git a/theme.yml b/theme.yml new file mode 100644 index 0000000..da4710f --- /dev/null +++ b/theme.yml @@ -0,0 +1,3 @@ +name: "Angular theme" +layout: "angular-layout" +icon_theme: [default, pidgin] -- libgit2 0.21.2