├── app ├── styles │ ├── menu │ │ └── menu.scss │ ├── _variables.scss │ ├── views │ │ └── home.scss │ ├── layout │ │ └── layout.scss │ ├── main.scss │ └── _fonts.scss ├── fonts │ ├── Roboto-Bold.ttf │ ├── Roboto-Light.ttf │ └── Roboto-Thin.ttf ├── scripts │ ├── controllers │ │ ├── settingsController.js │ │ ├── mainController.js │ │ └── homeController.js │ ├── utils │ │ └── lodash.js │ ├── config │ │ └── apiEndpoint.js │ ├── services │ │ ├── ApiService.js │ │ └── ExampleService.js │ └── app.js ├── templates │ ├── views │ │ ├── home.html │ │ └── settings.html │ └── main.html └── index.html ├── .bowerrc ├── resources ├── icon.png └── splash.png ├── ionic.project ├── test ├── e2e │ └── homepage.spec.js └── unit │ └── controllers │ └── homeController.test.js ├── .gitignore ├── protractor.conf.js ├── vendor.json ├── bower.json ├── .jshintrc ├── hooks ├── before_platform_add │ └── init_directories.js ├── after_plugin_rm │ └── 010_deregister_plugin.js ├── after_platform_add │ └── 010_install_plugins.js ├── after_prepare │ ├── 020_remove_sass_from_platforms.js │ └── 010_add_platform_class.js ├── after_plugin_add │ └── 010_register_plugin.js └── README.md ├── emulateios ├── package.json ├── karma.conf.js ├── config.xml ├── README.md └── gulpfile.js /app/styles/menu/menu.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /app/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Define variables here 3 | */ 4 | 5 | $textColor: #ccc; -------------------------------------------------------------------------------- /app/styles/views/home.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | 3 | } 4 | 5 | .content { 6 | padding: 1rem; 7 | } -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmaximini/ionic-gulp-seed/HEAD/resources/icon.png -------------------------------------------------------------------------------- /ionic.project: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IonicGulpSeed", 3 | "app_id": "de.thomasmaximini.ionicgulpseed" 4 | } -------------------------------------------------------------------------------- /resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmaximini/ionic-gulp-seed/HEAD/resources/splash.png -------------------------------------------------------------------------------- /app/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmaximini/ionic-gulp-seed/HEAD/app/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /app/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmaximini/ionic-gulp-seed/HEAD/app/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /app/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmaximini/ionic-gulp-seed/HEAD/app/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /app/styles/layout/layout.scss: -------------------------------------------------------------------------------- 1 | .loader { 2 | text-align: center; 3 | width: 100%; 4 | padding: 20px 0; 5 | } -------------------------------------------------------------------------------- /test/e2e/homepage.spec.js: -------------------------------------------------------------------------------- 1 | describe('HomePage', function(){ 2 | it('can render the homepage', function(){ 3 | browser.get('/'); 4 | var headerElem = element(by.css('h1.title')); 5 | 6 | expect(headerElem).not.to.be.null; 7 | 8 | }); 9 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | node_modules/ 5 | platforms/ 6 | plugins/ 7 | bower_components/ 8 | 9 | .tmp/ 10 | www/ 11 | .sass-cache 12 | *.DS_Store 13 | *.log 14 | tmp.run.file 15 | 16 | coverage 17 | -------------------------------------------------------------------------------- /app/scripts/controllers/settingsController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name IonicGulpSeed.controller:SettingsController 6 | * @description 7 | * # SettingsController 8 | */ 9 | angular.module('IonicGulpSeed') 10 | .controller('SettingsController', function($scope) { 11 | 12 | // do something with $scope 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /app/scripts/controllers/mainController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name IonicGulpSeed.controller:MainController 6 | * @description 7 | * # MainController 8 | * This controller handles the side menu 9 | */ 10 | angular.module('IonicGulpSeed') 11 | .controller('MainController', function($scope) { 12 | 13 | // do something with $scope 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /app/scripts/utils/lodash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name IonicGulpSeed.util:lodash 6 | * @description 7 | * # Lo-Dash 8 | * Expose Lo-Dash through injectable factory, so we don't pollute / rely on global namespace 9 | * just inject lodash as _ 10 | */ 11 | 12 | angular.module('IonicGulpSeed') 13 | .factory('_', function($window) { 14 | return $window._; 15 | }); -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | framework: 'mocha', 3 | 4 | seleniumAddress: 'http://localhost:4444/wd/hub', 5 | baseUrl: 'http://localhost:9000', 6 | 7 | multiCapabilities: [{ 8 | browserName: 'chrome' 9 | }], 10 | 11 | mochaOpts: { 12 | timeout: 4000 13 | }, 14 | 15 | onPrepare: function(){ 16 | global.expect = require('chai').expect; 17 | } 18 | } -------------------------------------------------------------------------------- /app/styles/main.scss: -------------------------------------------------------------------------------- 1 | /************************************ 2 | * 3 | * Main Styles to compile. Here only @imports! 4 | * 5 | ************************************/ 6 | 7 | @import "_variables"; 8 | @import "_fonts"; 9 | 10 | /** 11 | * LAYOUT 12 | */ 13 | 14 | @import "layout/layout"; 15 | 16 | 17 | /** 18 | * MENU 19 | */ 20 | 21 | @import "menu/menu"; 22 | 23 | /** 24 | * VIEWS 25 | */ 26 | 27 | @import "views/home"; -------------------------------------------------------------------------------- /vendor.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bower_components/angular/angular.js", 3 | "bower_components/angular-resource/angular-resource.js", 4 | "bower_components/angular-animate/angular-animate.js", 5 | "bower_components/angular-sanitize/angular-sanitize.js", 6 | "bower_components/angular-ui-router/release/angular-ui-router.js", 7 | "bower_components/ionic/js/ionic.js", 8 | "bower_components/ionic/js/ionic-angular.js", 9 | "bower_components/lodash/lodash.js", 10 | "bower_components/ngCordova/dist/ng-cordova.js" 11 | ] -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IonicGulpSeed", 3 | "private": "true", 4 | "devDependencies": { 5 | "ionic": "driftyco/ionic-bower#1.0.0-rc.1", 6 | "angular": "~1.3.11", 7 | "angular-mocks": "~1.4.7" 8 | }, 9 | "dependencies": { 10 | "ngCordova": "~0.1.12-alpha", 11 | "angular-resource": "1.3.13", 12 | "lodash": "~3.0.1", 13 | "angular-animate": "1.3.13" 14 | }, 15 | "resolutions": { 16 | "angular-animate": "1.3.13", 17 | "angular-sanitize": "1.3.13", 18 | "angular": "1.3.13" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": false, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "angular": false, 23 | "cordova": false, 24 | "StatusBar": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/templates/views/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Gulp Ionic Seed

5 |
6 | 7 | 8 | 11 | 12 |
13 | 14 |
15 |
16 |
17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /hooks/before_platform_add/init_directories.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * On a fresh clone, the local platforms/ and plugins/ directories will be 5 | * missing, so ensure they get created before the first platform is added. 6 | */ 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | 10 | var platformsDir = path.resolve(__dirname, '../../platforms'); 11 | var pluginsDir = path.resolve(__dirname, '../../plugins'); 12 | 13 | try { 14 | fs.mkdirSync(platformsDir, function (err) { 15 | if (err) { console.error(err); } 16 | }); 17 | } catch(ex) {} 18 | 19 | try { 20 | fs.mkdirSync(pluginsDir, function (err) { 21 | if (err) { console.error(err); } 22 | }); 23 | } catch(ex) {} 24 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | IonicGulpSeed 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /emulateios: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export DEVICES=`ios-sim showdevicetypes 2>&1` 3 | export DEVICES=\"`echo $DEVICES | sed -e 's/ com./" "com./g' | sed -e 's/, /,~/g'`\" 4 | PS3='Please enter your choice: ' 5 | options=($DEVICES) 6 | curpath="$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" 7 | app="`find $curpath/platforms/ios/build/emulator -name *.app -print`" 8 | escaped_app="\"$app\"" 9 | 10 | echo $app 11 | select opt in "${options[@]}" 12 | do 13 | case $opt in 14 | *) echo ios-sim launch "$escaped_app" --devicetypeid "`echo $opt | sed -e "s/~/ /g"`" --stderr ./platforms/ios/cordova/console.log --stdout ./platforms/ios/cordova/console.log > tmp.run.file;chmod +x tmp.run.file;./tmp.run.file;rm tmp.run.file;exit; 15 | esac 16 | done 17 | -------------------------------------------------------------------------------- /test/unit/controllers/homeController.test.js: -------------------------------------------------------------------------------- 1 | describe('HomeController', function(){ 2 | var controller, scope, ExampleService; 3 | 4 | beforeEach(module('IonicGulpSeed')); 5 | beforeEach(module('AppTemplate')); 6 | 7 | beforeEach(inject(function($rootScope, _$controller_, _ExampleService_){ 8 | scope = $rootScope.$new(); 9 | controller = _$controller_('HomeController', { 10 | $scope: scope, 11 | ExampleService: _ExampleService_ 12 | }); 13 | ExampleService = _ExampleService_; 14 | 15 | sinon.stub(ExampleService, 'doSomethingAsync', ExampleService.doSomethingAsync); 16 | })); 17 | 18 | it('calls the method doSomethingAsync()', function(){ 19 | scope.fetchRandomText(); 20 | expect(ExampleService.doSomethingAsync.called).to.eq(true); 21 | 22 | }); 23 | 24 | }); -------------------------------------------------------------------------------- /app/styles/_fonts.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * Import fonts either via @import url (e.g. from Googlee Fonts) 3 | * or via @font-face when font files are stored locally in /app/fonts 4 | */ 5 | 6 | 7 | // @import url(http://fonts.googleapis.com/css?family=Roboto:700,400,300); 8 | 9 | @font-face { 10 | font-family: 'Roboto'; 11 | src: url(../fonts/Roboto-Light.ttf) format("truetype"); 12 | font-weight: 300; 13 | font-style: normal; 14 | } 15 | 16 | @font-face { 17 | font-family: 'Roboto'; 18 | src: url(../fonts/Roboto-Regular.ttf) format("truetype"); 19 | font-weight: 400; 20 | font-style: normal; 21 | } 22 | 23 | @font-face { 24 | font-family: 'Roboto'; 25 | src: url(../fonts/Roboto-Bold.ttf) format("truetype"); 26 | font-weight: 700; 27 | font-style: normal; 28 | } -------------------------------------------------------------------------------- /app/scripts/controllers/homeController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name IonicGulpSeed.controller:HomeController 6 | * @description 7 | * # HomeController 8 | */ 9 | angular.module('IonicGulpSeed') 10 | .controller('HomeController', function($scope, ExampleService) { 11 | 12 | $scope.myHTML = null; 13 | 14 | $scope.fetchRandomText = function() { 15 | ExampleService.doSomethingAsync() 16 | .then(ExampleService.fetchSomethingFromServer) 17 | .then(function(response) { 18 | $scope.myHTML = response.data.text; 19 | // close pull to refresh loader 20 | $scope.$broadcast('scroll.refreshComplete'); 21 | }); 22 | }; 23 | 24 | $scope.fetchRandomText(); 25 | }); 26 | -------------------------------------------------------------------------------- /app/templates/views/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Settings

5 |
6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | Allow Push Notifications 15 | 16 | 17 | 18 | 19 | 20 | Allow cookies 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /app/scripts/config/apiEndpoint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc constant 5 | * @name IonicGulpSeed.API_ENDPOINT 6 | * @description 7 | * # API_ENDPOINT 8 | * Defines the API endpoint where our resources will make requests against. 9 | * Is used inside /services/ApiService.js to generate correct endpoint dynamically 10 | */ 11 | 12 | 13 | angular.module('IonicGulpSeed') 14 | 15 | // development 16 | .constant('API_ENDPOINT', { 17 | host: 'http://localhost', 18 | port: 3000, 19 | path: '', 20 | needsAuth: false 21 | }); 22 | 23 | 24 | // live example with HTTP Basic Auth 25 | /* 26 | .constant('API_ENDPOINT', { 27 | host: 'http://myserver.com', 28 | path: '/api/v2', 29 | needsAuth: true, 30 | username: 'whatever', 31 | password: 'foobar' 32 | }); 33 | */ 34 | 35 | -------------------------------------------------------------------------------- /hooks/after_plugin_rm/010_deregister_plugin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Remove plugins from cordovaPlugins array after_plugin_rm 5 | */ 6 | var fs = require('fs'); 7 | var packageJSON = require('../../package.json'); 8 | 9 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; 10 | 11 | process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) { 12 | var index = packageJSON.cordovaPlugins.indexOf(plugin); 13 | if (index > -1) { 14 | packageJSON.cordovaPlugins.splice(index, 1); 15 | } else { 16 | //If it didnt find a match, it may be listed as {id,locator} 17 | for(var i = 0, j = packageJSON.cordovaPlugins.length; i < j; i++) { 18 | var packagePlugin = packageJSON.cordovaPlugins[i]; 19 | if(typeof packagePlugin == 'object' && packagePlugin.id == plugin) { 20 | packageJSON.cordovaPlugins.splice(index, 1); 21 | break; 22 | } 23 | } 24 | } 25 | }); 26 | 27 | fs.writeFile('package.json', JSON.stringify(packageJSON, null, 2)); 28 | -------------------------------------------------------------------------------- /app/templates/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Side Menu

15 |
16 | 17 | 18 | 28 | 29 |
30 |
31 | -------------------------------------------------------------------------------- /hooks/after_platform_add/010_install_plugins.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Install all plugins listed in package.json 5 | * https://raw.githubusercontent.com/diegonetto/generator-ionic/master/templates/hooks/after_platform_add/install_plugins.js 6 | */ 7 | var exec = require('child_process').exec; 8 | var path = require('path'); 9 | var sys = require('sys'); 10 | 11 | var packageJSON = null; 12 | 13 | try { 14 | packageJSON = require('../../package.json'); 15 | } catch(ex) { 16 | console.log('\nThere was an error fetching your package.json file.') 17 | console.log('\nPlease ensure a valid package.json is in the root of this project\n') 18 | return; 19 | } 20 | 21 | var cmd = process.platform === 'win32' ? 'cordova.cmd' : 'cordova'; 22 | // var script = path.resolve(__dirname, '../../node_modules/cordova/bin', cmd); 23 | 24 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; 25 | packageJSON.cordovaPlugins.forEach(function (plugin) { 26 | exec('cordova plugin add ' + plugin, function (error, stdout, stderr) { 27 | sys.puts(stdout); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /hooks/after_prepare/020_remove_sass_from_platforms.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * After prepare, files are copied to the platforms/ios and platforms/android folders. 5 | * Lets clean up some of those files that arent needed with this hook. 6 | */ 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | 10 | var deleteFolderRecursive = function(removePath) { 11 | if( fs.existsSync(removePath) ) { 12 | fs.readdirSync(removePath).forEach(function(file,index){ 13 | var curPath = path.join(removePath, file); 14 | if(fs.lstatSync(curPath).isDirectory()) { // recurse 15 | deleteFolderRecursive(curPath); 16 | } else { // delete file 17 | fs.unlinkSync(curPath); 18 | } 19 | }); 20 | fs.rmdirSync(removePath); 21 | } 22 | }; 23 | 24 | var iosPlatformsDir = path.resolve(__dirname, '../../platforms/ios/www/lib/ionic/scss'); 25 | var androidPlatformsDir = path.resolve(__dirname, '../../platforms/android/assets/www/lib/ionic/scss'); 26 | 27 | deleteFolderRecursive(iosPlatformsDir); 28 | deleteFolderRecursive(androidPlatformsDir); 29 | -------------------------------------------------------------------------------- /app/scripts/services/ApiService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name IonicGulpSeed.ApiService 6 | * @description 7 | * # ApiService 8 | * Retrieves correct api to make requests against. 9 | * Uses settings from API_ENDPOINT defined in /config/apiEndpoint.js 10 | * 11 | * Usage example: $http({ 12 | * url: ApiService.getEndPoint() + '/things', 13 | * method: 'GET' 14 | * }) 15 | * 16 | */ 17 | angular.module('IonicGulpSeed') 18 | .factory('ApiService', function($window, $http, API_ENDPOINT) { 19 | 20 | var _api = API_ENDPOINT; 21 | var endpoint = _api.port ? (_api.host + ':' + _api.port + _api.path) : (_api.host + _api.path); 22 | 23 | // activate for basic auth 24 | if (_api.needsAuth) { 25 | $http.defaults.headers.common.Authorization = 'Basic ' + $window.btoa(_api.username + ':' + _api.password); 26 | } 27 | 28 | // public api 29 | return { 30 | getEndpoint: function() { return endpoint; } 31 | }; 32 | 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /app/scripts/services/ExampleService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name IonicGulpSeed.serive:ExampleService 6 | * @description 7 | * # ExampleService 8 | */ 9 | angular.module('IonicGulpSeed') 10 | // use factory for services 11 | .factory('ExampleService', function($http, $timeout, $q) { 12 | 13 | var kindOfPrivateVariable = 42; 14 | 15 | var doSomethingAsync = function() { 16 | var deferred = $q.defer(); 17 | $timeout(deferred.resolve.bind(null, kindOfPrivateVariable), 1000); 18 | return deferred.promise; 19 | }; 20 | 21 | var fetchSomethingFromServer = function() { 22 | return $http({ 23 | url: 'http://hipsterjesus.com/api', 24 | params: { 25 | paras: 2 26 | }, 27 | method: 'GET' 28 | }) 29 | .success(function(data) { 30 | console.log('fetched this stuff from server:', data); 31 | }) 32 | .error(function(error) { 33 | console.log('an error occured', error); 34 | }); 35 | }; 36 | 37 | // public api 38 | return { 39 | doSomethingAsync: doSomethingAsync, 40 | fetchSomethingFromServer: fetchSomethingFromServer 41 | }; 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc overview 5 | * @name IonicGulpSeed 6 | * @description 7 | * # Initializes main application and routing 8 | * 9 | * Main module of the application. 10 | */ 11 | 12 | 13 | angular.module('IonicGulpSeed', ['ionic', 'ngCordova', 'ngResource', 'ngSanitize']) 14 | 15 | .run(function($ionicPlatform) { 16 | 17 | $ionicPlatform.ready(function() { 18 | // save to use plugins here 19 | }); 20 | 21 | // add possible global event handlers here 22 | 23 | }) 24 | 25 | .config(function($httpProvider, $stateProvider, $urlRouterProvider) { 26 | // register $http interceptors, if any. e.g. 27 | // $httpProvider.interceptors.push('interceptor-name'); 28 | 29 | // Application routing 30 | $stateProvider 31 | .state('app', { 32 | url: '/app', 33 | abstract: true, 34 | templateUrl: 'templates/main.html', 35 | controller: 'MainController' 36 | }) 37 | .state('app.home', { 38 | url: '/home', 39 | cache: true, 40 | views: { 41 | 'viewContent': { 42 | templateUrl: 'templates/views/home.html', 43 | controller: 'HomeController' 44 | } 45 | } 46 | }) 47 | .state('app.settings', { 48 | url: '/settings', 49 | cache: true, 50 | views: { 51 | 'viewContent': { 52 | templateUrl: 'templates/views/settings.html', 53 | controller: 'SettingsController' 54 | } 55 | } 56 | }); 57 | 58 | 59 | // redirects to default route for undefined routes 60 | $urlRouterProvider.otherwise('/app/home'); 61 | }); 62 | 63 | 64 | -------------------------------------------------------------------------------- /hooks/after_plugin_add/010_register_plugin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Push plugins to cordovaPlugins array after_plugin_add 5 | */ 6 | var fs = require('fs'), 7 | packageJSON = require('../../package.json'), 8 | path = require('path'); 9 | 10 | packageJSON.cordovaPlugins = packageJSON.cordovaPlugins || []; 11 | process.env.CORDOVA_PLUGINS.split(',').forEach(function (plugin) { 12 | var configString, 13 | idRegEx, 14 | id, 15 | pluginXmlPath, 16 | pluginToAdd; 17 | 18 | if(plugin.indexOf('https') != -1 || plugin.indexOf('git') != -1) { 19 | console.log('Installing plugin from url'); 20 | } 21 | 22 | if(plugin.indexOf('/') != -1) { 23 | try { 24 | pluginXmlPath = path.resolve(plugin, 'plugin.xml'); 25 | console.log('got pluginXmlPath:', pluginXmlPath); 26 | if (!fs.existsSync(pluginXmlPath)) { 27 | var errorMessage = ['There was no plugin.xml file found for path: ', pluginXmlPath].join(''); 28 | return; 29 | } 30 | 31 | configString = fs.readFileSync(pluginXmlPath,{encoding: 'utf8'}); 32 | idRegEx = new RegExp(']*id="(.*)"', 'i'); 33 | id = idRegEx.exec(configString)[1] 34 | pluginToAdd = {id: id, locator: plugin}; 35 | } catch(ex) { 36 | console.log('There was an error retrieving the plugin.xml filr from the 010_register_plugin.js hook', ex); 37 | } 38 | } else { 39 | pluginToAdd = plugin; 40 | } 41 | 42 | if(typeof pluginToAdd == 'string' && packageJSON.cordovaPlugins.indexOf(pluginToAdd) == -1) { 43 | packageJSON.cordovaPlugins.push(pluginToAdd); 44 | } else if (typeof pluginToAdd == 'object') { 45 | var pluginExists = false; 46 | packageJSON.cordovaPlugins.forEach(function(checkPlugin) { 47 | if(typeof checkPlugin == 'object' && checkPlugin.id == pluginToAdd.id) { 48 | pluginExists = true; 49 | } 50 | }) 51 | if(!pluginExists) { 52 | packageJSON.cordovaPlugins.push(pluginToAdd); 53 | } 54 | } 55 | }); 56 | 57 | fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2)); 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ionicgulpseed", 3 | "version": "1.2.1", 4 | "description": "A blank seed project for rapid development of Ionic projects through gulp workflows", 5 | "author": "Thomas Maximini ", 6 | "dependencies": { 7 | "bluebird": "^3.0.5" 8 | }, 9 | "devDependencies": { 10 | "beepbeep": "^1.2.0", 11 | "bower": "^1.3.3", 12 | "chai": "^3.4.1", 13 | "connect-livereload": "^0.5.2", 14 | "del": "^1.1.1", 15 | "express": "^4.11.1", 16 | "glob": "^4.3.5", 17 | "gulp": "^3.8.10", 18 | "gulp-angular-templatecache": "^1.5.0", 19 | "gulp-autoprefixer": "^2.1.0", 20 | "gulp-cached": "^1.1.0", 21 | "gulp-changed": "^1.2.1", 22 | "gulp-concat": "^2.4.3", 23 | "gulp-iconfont": "^1.0.0", 24 | "gulp-iconfont-css": "0.0.9", 25 | "gulp-if": "^1.2.5", 26 | "gulp-inject": "^1.1.1", 27 | "gulp-jshint": "^1.9.0", 28 | "gulp-livereload": "^3.7.0", 29 | "gulp-load-plugins": "^0.8.0", 30 | "gulp-ng-annotate": "^0.5.0", 31 | "gulp-protractor": "^1.0.0", 32 | "gulp-remember": "^0.3.0", 33 | "gulp-rev": "^3.0.0", 34 | "gulp-sass": "^2.0.1", 35 | "gulp-shell": "^0.2.11", 36 | "gulp-strip-css-comments": "^1.1.0", 37 | "gulp-strip-debug": "^1.0.2", 38 | "gulp-uglify": "^1.1.0", 39 | "gulp-util": "^2.2.14", 40 | "jshint-stylish": "^1.0.0", 41 | "karma": "^0.13.15", 42 | "karma-chai": "^0.1.0", 43 | "karma-chrome-launcher": "^0.2.1", 44 | "karma-coverage": "^0.5.3", 45 | "karma-firefox-launcher": "^0.1.7", 46 | "karma-mocha": "^0.2.1", 47 | "karma-ng-html2js-preprocessor": "^0.2.0", 48 | "karma-sinon": "^1.0.4", 49 | "lodash": "^3.0.0", 50 | "merge-stream": "^0.1.7", 51 | "mocha": "^2.3.3", 52 | "open": "0.0.5", 53 | "protractor": "^2.5.1", 54 | "ripple-emulator": "^0.9.28", 55 | "run-sequence": "^1.0.2", 56 | "shelljs": "^0.3.0", 57 | "sinon": "^1.17.2", 58 | "streamqueue": "^0.1.1", 59 | "yargs": "^1.3.3" 60 | }, 61 | "scripts": { 62 | "gulp": "./node_modules/.bin/gulp" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sat Nov 14 2015 22:37:58 GMT+1100 (AEDT) 3 | 4 | 5 | var _ = require('lodash'); 6 | 7 | 8 | /** 9 | * This function returns a list of js/html files 10 | * to be loaded to karma runner 11 | */ 12 | function filesArray(){ 13 | 14 | var vendorJsFiles = require('./vendor.json'); 15 | 16 | var mockJsFiles = [ 17 | 'bower_components/angular-mocks/angular-mocks.js' 18 | ]; 19 | 20 | var appJsFiles = [ 21 | 'app/scripts/**/*.js', 22 | 'test/unit/**/*.js' 23 | ]; 24 | 25 | var htmlFiles = ['app/templates/**/*.html']; 26 | 27 | return _.union(vendorJsFiles, mockJsFiles, appJsFiles, htmlFiles); 28 | } 29 | 30 | module.exports = function(config) { 31 | config.set({ 32 | 33 | // base path that will be used to resolve all patterns (eg. files, exclude) 34 | basePath: '', 35 | 36 | // frameworks to use 37 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 38 | frameworks: ['mocha', 'chai', 'sinon'], 39 | 40 | 41 | // list of files / patterns to load in the browser 42 | files: filesArray(), 43 | 44 | 45 | // list of files to exclude 46 | exclude: [ 47 | ], 48 | 49 | // preprocess matching files before serving them to the browser 50 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 51 | preprocessors: { 52 | 'app/scripts/**/*.js': ['coverage'], 53 | 'app/templates/**/*.html': ['ng-html2js'] 54 | }, 55 | 56 | // settings for ng-html2js preprocessor 57 | ngHtml2JsPreprocessor: { 58 | stripPrefix: 'app/', 59 | moduleName: 'AppTemplate' 60 | }, 61 | 62 | // settings for coverage plugin 63 | coverageReporter: { 64 | type: 'html', 65 | dir: 'coverage/' 66 | }, 67 | 68 | // test results reporter to use 69 | // possible values: 'dots', 'progress' 70 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 71 | reporters: ['progress', 'coverage'], 72 | 73 | 74 | // web server port 75 | port: 9876, 76 | 77 | 78 | // enable / disable colors in the output (reporters and logs) 79 | colors: true, 80 | 81 | 82 | // level of logging 83 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 84 | logLevel: config.LOG_INFO, 85 | 86 | 87 | // enable / disable watching file and executing tests whenever any file changes 88 | autoWatch: true, 89 | 90 | 91 | // start these browsers 92 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 93 | browsers: ['Chrome', 'Firefox'], 94 | 95 | // Continuous Integration mode 96 | // if true, Karma captures browsers, runs the tests and exits 97 | singleRun: false, 98 | 99 | // Concurrency level 100 | // how many browser should be started simultanous 101 | concurrency: Infinity 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | IonicGulpSeed 4 | 5 | A blank seed project for rapid development of Ionic projects through gulp workflows 6 | 7 | 8 | Thomas Maximini 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /hooks/after_prepare/010_add_platform_class.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Add Platform Class 4 | // v1.0 5 | // Automatically adds the platform class to the body tag 6 | // after the `prepare` command. By placing the platform CSS classes 7 | // directly in the HTML built for the platform, it speeds up 8 | // rendering the correct layout/style for the specific platform 9 | // instead of waiting for the JS to figure out the correct classes. 10 | 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | 14 | var rootdir = process.argv[2]; 15 | 16 | function addPlatformBodyTag(indexPath, platform) { 17 | // add the platform class to the body tag 18 | try { 19 | var platformClass = 'platform-' + platform; 20 | var cordovaClass = 'platform-cordova platform-webview'; 21 | 22 | var html = fs.readFileSync(indexPath, 'utf8'); 23 | 24 | var bodyTag = findBodyTag(html); 25 | if(!bodyTag) return; // no opening body tag, something's wrong 26 | 27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added 28 | 29 | var newBodyTag = bodyTag; 30 | 31 | var classAttr = findClassAttr(bodyTag); 32 | if(classAttr) { 33 | // body tag has existing class attribute, add the classname 34 | var endingQuote = classAttr.substring(classAttr.length-1); 35 | var newClassAttr = classAttr.substring(0, classAttr.length-1); 36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; 37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr); 38 | 39 | } else { 40 | // add class attribute to the body tag 41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); 42 | } 43 | 44 | html = html.replace(bodyTag, newBodyTag); 45 | 46 | fs.writeFileSync(indexPath, html, 'utf8'); 47 | 48 | process.stdout.write('add to body class: ' + platformClass + '\n'); 49 | } catch(e) { 50 | process.stdout.write(e); 51 | } 52 | } 53 | 54 | function findBodyTag(html) { 55 | // get the body tag 56 | try{ 57 | return html.match(/])(.*?)>/gi)[0]; 58 | }catch(e){} 59 | } 60 | 61 | function findClassAttr(bodyTag) { 62 | // get the body tag's class attribute 63 | try{ 64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; 65 | }catch(e){} 66 | } 67 | 68 | if (rootdir) { 69 | 70 | // go through each of the platform directories that have been prepared 71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); 72 | 73 | for(var x=0; x 21 | # Cordova Hooks 22 | 23 | This directory may contain scripts used to customize cordova commands. This 24 | directory used to exist at `.cordova/hooks`, but has now been moved to the 25 | project root. Any scripts you add to these directories will be executed before 26 | and after the commands corresponding to the directory name. Useful for 27 | integrating your own build systems or integrating with version control systems. 28 | 29 | __Remember__: Make your scripts executable. 30 | 31 | ## Hook Directories 32 | The following subdirectories will be used for hooks: 33 | 34 | after_build/ 35 | after_compile/ 36 | after_docs/ 37 | after_emulate/ 38 | after_platform_add/ 39 | after_platform_rm/ 40 | after_platform_ls/ 41 | after_plugin_add/ 42 | after_plugin_ls/ 43 | after_plugin_rm/ 44 | after_plugin_search/ 45 | after_prepare/ 46 | after_run/ 47 | after_serve/ 48 | before_build/ 49 | before_compile/ 50 | before_docs/ 51 | before_emulate/ 52 | before_platform_add/ 53 | before_platform_rm/ 54 | before_platform_ls/ 55 | before_plugin_add/ 56 | before_plugin_ls/ 57 | before_plugin_rm/ 58 | before_plugin_search/ 59 | before_prepare/ 60 | before_run/ 61 | before_serve/ 62 | pre_package/ <-- Windows 8 and Windows Phone only. 63 | 64 | ## Script Interface 65 | 66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: 67 | 68 | * CORDOVA_VERSION - The version of the Cordova-CLI. 69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). 70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) 71 | * CORDOVA_HOOK - Path to the hook that is being executed. 72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) 73 | 74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted. 75 | 76 | 77 | ## Writing hooks 78 | 79 | We highly recommend writting your hooks using Node.js so that they are 80 | cross-platform. Some good examples are shown here: 81 | 82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) 83 | 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ionic Gulp Seed 2 | ### An ionic starter project with a gulp toolchain 3 | 4 | Heads-up: There is now also a [Yeoman Generator](https://github.com/tmaximini/generator-ionic-gulp) available for this seed. 5 | 6 | ## Features 7 | 8 | * Gulp jobs for development, building, unit testing, emulating and running your app 9 | * Compiles and concatenates your Sass 10 | * Local development server with live reload, even inside ios emulator 11 | * Automatically inject all your JS sources into `index.html` 12 | * Auto min-safe all Angular DI through `ng-annotate`, no need to use weird bracket notation 13 | * Comes already with [ng-cordova](http://ngcordova.com/) and [lodash](https://lodash.com) 14 | * generate icon font from svg files 15 | * Blazing fast 16 | * E2E(End-to-End) testing with Protractor 17 | 18 | ## Commands 19 | 20 | | gulp command | shortcut | what it does | 21 | |-----------------------------|----------------------|------------------------------------------------------------------------------------------------| 22 | | `gulp` | — | run local development server, start watchers, auto reload browser on change, targetfolder /tmp | 23 | | `gulp --build` | `gulp -b` | create a build from current `/app` folder, minify assets, targetfolder `/www` | 24 | | `gulp --emulate ` | `gulp -e ` | run a build first, then ionic emulate . defaults to ios | 25 | | `gulp --run ` | `gulp -r ` | run a build first, then ionic run . defaults to ios | 26 | | `gulp test-unit` | none | run all the test cases under `test/unit` folder using Karma runner | 27 | | `gulp test-e2e` | none | run all the test cases under `test/e2e` folder using Protractor | 28 | 29 | ## Installation 30 | 31 | I recommend using the available [Yeoman Generator](https://github.com/tmaximini/generator-ionic-gulp). 32 | 33 | ```bash 34 | npm install -g yo generator-ionic-gulp 35 | yo ionic-gulp 36 | ``` 37 | 38 | OR you can clone the repo manually: 39 | 40 | 1. Clone this project `git clone https://github.com/tmaximini/ionic-gulp-seed.git ` 41 | 2. Change remote to your repo `git remote set-url origin https://github.com//.git` 42 | 3. run `npm install` and `bower install` to install dependencies 43 | 44 | 45 | ## Structure 46 | 47 | The source code lives inside the `app` folder. 48 | 49 | | Source Files | Location | 50 | | ------------- | ------------- | 51 | | Javascript | `app/scripts` | 52 | | Styles (scss) | `app/styles` | 53 | | Templates | `app/templates` | 54 | | Images | `app/images` | 55 | | Fonts | `app/fonts` | 56 | | Icons | `app/icons` | 57 | 58 | A lot of starter kits and tutorials encourage you to work directly inside the `www` folder, but I chose `app` instead, as it conforms better with most Angular.js projects. Note that `www` is gitignored and will be created dynamically during our build process. 59 | 60 | All 3rd party Javascript sources have to be manually added into `.vendor.json` and will be concatenated into a single `vendor.js` file. 61 | I know there is [wiredep](https://github.com/taptapship/wiredep) but I prefer to explicitly control which files get injected and also wiredep ends up adding lots of `